# Getting started

### Getting started

This is the landing page for the [Orbeon Forms](https://www.orbeon.com/) documentation.

<figure><img src="/files/TL1UzBjxv0cFapOvztL3" alt=""><figcaption><p>Orbeon Forms</p></figcaption></figure>

### Things you can do next

* [Visit the Orbeon website](https://www.orbeon.com/) and learn more about Orbeon Forms.
* [Visit the demo site](https://demo.orbeon.com/demo/) with Form Builder and Form Runner.
* Peruse the documentation
  * [Installation](/installation)
  * [Form Builder](/form-builder)
  * [Form Runner terminology](/form-runner/overview/terminology)
  * [Form Runner Landing page](/form-runner/form-runner-pages/landing-page)
  * And more!
* [Download](https://www.orbeon.com/download) Orbeon Forms.
* Review the [latest release notes for Orbeon Forms 2025.1.2](/release-notes/orbeon-forms-2025.1.2)!


# Installation

## Software requirements

### Basic requirements

Orbeon Forms runs on any platform that supports:

* A Java runtime environment (JRE)
* a Servlet 2.5 (or greater) container such as [Apache Tomcat](http://tomcat.apache.org/). For versions supported, see [Tomcat](/installation/tomcat).

### Java versions

| Orbeon Forms Version | Java Versions | Comment         |
| -------------------- | ------------- | --------------- |
| 2025.1               | 11, 17, 21    |                 |
| 2024.1               | 11, 17, 21    |                 |
| 2023.1               | 11, 17, 21    |                 |
| 2022.1               | 11, 17        |                 |
| 2021.1               | 11            |                 |
| 2020.1               | 8, 11         |                 |
| 2019.2               | 8, 10, 11     |                 |
| 2019.1               | 8, 10, 11     |                 |
| 2018.2               | 8, 10         | recommended     |
| 2018.2               | 7             | not recommended |
| Up to 2018.1         | 8             | recommended     |
| Up to 2018.1         | 7             | not recommended |

### Tomcat versions

See [Tomcat](/installation/tomcat).

### Java Servlet and Jakarta Servlet APIs

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

Orbeon Forms supports both the Java Servlet and Jakarta Servlet APIs, without any extra configuration, which means that the same `orbeon.war` file can be deployed in Tomcat 9, Tomcat 10, WildFly 26, or WildFly 27+, for example.

To support both of those APIs, the various Orbeon servlet, filters, and listeners are now dynamically instantiated instead of being referenced in `web.xml`. If you need to disable that dynamic instantiation mechanism, you can remove the `servlet-container-initializer.jar` file from the `WEB-INF/lib` directory of the `orbeon.war` file.

## Hardware requirements

We recommend you run Orbeon Forms on a dedicated server or instance that satisfies the following requirements:

* CPU: recent 4-core, or more, Intel Xeon or Core i7 or newer. We don't recommend AMD CPUs prior to the [Ryzen](https://www.amd.com/en/ryzen) line.
* RAM: 4 GB of RAM, or more, available to the Java Virtual Machine (JVM heap size).

If using AWS EC2, we recommend you start with a c4.2xlarge instance. For most projects, it is safe to start with a configuration along those lines, but you might want to have more powerful or multiple servers or instances (or equivalent) for situations calling for high availability, or to handle more load. When running Orbeon Forms PE on multiple servers or instances, you need one [PE subscription](https://www.orbeon.com/pricing) per server or instance.

For more details on sizing, see the section on [how much load Orbeon Forms can handle](/faq/form-builder-runner#how-much-load-can-orbeon-forms-handle).

## Java virtual machine configuration

Configure the Java VM with:

* `-Xmx` option for dedicated Java heap memory:
  * on a development machine: at least 1 GB of Java heap: `-Xmx1g`
  * on a production machine: at least 4 GB of Java heap: `-Xmx4g`
* Java 1.7 only (Orbeon Forms 2018.2 and earlier only): `-XX:MaxPermSize` for "permgen" space:
  * use at least: `-XX:MaxPermSize=256m`

Also, make sure that you do *not* have tiered compilation when using Java 7. See [A dangerous Java 7 JVM option: TieredCompilation](https://blog.orbeon.com/2015/08/a-dangerous-java-7-jvm-option.html).

*NOTE: On Unix systems, GIJ / GCG is not supported as there are reports of issues with that runtime environment and Orbeon Forms. Instead, we recommend you use the Oracle runtime Java environment.*

## Database setup

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

Out-of-the-box, Orbeon Forms includes an SQLite embedded database with multiple demo forms. This setup is designed for a quick start, but for development or production use, you should configure Orbeon Forms to utilize a separate relational database. For more information, see [Relational Database](/form-runner/persistence/relational-db). A warning banner will display as a reminder to make this change.

Note that the SQLite demo database is stored in `WEB-INF/orbeon-demo.sqlite`, located where the Orbeon Forms' `.war` file is uncompressed. Be aware that updating the WAR file might overwrite the demo database, resulting in the loss of any saved data.

To disable the `sqlite` embedded database and demo forms, add the following property:

```xml
<property
    as="xs:boolean"
    name="oxf.fr.persistence.sqlite.active"
    value="false"/>
```

See also [Removing the built-in SQLite database](/configuration/advanced/production-war#removing-the-built-in-sqlite-database).

## License installation (Orbeon Forms PE only)

* If you are running Orbeon Forms CE, you don't need to install a license file.
* If you are running Orbeon Forms PE:

  * complete the steps for your application server below
  * you can obtain a full licence from Orbeon, or get a [trial license](https://prod.orbeon.com/prod/fr/orbeon/register/new)
  * before starting your servlet container, copy your license file under the Orbeon Forms WAR file as:

  ```
  WEB-INF/resources/config/license.xml
  ```

With Orbeon Forms 4.1 and newer, you can also place license.xml file under the user's home directory. For example, on Unix systems:

```
~/.orbeon/license.xml
```

Orbeon Forms first searches for the license file within the WAR, and if not found attempts to find it under the home directory.

The benefit of this approach is that you don't have to find where the WAR file is deployed in your container, or to uncompress and recompress the WAR file with the license.

*NOTE: Orbeon Forms uses Java's `System.getProperty("user.home")` to identify the user's home directory. This corresponds to the user running the servlet container and not necessarily to the user of the developer or system administrator.*

## Configuration properties

You must create a default `properties-local.xml` file. See [Properties](/configuration/properties).

In addition, you must set the [`oxf.crypto.password`](https://doc.orbeon.com/pages/-LEkBrAoJAmy6iucVDPz#oxf.crypto.password) property to something different from the default.

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

Orbeon Forms will cause an error when starting if the default value for `oxf.crypto.password` is used. This is to prevent you from using the default value in production.

In addition, a password strength checker will also cause an error if the password is too weak. Ideally, use a randomly-generated strong password.

If you plan to use [Field-level encryption](/form-builder/advanced/field-level-encryption), also set `oxf.fr.field-encryption.password`. See [Field-level encryption configuration](/form-builder/advanced/field-level-encryption#configuration) for details.

If you plan to use [Access tokens](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-runner/access-token.md), also set `oxf.fr.access-token.password`. See [Access tokens configuration](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-runner/access-token.md#configuration) for details.

## Base URL for internal services

This step is sometimes optional.

Depending on your setup, if things don't work out of the box (for example if you have database errors with the sample forms) you might have to set the [oxf.url-rewriting.service.base-uri](/configuration/properties/general#oxfurl-rewritingservicebase-uri) configuration property in your `properties-local.xml` file.

Often, it is enough to set it to the following (adjusting for port and prefix):

```xml
<property
    as="xs:anyURI"
    name="oxf.url-rewriting.service.base-uri"
    value="http://localhost:8080/orbeon"/>
```

For more information about how to set configuration properties, see [Configuration Properties](/configuration/properties).

## Logging configuration

This step is optional. See [Logging](/installation/logging).

## Specific steps for your container / app server

* [Logging](/installation/logging)
* [Docker](/installation/docker)
* [Tomcat](/installation/tomcat)
* [WildFly](/installation/wildfly)
* [WebLogic](/installation/weblogic)
* [WebSphere](/installation/websphere)
* [GlassFish](/installation/glassfish)


# Logging

## Introduction

Most applications support some form of [*logging*](https://en.wikipedia.org/wiki/Logging_\(software\)). Orbeon Forms is no different.

* Internally, the Orbeon Forms web application uses the [SLF4J](http://www.slf4j.org/) API, which allows using various Java logging implementations such as Log4j or logback.
* Out of the box, Orbeon Forms uses [Log4j](https://logging.apache.org/log4j/2.x/) as logging implementation.

## Version of Log4j

Due to late 2021 security vulnerabilities with Log4j, and even though Orbeon Forms was not directly affected by those vulnerabilities, Orbeon Forms switched from Log4j 1.x to the latest [Log4j 2.x](https://logging.apache.org/log4j/2.x/) version in order to respond faster to future vulnerabilities should they arise. See the following blog posts:

* [Vulnerability in the log4j library](https://blog.orbeon.com/2021/12/vulnerability-in-log4j-library.html)
* [Orbeon Forms PE Log4j maintenance releases](https://blog.orbeon.com/2021/12/orbeon-forms-pe-log4j-maintenance.html)
* [More Orbeon Forms PE Log4j maintenance releases](https://blog.orbeon.com/2021/12/more-orbeon-forms-pe-log4j-maintenance.html)

The following versions of Orbeon Forms use Log4j 2.x:

* 2022.1 CE and PE and newer
* 2021.1 CE and PE and newer
* 2020.1.6 PE and newer
* 2019.2.4 PE and newer
* 2019.1.2 PE and newer
* 2018.2.5 PE and newer
* 2018.1.4 PE and newer

## Log4j configuration

Log4j 2.x uses different configuration files than Log4j 1.x.

However, until Orbeon Forms 2024.1.x., Orbeon Forms provides backward compatibility support for the older Log4j 1.x configuration file. This means that in most cases, you do not have to update your configuration file immediately if you are upgrading from an older version of Orbeon Forms.

**WARNING: With 2025.1 onward, there is no longer Log4j 1.x configuration file compatibility. Use a `log4j2.xml` configuration file.**

Here is the location and names of the configuration files:

| Log4j version | Location and Name                     | From Orbeon Forms version | Until Orbeon Forms version |
| ------------- | ------------------------------------- | ------------------------- | -------------------------- |
| Log4j 1.x     | `WEB-INF/resources/config/log4j.xml`  | From the beginning        | Orbeon Forms 2024.1.x      |
| Log4j 2.x     | `WEB-INF/resources/config/log4j2.xml` | See above                 | ongoing                    |

Versions of Orbeon Forms that support Log4j 2.x no longer ship with a `log4j.xml` configuration file, but ship with a `log4j2.xml` configuration file.

* If you have pre-existing `log4j.xml` configuration file, for example because you are upgrading from an older version, you can still use that configuration file, which will take precedence over the new `log4j2.xml` file. However, we do recommend that you consider moving to a `log4j2.xml` configuration file.
* If you do not yet have an existing `log4j.xml` file, we recommend that you update the `log4j2.xml` configuration file that ships with Orbeon Forms and that you do not create a `log4j.xml`.

*WARNING: With version of Orbeon Forms that use Log4j 2.x, and whether you are using `log4j.xml` or `log4j2.xml`, you must make sure that you do not have **duplicate log file names in the configuration**, even if some of them are unused, or Log4j 2.x will complain about that and ignore the configuration. Log4j 1.x did not use to consider this an error, but Log4j 2.x does.*

*WARNING: If you are creating or updating a `log4j2.xml` file, you **cannot** simply copy the contents of an existing `log4j.xml` to `log4j2.xml` as the two formats are incompatible! Instead, start with the `log4j2.xml` provided, and visit the* [*Log4j 2 configuration*](https://logging.apache.org/log4j/2.x/manual/configuration.html) *online to understand and make changes.*

## How Orbeon Forms initializes logging

First, when the Orbeon Forms web application starts, it attempts to initialize a minimal, predefined Log4j configuration:

* Appender: `<Console>`
* Level: `info`
* Pattern: `"%date{ISO8601} %-5level %logger{1} - %message%n"`

Second, once Orbeon Forms is able to read `log4j.xml` or `log4j2.xml`, it reconfigures Log4j with the specified configuration.

You can disable Orbeon Forms's Log4j initialization in `WEB-INF/web.xml` with:

```xml
<context-param>
    <param-name>oxf.initialize-logging</param-name>
    <param-value>false</param-value>
</context-param>
```

Doing so is necessary if you want to configure Log4j with your own configuration files, or if you want to remove Log4j and use a different SLF4J backend.

## Selecting a specific file path

By default, logging information is output to a file path relative to the directory where you start your application server. This is usually not what you want, as that makes it hard to know where the log file is.

Log4j 2.x (`WEB-INF/resources/config/log4j2.xml`):

```xml
<File
    name="SingleFileAppender"
    fileName="../logs/orbeon.log"
    append="false">
    <PatternLayout pattern="%date{ISO8601} - %tid - %-5level %logger{1} %X{orbeon-incoming-http-header-host} - %message%n"/>
</File>
```

Log4j 1.x (`WEB-INF/resources/config/log4j.xml`):

```xml
<appender name="SingleFileAppender" class="org.apache.log4j.FileAppender">
    <param name="File" value="../logs/orbeon.log"/>
    <param name="Append" value="false" />
    <param name="Encoding" value="UTF-8"/>
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{ISO8601} %-5p %c{1} %x - %m%n"/>
    </layout>
</appender>
```

You can change this by modifying the `fileName` (Log4j 2.x) or `File` (Log4j 1.x) parameter and set an *absolute file path* to the log file.

Log4j 2.x (`WEB-INF/resources/config/log4j2.xml`):

```xml
<File
    name="SingleFileAppender"
    fileName="/path/to/logs/orbeon.log"
    append="false">
    <PatternLayout pattern="%date{ISO8601} - %tid - %-5level %logger{1} %X{orbeon-incoming-http-header-host} - %message%n"/>
</File>
```

Log4j 1.x (`WEB-INF/resources/config/log4j.xml`):

```xml
<appender name="SingleFileAppender" class="org.apache.log4j.FileAppender">
    <param name="File" value="/path/to/logs/orbeon.log"/>
    <param name="Append" value="false" />
    <param name="Encoding" value="UTF-8"/>
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{ISO8601} %-5p %c{1} %x - %m%n"/>
    </layout>
</appender>
```

Note that on Windows, you must use forward slashes.

Log4j 2.x (`WEB-INF/resources/config/log4j2.xml`):

```xml
<File
    name="SingleFileAppender"
    fileName="C:/My Path/To/Logs/orbeon.log"
    append="false">
    <PatternLayout pattern="%date{ISO8601} - %tid - %-5level %logger{1} %X{orbeon-incoming-http-header-host} - %message%n"/>
</File>
```

Log4j 1.x (`WEB-INF/resources/config/log4j.xml`):

```xml
<appender name="SingleFileAppender" class="org.apache.log4j.FileAppender">
    <param name="File" value="C:/My Path/To/Logs/orbeon.log"/>
    <param name="Append" value="false" />
    <param name="Encoding" value="UTF-8"/>
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{ISO8601} %-5p %c{1} %x - %m%n"/>
    </layout>
</appender>
```

The benefit of changing this configuration is that you know exactly where the file is stored. This can be really handy when trying to troubleshoot issues.

## Reducing the amount of logging

By default, Orbeon Forms logs quite a lot of information at the `info` level. In case this is too much, you can set the level to `warning` or even `error`. Similarly, for debugging, you can set the level to the more verbose `debug`.

Log4j 2.x (`WEB-INF/resources/config/log4j2.xml`):

```xml
<File
    name="SingleFileAppender"
    fileName="/path/to/logs/orbeon.log"
    append="false">
    <PatternLayout pattern="%date{ISO8601} - %tid - %-5level %logger{1} %X{orbeon-incoming-http-header-host} - %message%n"/>
    <ThresholdFilter level="error"/>
</File>
```

Log4j 1.x (`WEB-INF/resources/config/log4j.xml`):

```xml
<appender name="SingleFileAppender" class="org.apache.log4j.FileAppender">
    <param name="File" value="/path/to/logs/orbeon.log"/>
    <param name="Append" value="false" />
    <param name="Encoding" value="UTF-8"/>
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{ISO8601} %-5p %c{1} %x - %m%n"/>
    </layout>
    <filter class="org.apache.log4j.varia.LevelRangeFilter">
        <param name="LevelMin" value="error"/>
    </filter>
</appender>
```

## Logging HTTP headers

\[SINCE Orbeon Forms 2022.1]

You can set the following property to ask Orbeon Forms to add all HTTP headers to the [Log4j Thread Context](https://logging.apache.org/log4j/2.x/manual/thread-context.html).

```xml
<property 
    as="xs:boolean"
    name="oxf.log4j.thread-context.http-headers"                
    value="true"/>
```

When this property is set, you can then log specific headers using the `%X{}` syntax in Log4j pattern, prefixing the header name in lower case by `orbeon-incoming-http-header-`. For instance, adding the following to your pattern will log the value of the `Host` header.

```
%X{orbeon-incoming-http-header-host}
```

## Client-side logging

As more work is getting done on the client (web browser), Orbeon Forms has added logging abilities there as well. In general, Orbeon Forms doesn't log much except in case of error, and then it logs to the JavaScript console, which is usually not consulted by the end-user.

As of Orbeon Forms 2021.1, Orbeon Forms uses [Log4s](https://github.com/Log4s/log4s) and [Scribe](https://github.com/outr/scribe) to log on the client. However, the logging configuration is currently not customizable by the user.

## See also

* [XForms logging](/configuration/advanced/xforms-logging)
* [Relational database logging](/configuration/troubleshooting/database-logging)
* Blog posts:
  * [Vulnerability in the log4j library](https://blog.orbeon.com/2021/12/vulnerability-in-log4j-library.html)
  * [Orbeon Forms PE Log4j maintenance releases](https://blog.orbeon.com/2021/12/orbeon-forms-pe-log4j-maintenance.html)


# Configuration banner

## Availability

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

## Rationale

Orbeon Forms requires a few important configuration steps to be performed before it can be used, including:

* setting a password for encryption
* configuring a separate database

If either of these is not properly configured, Orbeon Forms show a banner at the top of application pages.

![Configuration banner](/files/NWHd3srUUAGi7yF91DDg)

You can learn more about the specific configurations that are missing by [configuring logging](/installation/logging) and checking the Orbeon Forms log file. The file will contain something like the following at the `ERROR` level:

```
The following Orbeon Forms configurations are incomplete:

- The password for the `oxf.crypto.password` property is missing or not strong enough.
- The password for the `oxf.fr.access-token.password` property is missing or not strong enough (optional).
- The password for the `oxf.fr.field-encryption.password` property is missing or not strong enough (optional).
- The database configuration is missing or incomplete.

Please visit this page for more: https://doc.orbeon.com/installation/configuration-banner
```

In addition, the following optional features require configuring separate passwords:

* [Token-based permissions](/form-runner/access-control/tokens)
* [Field-level encryption](/form-builder/advanced/field-level-encryption)

You do not have to configure these features if you do not plan to use them. However, the fact that these features are not configured will be logged at the `INFO` level.

The following subsections cover the individual configurations in more detail.

## Configurations

### Database

Out-of-the-box, Orbeon Forms includes an SQLite embedded database with multiple demo forms. This setup is designed for a quick start, but for development or production use, you should configure Orbeon Forms to use a separate [relational database](/form-runner/persistence/relational-db). In addition, make sure you also [disable the embedded SQLite database](/form-runner/persistence/relational-db#disabling-the-embedded-sqlite-provider).

The reason for considering this an incomplete configuration is that the SQLite database is not suitable for production use with Orbeon Forms at this point. This is not necessarily due to shortcomings of SQLite, but due to the fact that the SQLite database is embedded in the Orbeon Forms WAR file and more likely to be deleted by mistake. In addition, most users want to use a more powerful database for production use.

You are not required to configure a separate database if you are using Orbeon Forms for evaluation purposes only, but in this case the configuration banner will show.

See also [Database setup](/installation#database-setup).

### Main encryption password

You must set the [`oxf.crypto.password`](/configuration/properties/general#oxfcryptopassword) property to something different from the default. This is used for the following:

* internal encryption (admin token, operations, internally-submitted data, uploaded URLs)
* product version in the URL
* `cid:` URLs and XML metadata format

This password is mostly used for transient data, but you must still change it.

### Access token password

If you plan to use [Token-based permissions](/form-runner/access-control/tokens), also set `oxf.fr.access-token.password`. If you don't set it and try to use access tokens, an error will be produced when the password is needed.

### Field-level encryption password

If you plan to use [Field-level encryption](/form-builder/advanced/field-level-encryption), also set `oxf.fr.field-encryption.password`. If you don't set it and try to use field-level encryption, an error will be produced when the password is needed.

## See also

* [Installation](/installation)
* [Logging](/installation/logging)
* [Properties](/configuration/properties)
* [Removing the built-in SQLite database](/configuration/advanced/production-war#removing-the-built-in-sqlite-database)


# Docker

## Availability

* \[SINCE Orbeon Forms 2023.1.3, 2024.1]
* This is an [Orbeon Forms PE](https://www.orbeon.com/download) feature.

## Docker images

Multiple Docker images are available from the [Orbeon repository on Docker Hub](https://hub.docker.com/u/orbeon):

* [`orbeon/orbeon-forms`](https://hub.docker.com/r/orbeon/orbeon-forms): the Orbeon Forms application running on Tomcat or WildFly
* [`orbeon/postgres`](https://hub.docker.com/r/orbeon/postgres): a PostgreSQL database prepopulated with the Orbeon Forms database schema

The `orbeon/orbeon-forms` image can be run as a standalone container, as it contains an SQLite database with demo forms. This is intended for evaluation purposes only, and you will probably want to use another database in production. For this, you can use the `orbeon/postgres` image, which contains a PostgreSQL database prepopulated with the Orbeon Forms database schema. See the [Docker Compose configuration](#docker-compose-configuration) below for an example of how to run Orbeon Forms with PostgreSQL.

To install Orbeon Forms on Microsoft Azure, see [Azure](https://github.com/orbeon/orbeon-forms-doc/blob/master/installation/installation/azure.md).

## Evaluation mode

To create a container from the `orbeon/orbeon-forms` image, with the embedded SQLite database, run the following `docker` command:

```bash
docker create \
    --name orbeon-forms-with-sqlite \
    -p 8080:8080 \
    -v ~/.orbeon/license.xml:/usr/local/tomcat/webapps/orbeon/WEB-INF/resources/config/license.xml \
    orbeon/orbeon-forms:2025.1.2-pe
```

Make sure to replace `~/.orbeon/license.xml` with the path to your license file and to use another port if `8080` is already in use on your machine.

To start the container:

```bash
docker start -a orbeon-forms-with-sqlite
```

You can then access Orbeon Forms using the following URL: <http://localhost:8080/orbeon>.

While using SQLite, keep in mind that all modifications to the form definitions and data will be stored in the container's filesystem and will be lost if the container is deleted. The file storing the SQLite database is located at `/usr/local/tomcat/webapps/orbeon/WEB-INF/orbeon-demo.sqlite` inside the container.

### Storing SQLite data outside the container

To persist the data outside the container, you can copy the `orbeon-demo.sqlite` file to the host and mount it inside the container while creating the container from the image, by adding the following argument:

```bash
-v /path/to/orbeon-demo.sqlite:/usr/local/tomcat/webapps/orbeon/WEB-INF/orbeon-demo.sqlite
```

This is not recommended for production use.

This involves creating a first container just to extract the `orbeon-demo.sqlite` file, then creating a second container with the mounted file. Alternatively, you can also extract the `orbeon-demo.sqlite` file from the [Orbeon Forms WAR file](https://www.orbeon.com/download).

## Docker Compose configuration

The following `docker-compose.yml` file can be used to start Orbeon Forms with a PostgreSQL database:

```yaml
version: '3.8'
services:
  orbeon-forms:
    image: orbeon/orbeon-forms:2025.1.2-pe
    ports:
      - ${ORBEON_TOMCAT_PORT:-8080}:8080
    volumes:
      - ${ORBEON_PROPERTIES_FILE:-./properties-local.xml}:/usr/local/tomcat/webapps/orbeon/WEB-INF/resources/config/properties-local.xml
      - ${ORBEON_TOMCAT_CONTEXT_FILE:-./orbeon.xml}:/usr/local/tomcat/conf/Catalina/localhost/orbeon.xml
#      - ${ORBEON_LOG4J2_FILE:-./log4j2.xml}:/usr/local/tomcat/webapps/orbeon/WEB-INF/resources/config/log4j2.xml
    secrets:
      - source: license
        target: /usr/local/tomcat/webapps/orbeon/WEB-INF/resources/config/license.xml
    depends_on:
      - postgres
    networks:
      - orbeon-forms-and-postgres
  postgres:
    image: orbeon/postgres:2025.1.1-pe
    restart: always
    ports:
      - ${ORBEON_POSTGRES_PORT:-5432}:5432
    volumes:
      - pgdata:/var/lib/postgresql
    environment:
      POSTGRES_DB: ${ORBEON_POSTGRES_DB:-orbeon}
      POSTGRES_USER: ${ORBEON_POSTGRES_USER:-orbeon}
      POSTGRES_PASSWORD: ${ORBEON_POSTGRES_PASSWORD:-orbeon}
#      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
#    secrets:
#      - postgres_password
    networks:
      - orbeon-forms-and-postgres
networks:
  orbeon-forms-and-postgres:
    driver: bridge
volumes:
  pgdata:
    name: ${ORBEON_POSTGRES_VOLUME:-orbeon_pgdata}
secrets:
  license:
    file: ${ORBEON_LICENSE_FILE:-~/.orbeon/license.xml}
#  postgres_password:
#    file: ${ORBEON_POSTGRES_PASSWORD_FILE:-postgres_password.txt}
```

This Docker Compose file is meant as an example and can be customized to fit your needs.

Outside the `docker-compose.yml` file, you will also need at least three other files:

* the license file (like in the single container case)
* an Orbeon Forms properties file to specify that PostgreSQL must be used instead of SQLite
* a Tomcat context configuration file to specify the PostgreSQL data source

### Properties file

The `properties-local.xml` properties file needs to contain at least the following:

```xml
<properties xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:oxf="http://www.orbeon.com/oxf/processors">
    <property as="xs:string"  name="oxf.crypto.password"               value="CHANGE THIS PASSWORD"/>
    <property as="xs:string"  name="oxf.fr.persistence.provider.*.*.*" value="postgresql"/>
    <property as="xs:boolean" name="oxf.fr.persistence.sqlite.active"  value="false"/>
</properties>
```

Choose a strong password for the `oxf.crypto.password` property. See [Properties](https://doc.orbeon.com/configuration/properties) for more information about this property and other properties in general.

### Tomcat context configuration

The `orbeon.xml` Tomcat context configuration file needs to contain at least the following:

```xml
<Context path="/orbeon">
    <Resource
        name="jdbc/postgresql"
        driverClassName="org.postgresql.Driver"
    
        auth="Container"
        type="javax.sql.DataSource"
    
        initialSize="3"
        maxActive="10"
        maxIdle="10"
        maxWait="30000"
    
        poolPreparedStatements="true"
    
        testOnBorrow="true"
        validationQuery="select 1"
    
        username="orbeon"
        password="orbeon"
        url="jdbc:postgresql://postgres:5432/orbeon?useUnicode=true&amp;characterEncoding=UTF8&amp;socketTimeout=30&amp;tcpKeepAlive=true"/>
</Context>
```

If you've changed the default PostgreSQL service name, database name, user, password, or port in the Docker Compose configuration, make sure to update the `url`, `username`, and `password` attributes accordingly.

In particular, note that the `postgres` hostname in the `url` attribute refers to the name of the PostgreSQL service in the Docker Compose configuration.

### Environment variables

Using the example Docker Compose configuration above, you can customize the behavior of the containers by editing the configuration file directly or by using the following environment variables:

| Environment variable         | Default value            | Description                                     |
| ---------------------------- | ------------------------ | ----------------------------------------------- |
| `ORBEON_LICENSE_FILE`        | `~/.orbeon/license.xml`  | Path to the Orbeon Forms license file           |
| `ORBEON_PROPERTIES_FILE`     | `./properties-local.xml` | Path to the Orbeon Forms properties file        |
| `ORBEON_TOMCAT_CONTEXT_FILE` | `./orbeon.xml`           | Path to the Tomcat context configuration file   |
| `ORBEON_TOMCAT_PORT`         | 8080                     | Tomcat port                                     |
| `ORBEON_POSTGRES_PORT`       | 5432                     | PostgreSQL port                                 |
| `ORBEON_POSTGRES_DB`         | orbeon                   | PostgreSQL database                             |
| `ORBEON_POSTGRES_USER`       | orbeon                   | PostgreSQL user                                 |
| `ORBEON_POSTGRES_PASSWORD`   | orbeon                   | PostgreSQL password                             |
| `ORBEON_POSTGRES_VOLUME`     | orbeon\_pgdata           | Docker volume used to store the PostgreSQL data |

The values of those environment variables can be set in a `.env` file in the same directory as the `docker-compose.yml` file.

Note that the PostgreSQL password can be specified via a Docker secret file. To do so, uncomment the lines related to `postgres_password` in the Docker Compose configuration file.

### Running the containers using Docker Compose

To start Orbeon Forms with PostgreSQL, run the following command in the directory containing the `docker-compose.yml` file:

```bash
docker compose up
```

You can also specify the location of the Docker Compose file using the `-f` option.

## JDBC drivers and other databases

The `orbeon/orbeon-forms` image contains the JDBC drivers for SQLite and PostgreSQL. Other JDBC drivers are not included for licensing reasons. If you need to use another database, you will need to add the JDBC driver to the image.

This can be done by using the following Dockerfile, using MySQL as an example:

```dockerfile
FROM orbeon/orbeon-forms:2025.1.2-pe

RUN mkdir -p /tmp/orbeon
WORKDIR /tmp/orbeon

# JDBC driver for MySQL
RUN wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-j-9.0.0.tar.gz \
    && tar xvfz mysql-connector-j-9.0.0.tar.gz \
    && mv mysql-connector-j-9.0.0/mysql-connector-j-9.0.0.jar /usr/local/tomcat/lib/ 

# Remove SQLite database and JDBC driver (optional)
RUN rm -f /usr/local/tomcat/webapps/orbeon/WEB-INF/orbeon-demo.sqlite \
    && rm -f /usr/local/tomcat/webapps/orbeon/WEB-INF/lib/sqlite-jdbc-*.jar 

# Remove PostgreSQL JDBC driver (optional)
RUN rm -f /usr/local/tomcat/lib/postgresql-*.jar

# Cleanup
RUN rm -rf /tmp/orbeon
```

The customized image above can then be built using the following command:

```bash
docker build -f Dockerfile.mysql -t "orbeon/orbeon-forms-mysql:2025.1.1-pe" .
```

For further information about configuring Orbeon Forms to use a different database, see [Using a relational database](/form-runner/persistence/relational-db).

## Logging

By default, Orbeon Forms outputs its logs to the console. To change the logging configuration, you can mount a `log4j2.xml` file inside the container:

```bash
-v /path/to/log4j2.xml:/usr/local/tomcat/webapps/orbeon/WEB-INF/resources/config/log4j2.xml
```

See [Logging](/installation/logging) for more information.

## WildFly image

To use WildFly instead of Tomcat:

* use the tags suffixed with `-wildfly` (for example `2025.1-pe-wildfly`)
* mount the files in `/opt/jboss/wildfly/standalone/deployments/orbeon.war/WEB-INF` instead of `/usr/local/tomcat/webapps/orbeon/WEB-INF`
* configure data sources in WildFly's `standalone.xml` configuration file, which should be copied or mounted into the `/docker-entrypoint-wildfly.d/` directory

```bash
docker create \
    --name orbeon-forms-with-sqlite \
    -p 8080:8080 \
    -v ~/.orbeon/license.xml:/opt/jboss/wildfly/standalone/deployments/orbeon.war/WEB-INF/resources/config/license.xml \
    orbeon/orbeon-forms:2025.1.2-pe-wildfly
```

See also [WildFly](/installation/wildfly) for more information.


# Azure

## Availability

* \[SINCE Orbeon Forms 2024.1]
* This is an [Orbeon Forms PE](https://www.orbeon.com/download) feature.

## Overview

This guide walks you through deploying Orbeon Forms on Microsoft Azure using:

* [Entra ID](https://learn.microsoft.com/en-us/entra/identity/) for user/group management
* [Azure Storage](https://learn.microsoft.com/en-us/azure/storage/) for configuration files
* [PostgreSQL](https://learn.microsoft.com/en-us/azure/postgresql/) for the database
* [Kubernetes](https://learn.microsoft.com/en-us/azure/aks/) for container orchestration
* [Azure Virtual Network](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview) for database-cluster communication
* [Azure Container Registry](https://learn.microsoft.com/en-us/azure/container-registry/) for custom Docker images (optional)

We will use a self-signed certificate and a single-node Kubernetes cluster for demonstration purposes. In production, you would likely use a certificate signed by a trusted certificate authority (CA) and a multi-node cluster.

The users/groups will be accessed via the OpenID Connect (OIDC) protocol and WildFly's native OIDC support implementation. Entra ID groups will be mapped to WildFly roles, which will be used to control access to Orbeon Forms.

We will create two groups/roles:

* `orbeon-user`
* `orbeon-admin`

As well as two test users:

* `testuser1` (member of `orbeon-user`)
* `testuser2` (member of `orbeon-user` and `orbeon-admin`)

Orbeon Forms will be accessible only to users from the `orbeon-user` group (i.e. to both `testuser1` and `testuser2`). Form Builder and Forms Admin pages will be accessible only to users from the `orbeon-admin` group (i.e. to `testuser2` only).

A more complete example Bash script is available on [GitHub](https://github.com/orbeon/orbeon-forms/tree/master/docker/azure). It includes more commands, which will check if the resources already exist, among other things, but it follows roughly the same steps as described here.

Some values in the commands used in this guide will be hardcoded for simplicity. In a real-world scenario, you would likely want to parameterize them, for example by using environment variables.

## Requirements

The main requirement is an account with an Azure subscription.

The following utilities will be used during the installation process:

* `az` (Azure CLI)
* `psql` (PostgreSQL client)
* `kubectl` (Kubernetes deployment)
* `jq` (JSON manipulation)
* `base64` (Kubernetes passwords encoding)
* core Linux utilities such as `cat`, `curl`, `echo`, etc.

All steps described below can also be done manually via the Azure UI.

## Login and general configuration

The very first step is to login to Azure and set the Microsoft Graph API scope:

```bash
az login --scope https://graph.microsoft.com/.default
```

The following providers need to be registered:

```bash
az provider register --namespace Microsoft.Compute
az provider register --namespace Microsoft.ContainerRegistry
az provider register --namespace Microsoft.ContainerService
az provider register --namespace Microsoft.DBforPostgreSQL
az provider register --namespace Microsoft.Storage
```

## Self-signed certificate

Generate a self-signed certificate for the application:

```bash
keytool \
  -genkey \
  -alias server \
  -keyalg RSA \
  -validity 3650 \
  -keysize 2048 \
  -keystore application.keystore \
  -storepass password \
  -keypass password \
  -dname "CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown"
```

This command uses the keystore filename (`application.keystore`) and passwords (`password`) used by default in WildFly's configuration (`standalone.xml` file). Using a stronger password is recommended.

## Entra ID

Retrieve the Entra ID domain:

```bash
ENTRA_ID_DOMAIN=$(az rest \
                  --method get \
                  --url 'https://graph.microsoft.com/v1.0/domains' \
                  --query 'value[0].id' \
                  -o tsv)
```

It should look like `contoso.onmicrosoft.com`.

### Users

Create two test users `testuser1` and `testuser2` (replace `contoso.onmicrosoft.com` with your domain):

```bash
az ad user create \
  --user-principal-name 'testuser1@contoso.onmicrosoft.com' \
  --password 'CHANGEME0!' \
  --display-name 'Test User 1'
```

The [user principal name (UPN)](https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/plan-connect-userprincipalname) follows the format `prefix@domain`.

Optionally, you can associate an email address with the user. For this, we use the Microsoft Graph API:

```bash
az rest \
  --method patch \
  --url "https://graph.microsoft.com/v1.0/users/$user_id" \
  --body "{\"mail\":\"$email\"}"
```

To retrieve the user ID:

```bash
az ad user show --id 'testuser1@contoso.onmicrosoft.com' --query id -o tsv
```

### Groups

Create two groups `orbeon-user` and `orbeon-admin`:

```bash
az ad group create --display-name 'orbeon-user' --mail-nickname 'orbeon-user'
az ad group create --display-name 'orbeon-admin' --mail-nickname 'orbeon-admin'
```

Add users to groups:

```bash
az ad group member add --group 'orbeon-user' --member-id "$user1_id"
az ad group member add --group 'orbeon-user' --member-id "$user2_id"
az ad group member add --group 'orbeon-admin' --member-id "$user2_id"
```

### Application

Create an application called `Orbeon Forms`. This is needed to expose our users and groups to Orbeon Forms via the OIDC protocol.

```bash
az ad app create --display-name 'Orbeon Forms' --sign-in-audience 'AzureADMyOrg'
```

Retrieve the application ID:

```bash
ENTRA_ID_APP_ID=$(az ad app list --query "[?displayName=='Orbeon Forms'].appId" -o tsv)
```

You can also retrieve it from the output of the previous command.

Add an API identifier URI to the application:

```bash
az ad app update --id "$ENTRA_ID_APP_ID" --identifier-uris "api://$ENTRA_ID_APP_ID"
```

Retrieve the application object ID:

```bash
ENTRA_ID_APP_OBJECT_ID=$(az ad app list --query "[?displayName=='Orbeon Forms'].id" -o tsv)
```

Note that this is not the same as the application ID (`id` vs `appId`).

To have access to the groups/roles in both the OIDC ID and access tokens, we need to add a scope to the application. If we don't do this, WildFly's OIDC implementation will be unable to retrieve the groups/roles.

Add a scope called `groups.access` to the application:

```bash
az rest \
  --method PATCH \
  --uri "https://graph.microsoft.com/v1.0/applications/$ENTRA_ID_APP_OBJECT_ID" \
  --headers 'Content-Type=application/json' \
  --body "$(jq -n \
    --arg scope_id "$(uuidgen)" \
    '{
      api: {
        oauth2PermissionScopes: [{
          adminConsentDescription: "Allow the application to access groups on behalf of the signed-in user.",
          adminConsentDisplayName: "Access groups",
          id: $scope_id,
          isEnabled: true,
          type: "User",
          userConsentDescription: "Allow the application to access groups on your behalf.",
          userConsentDisplayName: "Access groups",
          value: "groups.access"
        }]
      }
    }')"
```

The scope ID is generated using `uuidgen`. Note that the `jq` command above is used to inject the scope ID into the JSON body. This can be done manually or in other ways as well.

For an existing scope, you can retrieve the scope ID from its name using the following command:

```bash
ENTRA_ID_SCOPE_ID=$(az ad app show \
                    --id "$ENTRA_ID_APP_ID" \
                    --query "api.oauth2PermissionScopes[?value=='groups.access'].id" \
                    -o tsv)
```

Pre-authorize the application, so that the users won't have to explicitly consent to the permissions:

```bash
az ad app show --id "$ENTRA_ID_APP_ID" | \
  jq --arg app_id "$ENTRA_ID_APP_ID" \
     --arg scope_id "$ENTRA_ID_SCOPE_ID" \
     '.api.preAuthorizedApplications = [{
       "appId": $app_id,
       "delegatedPermissionIds": [$scope_id]
     }]' | \
  az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$ENTRA_ID_APP_OBJECT_ID" --body @-
```

Add a client secret:

```bash
ENTRA_ID_CREDENTIAL_SECRET=$(az ad app credential reset \
                             --id "$ENTRA_ID_APP_ID" \
                             --display-name 'Orbeon Forms Credential' \
                             --years 2 | jq -r '.password')
```

Beware: the command above will update/overwrite any existing secret with the same name.

Only security group membership claims will be included as group IDs:

```bash
az ad app show --id "$ENTRA_ID_APP_ID" | \
  jq '.groupMembershipClaims = "SecurityGroup"' | \
  az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$ENTRA_ID_APP_OBJECT_ID" --body @-
```

Add optional OIDC claims (groups included as roles, email):

```bash
az ad app show --id "$ENTRA_ID_APP_ID" | \
  jq '.optionalClaims = {
    "accessToken": [{
      "additionalProperties": ["emit_as_roles"],
      "essential": false,
      "name": "groups",
      "source": null
    }],
    "idToken": [{
      "additionalProperties": ["emit_as_roles"],
      "essential": false,
      "name": "groups",
      "source": null
    },
    {
      "additionalProperties": [],
      "essential": false,
      "name": "email",
      "source": null
    }]
  }' | \
  az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$ENTRA_ID_APP_OBJECT_ID" --body @-
```

Note that we have updated the application manifest multiple times, using the `az rest --method PATCH` command, but this was mainly done for demonstration purposes. In a real-world scenario, you would likely update the application manifest only once, with all the changes.

Grant Microsoft Graph permissions:

```bash
# Azure API constants
API_MICROSOFT_GRAPH='00000003-0000-0000-c000-000000000000'
API_PERMISSION_OPENID='37f7f235-527c-4136-accd-4a02d197296e'
API_PERMISSION_EMAIL='64a6cdd6-aab1-4aaf-94b8-3cc8405e90d0'

az ad app permission add \
  --id "$ENTRA_ID_APP_ID" \
  --api "$API_MICROSOFT_GRAPH" \
  --api-permissions "$API_PERMISSION_OPENID=Scope"
  
az ad app permission add \
  --id "$ENTRA_ID_APP_ID" \
  --api "$API_MICROSOFT_GRAPH" \
  --api-permissions "$API_PERMISSION_EMAIL=Scope"
```

Grant admin consent for permissions above:

```bash
az ad app permission admin-consent --id "$ENTRA_ID_APP_ID"
```

## Configuration files

Get the tenant ID:

```bash
ENTRA_ID_TENANT_ID=$(az account show --query tenantId -o tsv)
```

Build the OIDC provider URL:

```bash
ENTRA_ID_PROVIDER_URL="https://login.microsoftonline.com/$ENTRA_ID_TENANT_ID/v2.0"
```

Build the scope API URL:

```bash
ENTRA_ID_API_SCOPE_URL="api://$ENTRA_ID_APP_ID/$ENTRA_ID_SCOPE_VALUE"
```

Generate the OIDC configuration file:

```bash
cat << EOF > oidc.json
{
  "client-id": "$ENTRA_ID_APP_ID",
  "provider-url": "$ENTRA_ID_PROVIDER_URL",
  "credentials": {
    "secret": "$ENTRA_ID_CREDENTIAL_SECRET"
  },
  "principal-attribute": "oid",
  "scope": "profile $ENTRA_ID_API_SCOPE_URL"
}
EOF
```

The above configuration will return users as IDs. If you want to return users as emails, you can use `email` instead of `oid` as the value for `principal-attribute`.

The `oidc.json` file will look like this:

```json
{
  "client-id": "4a3e3344-7f1b-4aea-b5d4-a18705757270",
  "provider-url": "https://login.microsoftonline.com/9eacdffb-6700-4a98-a9a7-507d898fdfa8/v2.0",
  "credentials": {
    "secret": "BP28Q~XQ68YAyh_N2vr1vw8EPeaGKAwRUTXGJb3N"
  },
  "principal-attribute": "oid",
  "scope": "profile api://4a3e3344-7f1b-4aea-b5d4-a18705757270/groups.access"
}
```

In OIDC, we will refer to the groups by their IDs (not their display names):

```bash
ENTRA_ID_USER_GROUP_ID=$(az ad group show --group 'orbeon-user' --query id -o tsv)
ENTRA_ID_ADMIN_GROUP_ID=$(az ad group show --group 'orbeon-admin' --query id -o tsv)
```

Generate the Form Builder permissions file:

```bash
cat << EOF > form-builder-permissions.xml
<roles>
  <role name="$ENTRA_ID_ADMIN_GROUP_ID" app="*" form="*"/>
</roles>
EOF
```

The `form-builder-permissions.xml` file will look like this:

```xml
<roles>
  <role name="79390699-2df8-4110-b0b3-2b97c3db1821" app="*" form="*"/>
</roles>
```

Extract the `web.xml` file from the [Orbeon Forms WAR file](https://www.orbeon.com/download) or download [`web.template.xml` from GitHub](https://github.com/orbeon/orbeon-forms/blob/master/docker/azure/web.template.xml), and make sure the following lines are present:

```xml
<web-app>
    <!-- PostgreSQL configuration -->
    <resource-ref>
        <description>DataSource</description>
        <res-ref-name>jdbc/postgresql</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
    <!-- Restrict Orbeon Forms to the orbeon-user group/role -->
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Form Runner</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ENTRA_ID_USER_GROUP_ID</role-name>
        </auth-constraint>
    </security-constraint>
    <!-- Restrict Form Builder and Forms Admin pages to the orbeon-admin group/role -->
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Form Builder</web-resource-name>
            <url-pattern>/fr/orbeon/builder/*</url-pattern>
            <url-pattern>/fr/admin</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ENTRA_ID_ADMIN_GROUP_ID</role-name>
        </auth-constraint>
    </security-constraint>
    <!-- Use OIDC for authentication -->
    <login-config>
        <auth-method>OIDC</auth-method>
    </login-config>
    <security-role>
        <role-name>ENTRA_ID_USER_GROUP_ID</role-name>
        <role-name>ENTRA_ID_ADMIN_GROUP_ID</role-name>
    </security-role>
</web-app>
```

Replace `ENTRA_ID_USER_GROUP_ID` and `ENTRA_ID_ADMIN_GROUP_ID` with the actual group IDs.

Download [`standalone.postgresql.azure.xml` from GitHub](https://github.com/orbeon/orbeon-forms/blob/master/docker/azure/standalone.postgresql.azure.xml) and make sure the following lines are present:

```xml
<server>
    <profile>
        <subsystem xmlns="urn:jboss:domain:datasources:7.2">
            <datasources>
                <!-- PostgreSQL configuration -->
                <datasource jndi-name="java:/jdbc/postgresql" pool-name="postgresql" enabled="true" use-java-context="true">
                    <connection-url>jdbc:postgresql://DATABASE_SERVER.postgres.database.azure.com:5432/orbeon?useUnicode=true&amp;characterEncoding=UTF8&amp;socketTimeout=30&amp;tcpKeepAlive=true</connection-url>
                    <driver>postgresql-42.7.3.jar</driver>
                    <security user-name="orbeon@DATABASE_SERVER" password="orbeon"/>
                </datasource>
            </datasources>
        </subsystem>
    </profile>
</server>
```

Replace `DATABASE_SERVER` with your database server name, which must be unique across Azure.

We need `jboss-web-xml` to be configured for PostgreSQL:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
    <resource-ref>
        <res-ref-name>jdbc/postgresql</res-ref-name>
        <jndi-name>java:/jdbc/postgresql</jndi-name>
    </resource-ref>
</jboss-web>
```

We will also configure the Orbeon Forms properties in `properties-local.xml` to use PostgreSQL instead of SQLite:

```xml
<properties xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <!-- Change the following values for production use -->
    <property as="xs:string" name="oxf.crypto.password"              value="J$WYh!Ltgg4Q^jnR"/>
    <property as="xs:string" name="oxf.fr.field-encryption.password" value=""/>
    <property as="xs:string" name="oxf.fr.access-token.password"     value=""/>

    <property as="xs:string"  name="oxf.fr.persistence.provider.*.*.*" value="postgresql"/>
    <property as="xs:boolean" name="oxf.fr.persistence.sqlite.active"  value="false"/>
</properties>
```

## Resource group

Create a resource group:

```bash
az group create \
  --name 'orbeon-forms-resource-group' \
  --location 'westus'
```

It will be used by all the resources we create (Azure Storage, PostgreSQL, etc.).

## Storage

Create a storage account:

```bash
az storage account create \
  --name "$STORAGE_ACCOUNT" \
  --resource-group 'orbeon-forms-resource-group' \
  --location 'westus' \
  --sku Standard_LRS
```

The storage account name must be unique across Azure.

Create a storage share:

```bash
az storage share create \
  --name 'orbeon-forms-share' \
  --account-name "$STORAGE_ACCOUNT"
```

To upload a file to the storage share, use the following command:

```bash
az storage file upload \
  --account-name "$STORAGE_ACCOUNT" \
  --share-name 'orbeon-forms-share' \
  --source "$source" \
  --path "$destination"
```

We will need to upload the following configuration files:

* `application.keystore`
* `form-builder-permissions.xml`
* `jboss-web.xml`
* `license.xml` (get a free trial license [here](https://www.orbeon.com/download) if needed)
* `oidc.json`
* `properties-local.xml`
* `standalone.xml`
* `web.xml`

Retrieve the storage account access key:

```bash
STORAGE_ACCOUNT_SECRET_KEY=$(az storage account keys list \
                             --account-name "$STORAGE_ACCOUNT" \
                             --resource-group 'orbeon-forms-resource-group' \
                             --query '[0].value' \
                             --output tsv)
```

Alternatively, all configuration files could be included in a custom Docker image, but this is less flexible when configuration needs to be changed.

## Database

Create a PostgreSQL database server:

```bash
az postgres flexible-server create \
  --name "$DATABASE_SERVER" \
  --resource-group 'orbeon-forms-resource-group' \
  --location 'westus' \
  --admin-user "$DATABASE_ADMIN_USERNAME" \
  --admin-password "$DATABASE_ADMIN_PASSWORD" \
  --sku-name standard_d2ds_v4 \
  --version 16 \
  --public-access None
```

The database server name must be unique across Azure.

Retrieve your client's public IP address:

```bash
DATABASE_PUBLIC_IP=$(curl -s 'https://api.ipify.org')
```

This will allow you to configure a firewall rule to allow access to the database server only from your client's IP address.

```bash
az postgres flexible-server firewall-rule create \
  --rule-name 'local-ip-allowed' \
  --name "$DATABASE_SERVER" \
  --resource-group 'orbeon-forms-resource-group' \
  --start-ip-address "$DATABASE_PUBLIC_IP" \
  --end-ip-address "$DATABASE_PUBLIC_IP"
```

Alternatively, you can call `az postgres flexible-server create` without `--public-access None`. This will automatically create the firewall rule above.

Create the `orbeon` database:

```bash
az postgres flexible-server db create \
  --database-name 'orbeon' \
  --server-name "$DATABASE_SERVER" \
  --resource-group 'orbeon-forms-resource-group';
```

Make the database administrator password available to the `psql` command:

```bash
export PGPASSWORD="$DATABASE_ADMIN_PASSWORD"
```

Create the `orbeon` database user:

```bash
psql \
  --host "$DATABASE_SERVER.postgres.database.azure.com" \
  --username "$DATABASE_ADMIN_USERNAME" \
  --dbname 'orbeon' \
  --command "CREATE USER \"orbeon@$DATABASE_SERVER\" WITH PASSWORD '$password';"
```

Note that Azure PostgreSQL users need to follow the format `username@servername`.

Grant privileges to the `orbeon` database user:

```bash
psql \
  --host "$DATABASE_SERVER.postgres.database.azure.com" \
  --username "$DATABASE_ADMIN_USERNAME" \
  --dbname 'orbeon' \
  --command "GRANT ALL PRIVILEGES ON DATABASE orbeon TO \"orbeon@$DATABASE_SERVER\";" \
  --command "GRANT ALL PRIVILEGES ON SCHEMA public TO \"orbeon@$DATABASE_SERVER\";" \
  --command "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"orbeon@$DATABASE_SERVER\";" \
  --command "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO \"orbeon@$DATABASE_SERVER\";" \
  --command "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO \"orbeon@$DATABASE_SERVER\";" \
  --command "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO \"orbeon@$DATABASE_SERVER\";";
```

Create the Orbeon Forms database schema:

```bash
psql \
  --host "$DATABASE_SERVER.postgres.database.azure.com" \
  --username "$DATABASE_ADMIN_USERNAME" \
  --dbname 'orbeon' \
  --file ./postgresql-2024_1.sql;
```

The SQL files needed to create the Orbeon Forms database schema can be downloaded from [PostgreSQL database setup](/form-runner/persistence/relational-db#postgresql-database-setup).

You can then delete the `local-ip-allowed` firewall rule:

```bash
az postgres flexible-server firewall-rule delete \
  --rule-name 'local-ip-allowed' \
  --name "$DATABASE_SERVER" \
  --resource-group 'orbeon-forms-resource-group' \
  --yes;
```

Alternatively, you can use other databases, such as Azure SQL Database or Azure Database for MySQL.

## Container registry

If you need to use a custom Docker image, create an Azure Container Registry:

```bash
az acr create --name "$CONTAINER_REGISTRY" --resource-group 'orbeon-forms-resource-group' --sku Basic
```

The container registry name must be unique across Azure.

Then login to the Azure Container Registry:

```bash
az acr login --name "$CONTAINER_REGISTRY"
```

Retrieve the Azure Container Registry ID:

```bash
CONTAINER_REGISTRY_ID=$(az acr show --name "$CONTAINER_REGISTRY" --resource-group 'orbeon-forms-resource-group' --query id -o tsv)
```

Create a Dockerfile to customize the Orbeon Forms Docker image:

```bash
cat > Dockerfile << EOF
FROM orbeon/orbeon-forms:2024.1-pe-wildfly
# TODO: customize your image here
EOF
```

Build the Docker image:

```bash
docker build --platform 'linux/amd64' -t 'orbeon-forms-custom:2024.1-pe-wildfly' .
```

Tag the Docker image with the full Azure Container Registry URL

```bash
docker tag 'orbeon-forms-custom:2024.1-pe-wildfly' "$CONTAINER_REGISTRY.azurecr.io/orbeon-forms-custom:2024.1-pe-wildfly"
```

Push the Docker image to the Azure Container Registry:

```bash
docker push "$CONTAINER_REGISTRY.azurecr.io/orbeon-forms-custom:2024.1-pe-wildfly"
```

## Kubernetes

Create an Azure Kubernetes Service (AKS) cluster:

```bash
az aks create \
  --name 'orbeon-forms-cluster' \
  --resource-group 'orbeon-forms-resource-group' \
  --node-count 1 \
  --network-plugin azure \
  --generate-ssh-keys
```

The AKS cluster name must be unique across Azure.

Retrieve AKS credentials, save them locally to `~/.kube/config`, and set the AKS cluster as the current context:

```bash
az aks get-credentials \
  --name 'orbeon-forms-cluster' \
  --resource-group 'orbeon-forms-resource-group' \
  --overwrite-existing
```

If you use a custom Docker image, you need to grant permission to the cluster to pull images from the Azure Container Registry.

```bash
# Retrieve the cluster's client ID
K8S_CLIENT_ID=$(az aks show \
                --name 'orbeon-forms-cluster' \
                --resource-group 'orbeon-forms-resource-group' \
                --query 'identityProfile.kubeletidentity.clientId' \
                -o tsv)

az role assignment create --assignee "$K8S_CLIENT_ID" --role AcrPull --scope "$CONTAINER_REGISTRY_ID"
```

Generate the storage account name/key secret file

```bash
cat > storage-secret.yaml << EOF
apiVersion: v1
kind: Secret
metadata:
  name: storage-secret
type: Opaque
data:
  azurestorageaccountname: $(echo -n "$STORAGE_ACCOUNT" | base64)
  azurestorageaccountkey: $(echo -n "$STORAGE_ACCOUNT_SECRET_KEY" | base64)
EOF
```

The Azure Storage account name and key need to be encoded in Base64.

Import the storage account name/key secret

```bash
kubectl apply -f storage-secret.yaml
```

Generate the persistence volume configuration file:

```bash
cat > orbeon-forms-pv.yaml << EOF
apiVersion: v1
kind: PersistentVolume
metadata:
  name: orbeon-forms-pv
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany
  storageClassName: azure-file
  azureFile:
    secretName: storage-secret
    shareName: orbeon-forms-share
    readOnly: false
EOF
```

Import the persistence volume configuration:

```bash
kubectl apply -f orbeon-forms-pv.yaml
```

Generate the persistence volume claim configuration file:

```bash
cat > orbeon-forms-pvc.yaml << EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: orbeon-forms-pvc
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: azure-file
  resources:
    requests:
     storage: 5Gi
EOF
```

Import the persistence volume claim configuration:

```bash
kubectl apply -f orbeon-forms-pvc.yaml
```

Depending on whether you use the default Orbeon Forms image or a custom one, you will need to set the image name as follows:

```bash
# Unmodified Orbeon Forms image
K8S_IMAGE='orbeon/orbeon-forms:2024.1-pe-wildfly'

# Custom Orbeon Forms image
K8S_IMAGE="$CONTAINER_REGISTRY.azurecr.io/orbeon-forms-custom:2024.1-pe-wildfly"
```

Generate the deployment configuration file:

```bash
cat > orbeon-forms-deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: orbeon-forms-deployment
  labels:
  app: orbeon-forms
spec:
  replicas: 1
  selector:
    matchLabels:
     app: orbeon-forms
  template:
    metadata:
      labels:
        app: orbeon-forms
    spec:
      containers:
      - name: orbeon-forms
        image: $K8S_IMAGE
        ports:
        - containerPort: 8443
        volumeMounts:
          - name: azure-volume
            mountPath: /opt/jboss/wildfly/standalone/deployments/orbeon.war/WEB-INF/resources/config/license.xml
            subPath: license.xml
          - name: azure-volume
            mountPath: /opt/jboss/wildfly/standalone/deployments/orbeon.war/WEB-INF/resources/config/form-builder-permissions.xml
            subPath: form-builder-permissions.xml
          - name: azure-volume
            mountPath: /opt/jboss/wildfly/standalone/deployments/orbeon.war/WEB-INF/resources/config/properties-local.xml
            subPath: properties-local.xml
          - name: azure-volume
            mountPath: /opt/jboss/wildfly/standalone/deployments/orbeon.war/WEB-INF/jboss-web.xml
            subPath: jboss-web.xml
          - name: azure-volume
            mountPath: /opt/jboss/wildfly/standalone/deployments/orbeon.war/WEB-INF/oidc.json
            subPath: oidc.json
          - name: azure-volume
            mountPath: /opt/jboss/wildfly/standalone/deployments/orbeon.war/WEB-INF/web.xml
            subPath: web.xml
          - name: azure-volume
            mountPath: /opt/jboss/wildfly/standalone/configuration/application.keystore
            subPath: application.keystore
          - name: azure-volume
            mountPath: /docker-entrypoint-wildfly.d/standalone.xml
            subPath: standalone.xml
      volumes:
        - name: azure-volume
          persistentVolumeClaim:
          claimName: orbeon-forms-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: orbeon-forms-service
spec:
  type: LoadBalancer
  selector:
    app: orbeon-forms
  ports:
    - protocol: TCP
      port: 443
      targetPort: 8443
EOF
```

Note that the `standalone.xml` file is mounted in the container as `docker-entrypoint-wildfly.d/standalone.xml`. This is because WildFly needs to move/rename that file, so it needs to be copied to instead of mounted directly in the `/opt/jboss/wildfly/standalone/configuration` configuration directory. See [this issue on GitHub](https://github.com/jboss-dockerfiles/wildfly/issues/70) for more information. The WildFly version of Orbeon Forms will copy any `standalone.xml` found in `docker-entrypoint-wildfly.d` to the WildFly configuration directory.

Import the deployment configuration:

```bash
kubectl apply -f orbeon-forms-deployment.yaml
```

You can display information about you Kubernetes contexts, cluster, pods, nodes, and service with the following commands:

```bash
# Contexts information
kubectl config get-contexts

# Cluster information
kubectl cluster-info

# Pods information
kubectl describe pods

# Nodes information
kubectl get nodes -o wide

# Service information
kubectl get service 'orbeon-forms-service'
```

Retrieve the cluster's external/public IP:

```bash
K8S_EXTERNAL_IP=$(kubectl get service 'orbeon-forms-service' --output jsonpath='{.status.loadBalancer.ingress[0].ip}')
```

Orbeon Forms will be available from the following URL:

```bash
K8S_APP_URL="https://$K8S_EXTERNAL_IP/orbeon"
```

Update the Entra ID redirect URIs with the actual Orbeon Forms URL:

```bash
az ad app update --id "$ENTRA_ID_APP_ID" --web-redirect-uris "$K8S_APP_URL/*"
```

Retrieve the Kubernetes pod name, which can be used to retrieve the logs (see below):

```bash
K8S_POD=$(kubectl get pod -o name | head -1)
```

Alternatively, you can use Azure Container Instances (ACI) for a simpler deployment, but with limited support for file mounts, port mappings, etc.

## Private network

The last step is to configure a private network to allow the Kubernetes cluster to access the PostgreSQL database server.

Create a private DNS zone:

```bash
az network private-dns zone create \
  --resource-group 'orbeon-forms-resource-group' \
  --name 'private.postgres.database.azure.com'
```

The network resource group we will use below is the Kubernetes node resource group. It follows the following format:

```bash
K8S_NODE_RESOURCE_GROUP="MC_${RESOURCE_GROUP}_${K8S_CLUSTER}_${AZURE_LOCATION}"
```

Retrieve the virtual network name:

```bash
NETWORK_VNET_NAME=$(az network vnet list \
                    --resource-group 'MC_orbeon-forms-resource-group_orbeon-forms-cluster_westus' \
                    --query '[0].name' \
                    --output tsv)
```

Retrieve the virtual network ID:

```bash
NETWORK_VNET_ID=$(az network vnet show \
                  --resource-group 'MC_orbeon-forms-resource-group_orbeon-forms-cluster_westus' \
                  --name "$NETWORK_VNET_NAME" \
                  --query 'id' \
                  --output tsv)
```

Link the private DNS zone to the Kubernetes cluster virtual network:

```bash
az network private-dns link vnet create \
  --resource-group 'orbeon-forms-resource-group' \
  --zone-name 'private.postgres.database.azure.com' \
  --name 'MyDNSLink' \
  --virtual-network "$NETWORK_VNET_ID" \
  --registration-enabled false
```

Retrieve the subnet name:

```bash
NETWORK_SUBNET_NAME=$(az network vnet subnet list \
                      --resource-group 'MC_orbeon-forms-resource-group_orbeon-forms-cluster_westus' \
                      --vnet-name "$NETWORK_VNET_NAME" \
                      --query '[0].name' \
                      --output tsv)
```

Retrieve the subnet ID:

```bash
NETWORK_SUBNET_ID=$(az network vnet subnet show \
                    --resource-group 'MC_orbeon-forms-resource-group_orbeon-forms-cluster_westus' \
                    --vnet-name "$NETWORK_VNET_NAME" \
                    --name "$NETWORK_SUBNET_NAME" \
                    --query 'id' \
                    --output tsv)
```

Retrieve the database server ID:

```bash
DATABASE_SERVER_ID=$(az postgres flexible-server show \
                     --resource-group 'orbeon-forms-resource-group' \
                     --name "$DATABASE_SERVER" \
                     --query 'id' \
                     --output tsv)
```

Create a private endpoint:

```bash
az network private-endpoint create \
  --resource-group 'orbeon-forms-resource-group' \
  --name 'postgres-pe' \
  --subnet "$NETWORK_SUBNET_ID" \
  --private-connection-resource-id "$DATABASE_SERVER_ID" \
  --connection-name 'postgres-connection' \
  --group-id 'postgresqlServer'
```

Retrieve the private IP:

```bash
NETWORK_PRIVATE_IP=$(az network private-endpoint show \
                     --resource-group 'orbeon-forms-resource-group' \
                     --name 'postgres-pe' \
                     --query 'customDnsConfigs[0].ipAddresses[0]' \
                     --output tsv)
```

Create a private DNS record:

```bash
az network private-dns record-set a add-record \
  --resource-group 'orbeon-forms-resource-group' \
  --zone-name 'private.postgres.database.azure.com' \
  --record-set-name "$DATABASE_SERVER" \
  --ipv4-address "$NETWORK_PRIVATE_IP"
```

The database server will now be reachable from the Kubernetes cluster as `$DATABASE_SERVER.private.postgres.database.azure.com`.

## Accessing the application and its logs

Orbeon Forms is now available from the following URL:

```bash
K8S_APP_URL="https://$K8S_EXTERNAL_IP/orbeon"
```

You can display and follow the Orbeon Forms logs using the following command:

```bash
kubectl logs $K8S_POD -f
```

## Limitations

Here is a list of limitations and possible improvements:

* Groups/roles are returned by Entra ID as IDs, not names.
* The example Bash script has been tested on macOS only, but should work on Linux and Windows, using the Windows Subsystem for Linux (WSL).
* No actual load balancing is configured in the Kubernetes cluster.
* The TLS/SSL configuration should be done at the Application Gateway Ingress Controller level and not at the WildFly level.
* Azure Resource Manager (ARM) templates could be used to automate the deployment.


# IIS

## Rationale

Orbeon Forms runs on a Java servlet container such as Tomcat. In some environments, particularly in organizations using Windows-based infrastructure, IIS (Internet Information Services) is the standard web server, and Windows Authentication (often backed by Active Directory) is the required authentication mechanism. In such cases, IIS can act as a reverse proxy in front of Tomcat, and forward the authenticated user's identity to Orbeon Forms. This page will walk you though the steps to set up IIS as a reverse proxy for Orbeon Forms running on Tomcat.

<figure><img src="/files/1bYi6SnfClukQFFLZJ6C" alt="IIS as a reverse proxy for Tomcat" width="420"><figcaption></figcaption></figure>

## Steps

### Download configuration files

Download the following files by clicking on each link, then clicking on the "Download raw file" button (the button is at the top right of the file, and its icon shows an arrow pointing down):

* [`user.aspx`](https://github.com/orbeon/orbeon-forms-doc/blob/master/installation/iis/user.aspx)
* [`WindowsAuthHeaderModule.cs`](https://github.com/orbeon/orbeon-forms-doc/blob/master/installation/iis/WindowsAuthHeaderModule.cs)
* [`web.config`](https://github.com/orbeon/orbeon-forms-doc/blob/master/installation/iis/web.config)
* [`user.jsp`](https://github.com/orbeon/orbeon-forms-doc/blob/master/installation/iis/user.jsp)
* [`properties-local.xml`](https://github.com/orbeon/orbeon-forms-doc/blob/master/installation/iis/properties-local.xml)

### Ensure IIS knows who the current user is

* If you've already set up a rewrite rule to forward requests to Tomcat, for now, disable that rule.
* Move the `user.aspx` in your site directory (the default is `C:\inetpub\wwwroot`).
* Access <http://localhost/user.aspx>. It should show something like *Page.User.Identity.Name: DOMAIN\Homer Simpson*.

### Pass the user information to Tomcat through a header

* In the directory where you put `user.aspx`, create a directory named `App_Code`. Move the `WindowsAuthHeaderModule.cs` inside that `App_Code` directory. This module sets the `HTTP_ORBEON_USERNAME` variable to the part of Windows username that follows the `\` character.
* Move the `web.config` in the same directory where you have `App_Code` and `user.aspx`.
* Move `user.jsp` in the directory `webapps\ROOT`, inside the Tomcat directory (for instance `C:\Program Files\Apache Software Foundation\Tomcat 10.1\webapps\ROOT`). This page will show the value of the `Orbeon-Username` header.
* Access <http://localhost/user.jsp>. It should show something like: *Forwarded User: Homer Simpson*.

### Configure Orbeon Forms to use the value of that header

* Move the `properties-local.xml` in Tomcat's `webapps\orbeon\WEB-INF\resources\config`.
* From Services, restart Tomcat.
* Access <http://localhost/orbeon/>. On the top-right of the page, you'll see a button with a user profile icon. Click on it. It should show under which username you are logged in, like *Logged in as Homer Simpson*.


# Tomcat

## Introduction

Installing Orbeon Forms on Tomcat is only one of the possibilities. You can also install Orbeon Forms on other Servlet containers. You can also use Docker containers. See also:

* Docker
  * [Blog post](https://www.orbeon.com/2024/10/orbeon-forms-docker-images)
  * [Detailed documentation](/installation/docker)
* Servlet containers
  * [WildFly](/installation/wildfly)
  * [WebLogic](/installation/weblogic)
  * [WebSphere](/installation/websphere)
  * [GlassFish](/installation/glassfish)

## Supported Tomcat versions

The following versions of Tomcat are supported:

| Orbeon Forms Version           | Tomcat Versions | Comment                                     |
| ------------------------------ | --------------- | ------------------------------------------- |
| 2025.1                         | 9, 10, 11       |                                             |
| 2024.1                         | 9, 10           |                                             |
| 2023.1                         | 8.5, 9, 10      |                                             |
| 2019.2, 2020.1, 2021.1, 2022.1 | 8.5, 9          |                                             |
| 2019.1                         | 8.5, 9          |                                             |
| 2018.2                         | 8.0, 8.5, 9     | Tomcat 8.0 not recommended, see below       |
| 2018.1                         | 8.0, 8.5, 9     | Tomcat 8.0 not recommended, see below       |
| 2017.2                         | 7, 8.0, 8.5, 9  | Tomcat 8.0 not recommended, see below       |
| 2017.1                         | 6, 7, 8.0, 8.5  | Tomcat 6 and 8.0 not recommended, see below |

Notes about older versions of Tomcat:

* The Apache Tomcat team has [announced](https://tomcat.apache.org/tomcat-60-eol.html) that support for Apache Tomcat 6.0.x ended on December 31, 2016.
  * Tomcat 6 isn't supported by Orbeon Forms starting with version 2017.2.
* The Apache Tomcat team has [announced](https://tomcat.apache.org/tomcat-80-eol.html) that support for Apache Tomcat 8.0.x ended on June 30, 2019.
  * Tomcat 8.0 isn't supported by Orbeon Forms starting with version 2019.1, but Tomcat 8.5 is supported.
* [TIFF support](/form-runner/features/tiff-production) is not available when using Tomcat 6 (see [#2717](https://github.com/orbeon/orbeon-forms/issues/2717)).

## Setup

We assume below that `TOMCAT_HOME` represents the location of your Tomcat installation.

If using Orbeon Forms PE, make sure the `license.xml` file is [in place](/installation#license-installation-orbeon-forms-pe-only).

### Quick setup

1. Create a new `TOMCAT_HOME/webapps/orbeon` directory.
2. Unzip `orbeon.war` in the `orbeon` directory you just created. So now you should have a directory `TOMCAT_HOME/webapps/orbeon/WEB-INF`.
3. You can now start Tomcat, and access `http://localhost:8080/orbeon/` to test your installation (replacing `localhost` and `8080` with the host name and port number of your Tomcat installation if different from the default).

## Optional steps

### Custom context within server.xml

Edit `TOMCAT_HOME/conf/server.xml`, and inside the `<Host>` create a `<Context>` as follows, changing the value of the `docBase` attribute as appropriate on your system. Make sure the `<Context>` element is *within* the `<Host>` element. Tomcat will not report an error if your context is misplaced in the file, and that will cause it to be ignored and, in particular, datasources might not be taken into account.

```xml
<Context
    path="/orbeon"
    docBase="TOMCAT_HOME/webapps/orbeon"
    reloadable="false"
    override="true"
    allowLinking="true"/>
```

## Compression

Unless you have another front-end performing stream compression, it is important to tell Tomcat to enable gzip compression on its connectors. This makes the amount of data transferred for JavaScript and CSS assets, in particular, significantly smaller. You can enable this on the Tomcat `<Connector>` elements in `server.xml` with the `compression="on"` attribute. For example:

```xml
<Connector 
    port="8080" 
    protocol="HTTP/1.1"
    connectionTimeout="20000"
    URIEncoding="UTF-8"
    compression="on"
/>
```

You can check that compression is working in your browser's Dev Tools' Network tab, where the two sizes indicate the compressed size and the uncompressed size:

![Gzip compression sizes](/files/Ck8ioKFCODTIkJ9X4Hlk)

Response headers will also include a `Content-Encoding: gzip` response header on pages and most text assets.

## Datasource setup

If you have a JDBC datasource, add it **inside** the `<Context>`, as in the following example:

```xml
<Context
    path="/orbeon"
    docBase="/path/to/orbeon-war"
    reloadable="false"
    override="true"
    allowLinking="true">
    <Resource 
        name="jdbc/mysql"
        driverClassName="com.mysql.jdbc.Driver"
        
        auth="Container" 
        type="javax.sql.DataSource"
        
        initialSize="3" 
        maxActive="10" 
        maxIdle="10" 
        maxWait="30000"
        
        poolPreparedStatements="true"
        
        testOnBorrow="true"
        validationQuery="select 1"
        
        username="orbeon"
        password="password"
        url="jdbc:mysql://localhost:3306/orbeon?useUnicode=true&amp;characterEncoding=UTF8"/>
</Context>
```

### URIEncoding

We recommend you add the `URIEncoding="UTF-8"` attribute on the [`<Connector>`](http://tomcat.apache.org/tomcat-7.0-doc/config/http.html) element, in your `server.xml`, as [recommended in the Tomcat FAQ](https://cwiki.apache.org/confluence/display/TOMCAT/Character+Encoding). This will ensure that all characters get properly decoded on the URL, which is especially important if you're using non-ASCII characters in the app or form name in Form Builder.

### Form Runner authentication

To setup Form Runner authentication:

1. Open `TOMCAT_HOME/webapps/orbeon/WEB-INF/web.xml` and uncomment the `security-constraint`, `login-config` and `security-role` declarations at the end of the file.
2. Open `TOMCAT_HOME/conf/server.xml` and make sure there is a `<Realm>` enabled. For example, by default with Tomcat 7:

   ```xml
   <Realm className="org.apache.catalina.realm.LockOutRealm">
       <Realm
           className="org.apache.catalina.realm.UserDatabaseRealm"
           resourceName="UserDatabase"/>
   </Realm>
   ```
3. Edit `TOMCAT_HOME/conf/tomcat-users.xml` and replace the content of the file with:

   ```xml
   <tomcat-users>
       <user
           username="orbeon-user"
           password="Secret, change me!"
           roles="orbeon-user"/>
       <user
           username="orbeon-admin"
           password="Secret, change me!"
           roles="orbeon-user,orbeon-admin"/>
   </tomcat-users>
   ```
4. Enumerate the roles in the following property:

   ```xml
   <property
       as="xs:string"
       name="oxf.fr.authentication.container.roles"
       value="orbeon-user orbeon-admin"/>
   ```

### BASIC or DIGEST authentication

For BASIC or DIGEST authentication, add the following `<Valve>` element within the `<Context>` element corresponding to the Orbeon Forms web application in Tomcat's configuration. Remove this `<Valve>` element if switching to FORM authentication, as leaving it will enforce BASIC or DIGEST authentication regardless of the configuration in `web.xml`.

```xml
<Valve
    className="org.apache.catalina.authenticator.BasicAuthenticator"
    changeSessionIdOnAuthentication="false"/>
```


# WildFly

## Introduction

Installing Orbeon Forms on WildFly is only one of the possibilities. You can also install Orbeon Forms on other Servlet containers. You can also use Docker containers. See also:

* Docker
  * [Blog post](https://www.orbeon.com/2024/10/orbeon-forms-docker-images)
  * [Detailed documentation](/installation/docker)
* Servlet containers
  * [Tomcat](/installation/tomcat)
  * [WebLogic](/installation/weblogic)
  * [WebSphere](/installation/websphere)
  * [GlassFish](/installation/glassfish)

## Versions

WildFly was formerly known as JBoss.

The following instructions should work with recent versions of WildFly.

## Deploy Orbeon Forms

To install Orbeon Forms:

1. For Orbeon Forms PE only, either:
   * place your license file under `~/.orbeon/license.xml` (see [License installation](/installation#license-installation-orbeon-forms-pe-only)),
   * or add your `license.xml` to the `orbeon.war` under `WEB-INF/resources/config/license.xml`
2. Start a standalone server with `bin/standalone.sh`
3. Move the `orbeon.war` file into the WildFly `standalone/deployments` folder
4. Check whether the deployment was successful by watching `standalone/log/server.log`

## Creating an jboss-deployment-structure.xml

With Orbeon Forms 2018.2.2 and earlier, with some versions of WildFly, a `jboss-deployment-structure.xml` under the Orbeon Forms WAR's `WEB-INF` directory is needed:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.1">
    <deployment>
        <dependencies>
            <system export="true">
                <paths>
                    <path name="org/w3c/dom/css"/>             
                </paths>
            </system>
        </dependencies>
    </deployment>
</jboss-deployment-structure>
```

Orbeon Forms 2018.2.3 and later, as well as Orbeon Forms 2019.1, already include this descriptor.

## Setup a JDBC datasource

To setup a datasource, if you'd like Orbeon Forms to connect to your relational database, do the following:

1. Setup Orbeon Forms to use a WildFly datasource (configured in the following steps):
   1. Set the `oxf.fr.persistence.provider.*.*.*` property in your `properties-local.xml`
      1. If you already created a `WEB-INF/resources/config/properties-local.xml` unzip it and add the property per the example below. Otherwise create that file with the following content:

         ```xml
         <properties xmlns:xs="http://www.w3.org/2001/XMLSchema"
                     xmlns:oxf="http://www.orbeon.com/oxf/processors">
             <property as="xs:string"
                       name="oxf.fr.persistence.provider.*.*.*"
                       value="oracle"/>
         </properties>
         ```
      2. Change the value of the property according to the database you're using, setting it either to `oracle`, `mysql`, `sqlserver`, `postgresql`, or `db2`.
      3. Update `WEB-INF/resources/config/properties-local.xml` inside the `orbeon.war` with the version you edited.
   2. Update the `web.xml`
      1. Unzip the `WEB-INF/web.xml` inside the `orbeon.war`.
      2. Editing `WEB-INF/web.xml`, towards the end of the file, uncomment the following:

         ```xml
         <resource-ref>
             <description>DataSource</description>
             <res-ref-name>jdbc/oracle</res-ref-name>
             <res-type>javax.sql.DataSource</res-type>
             <res-auth>Container</res-auth>
         </resource-ref>
         ```
      3. Inside `<resource-ref>`, replace `oracle` by the name of your database.
      4. Update `WEB-INF/web.xml` inside the `orbeon.war` with the version you edited.
   3. Update the `jboss-web.xml`
      1. Unzip the `WEB-INF/jboss-web.xml` inside the `orbeon.war`.
      2. Editing `WEB-INF/jboss-web.xml`, uncomment the following:

         ```xml
         <resource-ref>
             <res-ref-name>jdbc/oracle</res-ref-name>
             <jndi-name>java:jboss/datasources/oracle</jndi-name>
         </resource-ref>
         ```
      3. Change the `<res-ref-name>` to match what the `<res-ref-name> in your` web.xml\`.
      4. In `<jndi-name>java:jboss/datasources/oracle</jndi-name>`, replace `oracle` by the database name you used in `<res-ref-name>`.
      5. Update `WEB-INF/jboss-web.xml` inside the `orbeon.war` with the version you edited.
2. In WildFly, install the JDBC driver:
   1. Download the MySQL JDBC driver, say `oracle-driver.jar`, and place it in the `standalone/deployments` directory.
   2. Start the server, and check you see the message `Deployed "oracle-driver.jar" (runtime-name : "oracle-driver.jar")`.
3. In WildFly, define the datasource:
   1. Editing `standalone/configuration/standalone.xml`, inside the `<datasources>` add the following:

      ```xml
      <datasource jndi-name="java:jboss/datasources/oracle" pool-name="oracle" enabled="true">
          <connection-url>…</connection-url>
          <driver>…</driver>
          <security>
              <user-name>…</user-name>
              <password>…</password>
          </security>
      </datasource>
      ```
   2. In the `jndi-name` attribute, replace `oracle` by the name of your database. The value of this attribute must match the value you set earlier inside `<jndi-name>` when editing the `jboss-web.xml`.
   3. In `<connection-url>`, put the JDBC URL to your database.
   4. In `<driver>`, put the "runtime-name" of your driver as it shows in the log (it was `oracle-driver.jar` in our example above).
   5. In `<security>`, fill in the proper username and password.

Finally, you might want to double check the configuration you just did, ensuring names match across files, per the following diagram.

![Configuration files that need to be in sync](/files/-LEkBspfh7A99ktDCv2T)


# WebSphere

## Introduction

Installing Orbeon Forms on WebSphere is only one of the possibilities. You can also install Orbeon Forms on other Servlet containers. You can also use Docker containers. See also:

* Docker
  * [Blog post](https://www.orbeon.com/2024/10/orbeon-forms-docker-images)
  * [Detailed documentation](/installation/docker)
* Servlet containers
  * [Tomcat](/installation/tomcat)
  * [WildFly](/installation/wildfly)
  * [WebLogic](/installation/weblogic)
  * [GlassFish](/installation/glassfish)

## Installation notes

1. **Installing WebSphere** – Download [WebSphere 8.5 Liberty Profile](https://developer.ibm.com/wasdev/downloads/liberty-profile-using-non-eclipse-environments/). Run the command line mentioned on that page, e.g. `java -jar wlp-developers-runtime-8.5.5.0.jar`. In what follows, we'll refer to the directory where you installed WebSphere Liberty Profile as `WLP`.
2. **Running WebSphere** – Run the server: `cd WLP/bin ; ./server run`. This will create the directory structure under `WLP/usr/servers/defaultServer`.
3. **Deploying Orbeon Forms** – In `WLP/usr/servers/defaultServer/apps` create a `war` directory, and inside it an `orbeon` directory. Uncompress the Orbeon Forms war into that `orbeon` directory. Open `WLP/usr/servers/defaultServer/server.xml` in an editor, inside the `<server>` root element, add the following two lines. The first lines declared the Orbeon Form app. The second disables automatic application redeployment when files in Orbeon Forms war file change. You need this as by default Form Runner uses the embedded eXist database, which writes to `WEB-INF/exist-data`, inside the Orbeon Forms `war`, which would trigger the app to restart as data is written to disk.

   ```xml
   <application id="orbeon" name="orbeon" location="war/orbeon" type="war"/>
   <applicationMonitor updateTrigger="disabled"/>
   ```
4. **Testing your installation** – Check the console: you should see the that Orbeon Forms was deployed successfully. Then access `http://localhost:9080/orbeon/home/`, and you should see the Orbeon Forms Home page. If you start WebSphere from the WLP/bin directory, as mentioned on step 2, you will find the Orbeon Forms log file in `WLP/usr/servers/logs/orbeon.log`.

\[SINCE Orbeon Forms 4.3]

To setup a JDBC, for instance here for Oracle:

1. **Install the database driver** – Create a directory `WLS/usr/servers/defaultServer/lib`, and inside it place the database driver jar file, for instance `ojdbc6_g.jar`. On WebSphere, Orbeon Forms requires a JDBC 4 driver (e.g. for Oracle , use `ojdbc6_g.jar` or `ojdbc6.jar` but not `ojdbc5_g.jar` or `ojdbc5.jar`).
2. **Setup a datasource in WebSphere** – Open your `WLP/usr/servers/defaultServer/server.xml` in an editor. Your server.xml should look like the one below. The `jdbc-4.0` feature in included, a top level library is declared pointing to the driver jar (in this case `ojdbc6_g.jar`), a datasource is defined, and the JNDI name set to `jdbc/oracle`, and both the data source and the application point to the same top level library, which is particularly important so WebSphere loads the driver classes with a single shared class loader.

   ```xml
   <server description="new server">

       <featureManager>
           <feature>jsp-2.2</feature>
           <feature>jdbc-4.0</feature>
       </featureManager>

       <httpEndpoint id="defaultHttpEndpoint"
                     host="localhost"
                     httpPort="9080"
                     httpsPort="9443" />

       <library id="oracle-lib">
           <fileset dir="lib" includes="ojdbc6_g.jar"/>
       </library>

       <dataSource id="oracle-ds" jndiName="jdbc/oracle" type="javax.sql.DataSource">
           <jdbcDriver libraryRef="oracle-lib" id="oracle-driver"/>
           <connectionManager numConnectionsPerThreadLocal="10" id="ConnectionManager" minPoolSize="1"/>
           <properties.oracle user="orbeon" password="password"
                              url="jdbc:oracle:thin:@//localhost:1521/orbeon"/>
       </dataSource>

       <applicationMonitor updateTrigger="disabled"/>

       <application id="orbeon" name="orbeon" location="war/orbeon" type="war">
           <classloader delegation="parentLast" commonLibraryRef="oracle-lib"/>
       </application>

   </server>
   ```
3. **Setup the datasource the Orbeon Forms web app** – In `WLP/usr/servers/defaultServer/apps/war/orbeon/WEB-INF` create a file `ibm-web-bnd.xml` with the following content:

   ```xml
   <web-bnd
           xmlns="http://websphere.ibm.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee
           http://websphere.ibm.com/xml/ns/javaee/ibm-web-bnd_1_0.xsd"
           version="1.0">
       <virtual-host name="default_host"/>
       <resource-ref name="jdbc/oracle" binding-name="jdbc/oracle"/>
   </web-bnd>
   ```

   Then, edit the `web.xml` in the same directory, and uncomment the `<resource-ref>`, which should look as follows:

   ```xml
   <resource-ref>
       <description>DataSource</description>
       <res-ref-name>jdbc/oracle</res-ref-name>
       <res-type>javax.sql.DataSource</res-type>
       <res-auth>Container</res-auth>
   </resource-ref>
   ```
4. **Setup Form Runner** – If you're doing this setup so Form Runner (i.e. the forms you create with Form Builder) stores data in a relational database, then you also need to add the following property to instruct Form Runner to use the appropriate persistence implementation. The value of the property will typically be `oracle`, `mysql`, or `db2`.

   ```xml
   <property as="xs:string" name="oxf.fr.persistence.provider.*.*.*" value="oracle">
   ```


# WebLogic

## Introduction

Installing Orbeon Forms on WebLogic is only one of the possibilities. You can also install Orbeon Forms on other Servlet containers. You can also use Docker containers. See also:

* Docker
  * [Blog post](https://www.orbeon.com/2024/10/orbeon-forms-docker-images)
  * [Detailed documentation](/installation/docker)
* Servlet containers
  * [Tomcat](/installation/tomcat)
  * [WildFly](/installation/wildfly)
  * [WebSphere](/installation/websphere)
  * [GlassFish](/installation/glassfish)

## Installation notes

A version of the ANTLR library that ships with WebLogic 11g conflicts with the version required by Orbeon Forms. To run Orbeon Forms on WebLogic 10/11g, you need to instruct WebLogic to let Orbeon Forms use the version of ANTLR that ships with Orbeon Forms. You can do this in the WebLogic EAR descriptor, which means you need to encapsulate Orbeon Forms in an EAR before you deploy it:

1. Create the following directory structure in a temporary directory:

   ```
   orbeon-ear
       META-INF
           application.xml
           weblogic-application.xml
       orbeon
   ```

   Populate `application.xml` with:

   ```xml
   <?xml version="1.0"?>
   <j2ee:application xmlns:j2ee="http://java.sun.com/xml/ns/j2ee">
       <j2ee:display-name>Orbeon Forms</j2ee:display-name>
       <j2ee:module>
           <j2ee:web>
               <j2ee:web-uri>orbeon</j2ee:web-uri>
               <j2ee:context-root>/orbeon</j2ee:context-root>
           </j2ee:web>
       </j2ee:module>
   </j2ee:application>
   ```

   Populate `weblogic-application.xml` with:

   ```xml
   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE weblogic-application PUBLIC
       "-//BEA Systems, Inc.//DTD WebLogic Application 8.1.0//EN"
       "http://www.bea.com/servers/wls810/dtd/weblogic-application_2_0.dtd">
   <weblogic-application>
       <prefer-application-packages>
           <package-name>antlr.*</package-name>
           <package-name>org.apache.commons.lang.*</package-name>
           <package-name>org.apache.commons.fileupload.*</package-name>
           <package-name>org.apache.lucene.*</package-name>
       </prefer-application-packages>
   </weblogic-application>
   ```
2. Uncompress the `orbeon.war` into the `orbeon-ear/orbeon` directory you created in step 1. After this, you should have a directory `orbeon-ear/orbeon/WEB-INF`.
3. Deploy the `orbeon-ear` directory. If you are running WebLogic in development mode, you can move it to `user_projects/domains/base_domain/autodeploy`.
4. Optionally, you might want to change where the `orbeon.log` is stored. You define the location of the file in `WEB-INF/resources/config/log4j.xml`, in the `SingleFileAppender`. By default the location of the file is defined as `../logs/orbeon.log`. If you start WebLogic with `user_projects/domains/base_domain/startWebLogic.sh`, the log will be located in `user_projects/domains/logs/orbeon.log`.


# GlassFish

## Introduction

Installing Orbeon Forms on GlassFish is only one of the possibilities. You can also install Orbeon Forms on other Servlet containers. You can also use Docker containers. See also:

* Docker
  * [Blog post](https://www.orbeon.com/2024/10/orbeon-forms-docker-images)
  * [Detailed documentation](/installation/docker)
* Servlet containers
  * [Tomcat](/installation/tomcat)
  * [WildFly](/installation/wildfly)
  * [WebLogic](/installation/weblogic)
  * [WebSphere](/installation/websphere)

## Installation notes

On GlassFish, you need to do the following setup to avoid a `java.security.UnrecoverableKeyException` with the message *Password must not be null*:

1. Edit your domain's `domain.xml` (e.g. in `domains/domain1/config/domain.xml`).
2. Search for the section of the file that contains `<jvm-options>` elements, and there, add: `<jvm-options>-Djavax.net.ssl.keyStorePassword=changeit</jvm-options>`. If you changed your Glassfish [master password](http://docs.oracle.com/cd/E18930_01/html/821-2435/ghgrp.html), set this property to your new password.

The above steps have been verified on GlassFish 3.1 and 4.1.1.


# Caches

## In-memory caches

Orbeon Forms uses in-memory caches for data structures associated with forms and form state:

* Compiled forms
  * This is the result of compiling the form definition. It is a data structure shared amongst all users of a form.
  * The size of this cache is controlled by the `oxf.xforms.cache.static-state.size` property and defaults to 50.
  * If a compiled form is evicted from the cache, it is serialized to the `xforms.state` store, from which it can be reloaded and reconstructed when needed.
* Form sessions
  * A form session represents one interactive session of a specific user with a specific form. This is *not* shared between users of a given form.
  * The size of this cache is controlled by the `oxf.xforms.cache.documents.size` property and defaults to 50.
  * For testing purposes, this can be turned off with the `oxf.xforms.cache.document` property, but this is not recommended.
  * If a form session is evicted from the cache, it is stored in the `xforms.state` store, from which it can be reloaded and reconstructed when needed.
  * Whe a user session expires (that is, an application server session expires), the corresponding form sessions are evicted from the cache.

The default size of `oxf.xforms.cache.static-state.size` or `oxf.xforms.cache.documents.size` is fairly small. You definitely will want to consider what size to use for your deployment. If possible, set:

* `oxf.xforms.cache.static-state.size` to the number of distinct published form definitions you have in production (or slightly larger, for example 10-25% larger).
* `oxf.xforms.cache.documents.size` to the number of concurrent users you expect to have (or slightly larger, for example 10-25% larger).

**IMPORTANT: Performance will suffer if any of the data in the in-memory caches needs to be reconstructed from the `xforms.state` store.**

However, there is a trade-off between performance and memory usage. The larger the cache, the more memory is used.

For more information on these configuration properties, see [Configuring state handling](/configuration/advanced/state-handling#configuring-state-handling).

## Other caches used by Orbeon Forms

Orbeon Forms also uses the following caches that use an underlying cache implementation and configuration, such as Infinispan or through the JCache API (see [Supported cache implementations](#supported-cache-implementations) below):

* Form state
  * This includes form data and other form state as the user interacts with a form.
  * This cache acts as a store: some information cannot be reconstructed from other sources.
  * This is configured by the `xforms.state` cache.
  * This must be replicated when [replication](/installation/replication) is enabled.
* Mapping of some web resources
  * This is configured by the `xforms.resources` cache.
  * This cache acts as a store: some information cannot be reconstructed from other sources.
  * This must be replicated when [replication](/installation/replication) is enabled.
* Caching of form definition metadata in the persistence layer
  * [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)
  * This is configured by the `form-runner.persistence.form-definition` cache.
  * This is a true cache, which doesn't need to be replicated.
  * See [Persistence layer caching of form definition metadata](#persistence-layer-caching-of-form-definition-metadata) below.

## Supported cache implementations

Orbeon Forms has traditionally used Ehcache 2.x. In recent versions, JCache API (JSR-107) support has been added.

| Orbeon Forms Version | Infinispan support | Ehcache 2.x Support | JCache Support | Default     |
| -------------------- | ------------------ | ------------------- | -------------- | ----------- |
| 2025.1               | Yes                | Yes (deprecated)    | Yes            | Infinispan  |
| 2023.1 and newer     | No                 | Yes                 | Yes            | Ehcache 2.x |
| 2022.1.5 and newer   | No                 | Yes                 | Yes            | Ehcache 2.x |
| 2021.1.9 and newer   | No                 | Yes                 | Yes            | Ehcache 2.x |
| 2022.1.4 and earlier | No                 | Yes                 | No             | Ehcache 2.x |
| 2021.1.8 and earlier | No                 | Yes                 | No             | Ehcache 2.x |
| 2020.1.x and earlier | No                 | Yes                 | No             | Ehcache 2.x |

## Using the default configuration

### Orbeon Forms 2025.1 and newer: Infinispan configuration

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)

By default, the following properties are set and enable Infinispan through JCache:

```xml
<property as="xs:string"  name="oxf.xforms.cache.provider" value="infinispan"/>
```

An internal `infinispan.xml` configuration file is used. Here are the default configuration files:

* [Orbeon Forms 2025.1 Infinispan configuration file](https://github.com/orbeon/orbeon-forms/blob/tag-release-2025.1-ce/orbeon-war/js/src/test/resources/resources/config-replication/infinispan.xml)

You can update that configuration by placing your own `infinispan.xml` file in the `WEB-INF/resources/config` directory of your web app. This is rarely needed, except for enabling [replication](/installation/replication).

As of Orbeon Forms 2025.1, Ehcache 2.x support is still included in the Orbeon Forms web app for backward compatibility, but it is deprecated and support for it will be removed in a future release. We recommend migrating to a JCache provider such as Infinispan.

### Orbeon Forms 2024.1 and earlier: Ehcache 2.x configuration

\[UNTIL Orbeon Forms 2024.1.x]

By default, the following property is set and enables Ehcache 2.x:

```xml
<property
    as="xs:string"
    name="oxf.xforms.cache.provider"
    value="ehcache2"/>
```

An internal `ehcache.xml` configuration file is used. Here are the default configuration files:

* [as of Orbeon Forms 2022.1.4](https://github.com/orbeon/orbeon-forms/blob/0f5bcf02178009c8a33868227c9b1d03e019e80d/src/main/resources/config/ehcache.xml)
* [as of Orbeon Forms 2023.1](https://github.com/orbeon/orbeon-forms/blob/2023.1-ce/src/main/resources/config/ehcache.xml)

You can update that configuration by placing your own `ehcache.xml` file in the `WEB-INF/resources/config` directory of your web app. This is rarely needed, except for enabling [replication](/installation/replication).

## Enabling JCache providers

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

With the Orbeon Forms versions that support it, you can enable JCache support with the following:

```xml
<property
    as="xs:string"
    name="oxf.xforms.cache.provider"
    value="jcache"/>
```

When this is set to `jcache`, Orbeon Forms uses the *default caching provider* as provided by the JCache API. This means that you must provide, in the Orbeon Forms web app, your own JCache-compatible implementation. Orbeon Forms comes by default with Ehcache 3.x as implementation, but you do not have to use Ehcache 3.x.

You can further specify the configuration to use with the following properties:

```xml
<property as="xs:string"  name="oxf.xforms.cache.jcache.classname" value=""/>
<property as="xs:string"  name="oxf.xforms.cache.jcache.resource"  value=""/>
<property as="xs:string"  name="oxf.xforms.cache.jcache.uri"       value=""/>
```

* `oxf.xforms.cache.jcache.classname` specifies a regular expression that must match the provider fully-qualified class name. This is useful in case there is more than one cache provider in the classpath and you need to select a specific one.
* `oxf.xforms.cache.jcache.resource` specifies the path to a webapp resource. The configuration can be stored under the web app's WEB-INF or a JAR file included in the webapp.
* `oxf.xforms.cache.jcache.uri` specifies the URI to a configuration.

Example:

```xml
<property
    as="xs:string"
    name="oxf.xforms.cache.jcache.classname"                
    value=".*EhcacheCachingProvider"/>
```

The `resource` property is checked first, then the `uri` property. A blank property is ignored.

For example, to point to the built-in Ehcache 3.x configuration, set:

```xml
<property 
    as="xs:string"  
    name="oxf.xforms.cache.jcache.resource" 
    value="/ehcache3.xml"/>
```

*NOTE: The Ehcache 3 JAR file is already included in Orbeon Forms. Java packages are different, and therefore the Ehcache 2.x and Ehcache 3.x JAR files do not conflict.*

If you are using a different JCache-compatible implementation:

* Different values for the `oxf.xforms.cache.jcache.resource` and/or `oxf.xforms.cache.jcache.uri` properties might be needed.
* Add the property mentioned below to disable caching of form definitions, as we don't want cached form definitions to be replicated.

## Persistence layer caching of form definition metadata

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

When you call the [Form Runner persistence API](/form-runner/api/persistence), Form Runner can cache accesses to form definition metadata. This is done to avoid having to load the form definition from the database on each persistence API call. This cache is not replicated, and is local to each Orbeon Forms instance.

This cache is enabled by default. To disable it, set the following property:

```xml
<property
    as="xs:boolean"
    name="oxf.fr.persistence.form-definition-cache.enable"
    value="false"/>
```

\[SINCE Orbeon Forms 2026.1] The property is now named `oxf.fr.persistence.proxy.form-definition-cache.enable`.

One reason to disable this cache might be if you perform accesses to the database from outside of Orbeon Forms, and you want to make sure that you always get the latest form definition. However, we recommend using the [Form Runner persistence API](/form-runner/api/persistence) instead.

This property enables access to cache with the following name, configured either in `ehcache.xml` or in the JCache configuration:

* `form-runner.persistence.form-definition`

## See also

* [State Handling](/configuration/advanced/state-handling)
* [Installation](/installation)
* [Replication](/installation/replication)
* Blog post: [High-Availability Thanks to State Replication](https://blog.orbeon.com/2018/03/high-availability-thanks-to-state.html)
* [Clustering and High Availability](/configuration/advanced/clustering)
* [Form Runner persistence API](/form-runner/api/persistence)


# Replication

## Availability

This is an [Orbeon Forms PE](https://www.orbeon.com/download) feature, and it is available since Orbeon Forms 2017.2.

We also have reports of this feature working with OpenLiberty and Hazelcast session replication.

## Purpose

The purpose of replication is to provide high-availability of Orbeon Forms with as little disruption as possible to users currently filling out forms. This is achieved by replicating state between servers.

Consider a simple scenario of load balancing with two servers, with sticky sessions (that is, a given user's requests always reach the same server). If one of the servers fails, new users will be assigned to the other server. So the system remains operational from that point of view. However, users with active sessions will have their current work lost, as the content will still in the failed server-memory.

Replication changes that by replicating state to one or more additional servers. So if a server goes down, the load balancer can redirect users with active sessions to other servers, and because state was replicated there, users can continue their work.

## Architecture

Orbeon Forms achieves replication by enabling the replication of servlet sessions and of caches. All current state in memory, whether in the session or relevant caches, is replicated so that work can be resumed on replica servers when needed.

Sessions are still sticky for performance reasons. Because Orbeon Forms stores a lot of information in memory, and there are data structures associated with that information, there is a cost to recreate all necessary data structures at each request. Therefore, requests for a given user must constantly reach the same server. However, if a server fails, then there is a one-time cost to recreating data structures on the new server for the given user (in fact, for a given form in use by that user).

A load balancer is required. It is in charge of proxying client requests to specific servers, detect which servers might have failed or are being brought back, and ensuring session affinity.

<figure><img src="/files/Kr6SG0h4paDySbiIaiTj" alt="Replication architecture" width="500"><figcaption><p>Replication architecture</p></figcaption></figure>

## Configuration

### Which configuration to use

When to use the Infinispan configuration:

* Infinispan is better suited for traditional on-premises deployments where servers are on the same network and can use multicast for automatic peer discovery.
* Your servers can communicate via multicast (IP multicast address and port).
* You're using Orbeon Forms 2025.1 or newer.

When to use the Redis configuration:

* You're deploying in cloud environments where multicast is typically not available.
* You have Redis available as a managed service (common in cloud platforms).
* You need a simpler configuration with fewer components to manage.
* You're using Orbeon Forms 2024.1.2 or newer.

When to use the Ehcache configuration (deprecated as of Orbeon Forms 2025.1):

* Ehcache is better suited for traditional on-premises deployments where servers are on the same network and can use multicast for automatic peer discovery.
* Your servers can communicate via multicast (IP multicast address and port).
* You are using version of Orbeon Forms before 2024.1.2.

### Infinispan configuration

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)

See [Infinispan configuration file for replication](https://github.com/orbeon/orbeon-forms/blob/master/orbeon-war/js/src/test/resources/resources/config-replication/infinispan.xml).

Unlike with Ehcache 2.x, which synchronizes disk stores as well as in-memory caches, with Infinispan only in-memory caches are synchronized. There are a few ways to deal with this with Infinispan:

1. Use only in-memory caches, and make them larger. This can work if you have enough RAM.
2. Use disk stores, but accept that in case of failure of a server, some state might be lost.
3. Use shared disk stores, which require a shared filesystem between servers.

The default Orbeon Forms `infinispan.xml` configuration uses in-memory caches only.

### Redis configuration

[\[SINCE Orbeon Forms 2024.1.2\]](/release-notes/orbeon-forms-2024.1.2)

This setup is best for cloud deployments where instances of Orbeon Forms typically can't use multicast for discovery, and Redis is provided as a service.

1. Extract `orbeon-redis-jars.zip` which is part of the Orbeon Forms distribution.
2. Copy the jar files it contains to Tomcat's `lib` directory.
3. Create `redisson-jcache.yaml` in Tomcat's `conf` directory, changing `redis.example.com` to point to your Redis server:

   ```yaml
   codec: !<org.redisson.codec.FuryCodec> {}
   singleServerConfig:
       address: "redis://redis.example.com:6379"
   ```
4. In `properties-local.xml`, add the following (modifying the path to `redisson-jcache.yaml` as needed):

   ```xml
   <property as="xs:string"  name="oxf.xforms.store.provider"                          value="jcache"/>
   <property as="xs:string"  name="oxf.xforms.store.jcache.classname"                  value="org.redisson.jcache.JCachingProvider"/>
   <property as="xs:string"  name="oxf.xforms.store.jcache.uri"                        value="file:/usr/local/tomcat/conf/redisson-jcache.yaml"/>
   ```
5. Inside the `<Context>` for Orbeon Forms (typically found in the Tomcat `server.xml` or an `orbeon.xml`), add:

   ```xml
   <Manager
       className="org.redisson.tomcat.RedissonSessionManager"
       configPath="${catalina.base}/conf/redisson-jcache.yaml"
       readMode="REDIS" updateMode="DEFAULT"
       broadcastSessionUpdates="false"
       broadcastSessionEvents="false"
       keyPrefix=""/>
   <Valve
       className="org.apache.catalina.authenticator.BasicAuthenticator"
       changeSessionIdOnAuthentication="false"/>
   ```

### Ehcache configuration

\[DEPRECATED AS OF Orbeon Forms 2025.1]

Orbeon Forms has a single property enabling replication. By default, it is set to `false`, because there is a cost to serializing the state of forms after each update in memory.

```xml
<property
    as="xs:boolean"
    name="oxf.xforms.replication"
    value="true"/>
```

The Orbeon Forms `ehcache.xml` must be modified to include replication settings, which are turned off by default. This is similar to Tomcat session replication. To modify this file, extract it from the `WEB-INF/lib/orbeon-core.jar`, and copy it in the `WEB-INF/resources/config` directory. You can then modify the `ehcache.xml` in that directory, and your updated version will take precedence over the built-in version of that file found inside `orbeon-core.jar`.

*NOTE: There isn't as single set of settings to replicate the Tomcat servlet session and Ehcache, as the two products use different libraries for replication. But the idea is that both configuration should behave as closely as possible from each other.*

The keys to this configuration are:

* for relevant caches
  * `RMICacheReplicatorFactory` as `<cacheEventListenerFactory>`
  * `RMIBootstrapCacheLoaderFactory` as `<bootstrapCacheLoaderFactory>`
* global
  * `RMICacheManagerPeerProviderFactory` as `<cacheManagerPeerProviderFactory>`
  * `RMICacheManagerPeerListenerFactory` as `<cacheManagerPeerListenerFactory>`

Here is an example configuration:

```xml
<ehcache updateCheck="false" monitoring="off" dynamicConfig="true">

    <!-- Where the disk store will go -->
    <diskStore path="java.io.tmpdir/orbeon/cache"/>

    <!-- Default cache (not used by Orbeon) -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            diskSpoolBufferSizeMB="30"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            statistics="false"/>

    <!-- XForms state store configuration. Only modify if you know what you are doing! -->
    <!-- NOTE: We set this as a disk cache, but follow the Ehcache doc and set maxElementsInMemory to 1 instead of 0. -->
    <cache name="xforms.state"
           maxElementsInMemory="1"
           memoryStoreEvictionPolicy="LFU"
           overflowToDisk="true"
           diskSpoolBufferSizeMB="10"
           eternal="false"
           timeToLiveSeconds="0"
           timeToIdleSeconds="18000"
           diskPersistent="false"
           maxElementsOnDisk="0"
           diskExpiryThreadIntervalSeconds="120">

        <cacheEventListenerFactory
            class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
            properties="replicateAsynchronously=false"/>

        <bootstrapCacheLoaderFactory
            class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
            properties="bootstrapAsynchronously=false"/>
    </cache>

    <!-- XForms resources. Only modify if you know what you are doing! -->
    <cache name="xforms.resources"
           maxElementsInMemory="200"
           memoryStoreEvictionPolicy="LFU"
           overflowToDisk="true"
           diskSpoolBufferSizeMB="1"
           eternal="true"
           timeToLiveSeconds="0"
           timeToIdleSeconds="0"
           diskPersistent="false"
           maxElementsOnDisk="0"
           diskExpiryThreadIntervalSeconds="120">

        <cacheEventListenerFactory
            class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
            properties="replicateAsynchronously=false"/>

        <bootstrapCacheLoaderFactory
            class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
            properties="bootstrapAsynchronously=false"/>

    </cache>

    <cacheManagerPeerProviderFactory
        class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
        properties="
            peerDiscovery=automatic,
            multicastGroupAddress=228.0.0.5,
            multicastGroupPort=4446,
            timeToLive=1"
    />

    <cacheManagerPeerListenerFactory
        class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>

</ehcache>
```

When using a firewall:

1. The `multicastGroupPort` port might need an UDP firewall unlock.
2. If you don't specify ports for `<cacheManagerPeerListenerFactory>`, the ports are chosen at random and might be blocked by the firewall. You can specify explicit ports to address this:

   ```xml
   <cacheManagerPeerListenerFactory
       class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
       properties="
           port=4501,
           remoteObjectPort=4502"
   />
   ```

The servlet container must be configured to replicate the session information.

With Tomcat, this is done in `server.xml` within the `<Engine>` element:

```xml
<Cluster
    className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
    channelSendOptions="6">

    <Manager
        className="org.apache.catalina.ha.session.DeltaManager"
        expireSessionsOnShutdown="false"
        notifyListenersOnReplication="true"/>
        
    <Channel className="org.apache.catalina.tribes.group.GroupChannel">
        <Membership
            className="org.apache.catalina.tribes.membership.McastService"
            address="228.0.0.4"
            port="45564"
            frequency="500"
            dropTime="3000"/>
            
        <Receiver
            className="org.apache.catalina.tribes.transport.nio.NioReceiver"
            address="auto"
            port="5000"
            selectorTimeout="100"
            maxThreads="6"/>

        <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
            <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
        </Sender>
        
        <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
        <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
        <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
    </Channel>

    <Valve
        className="org.apache.catalina.ha.tcp.ReplicationValve"
        filter=".*\.gif|.*\.js|.*\.jpeg|.*\.jpg|.*\.png|.*\.htm|.*\.html|.*\.css|.*\.txt"/>
           
    <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
```

In that configuration, the following can be changed:

* the IP multicast address, here `address="228.0.0.4"`
* the IP multicast port, here `port="45564"`

For details about the Tomcat configuration, see [Clustering/Session Replication HOW-TO](https://tomcat.apache.org/tomcat-9.0-doc/cluster-howto.html).

## Other considerations

### Individual server load

Consider a scenario where you have two servers with replication enabled, and one of them fails. This means that users from the failed server are redirected by the load balancer to the server which is still working. If, at the time of failure, both servers were nearing their full capacity, then suddenly the only remaining server will have to handle all the load.

This means that the load balanced servers should not be allowed to reach full capacity so that, in case of failure of a single server, the remaining server can handle all the load. Theoretically, this means that each server, in normal use, should be at under 50 % of total capacity.

Using more than 2 replicated servers allows using more of the available capacity of all servers in the case of a single server failure.

### HAProxy configuration

If using HAProxy, a simple configuration looks like this:

```
global
    daemon
    maxconn 256
    debug

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend http-in
    bind *:8080
    default_backend servers

backend servers
    cookie JSESSIONID prefix nocache
    server s1 127.0.0.1:8888 maxconn 32 cookie s1 check
    server s2 127.0.0.1:8889 maxconn 32 cookie s2 check

```

This configuration round-robins between two servers, `s1` and `s2`, on two ports, 8888 and 8889.

In this example, the servers are accessed at address 127.0.0.1, but in practice they might be on different physical servers.

For testing, you can start HAProxy with the following command:

```
haproxy -db -f haproxy.conf
```

For details about the HAProxy configuration, see the [HAProxy Configuration Manual](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html).

## Limitations

### Uploaded files

Uploaded files which are not yet saved to a database are currently not replicated. If a user is switched from one server to another, Form Runner:

* checks all unsaved attachments
* if there are any
  * clears the associated temporary file path
  * shows an alert to the user

This requires users with unsaved attachments to re-upload their attachments. This is not ideal, but it is likely that the user still have the attachment or attachments available.

### Loss of state

If a server fails instantly before it had the chance to replicate the latest modifications to a form, and after an Ajax response has been sent to the client, then state might be lost. The user is redirected by the load balancer to another server, but state will be missing from that server. In such cases, the user will see an error, and won't be able to continue working with the form. Unsaved data will be lost. In such cases, enabling the [autosave feature](/form-runner/persistence/autosave) can alleviate the issue.

The `ehcache.xml` configuration provided above attempts to minimize this kind of issues by adding `replicateAsynchronously=false`. *NOTE: We have feedback from customers that if manual peer discovery (RMI TCP unicast) is enabled, setting `replicateAsynchronously="true"` works and helps reduce latency.*

## See also

* [Installation](/installation)
* [Orbeon Forms Caches](/installation/caches)
* Blog post: [High-Availability Thanks to State Replication](https://blog.orbeon.com/2018/03/high-availability-thanks-to-state.html)
* [Clustering and High Availability](/configuration/advanced/clustering)


# Upgrading

## Steps

We recommend you test your upgrade in a non-production environment, and only upgrade your production environment once you've validated the upgrade. Also, before upgrading, we recommend you have a current backup of your database. Once done:

1. Stop your application server (e.g. Tomcat).
2. Move your existing Orbeon Forms install to a temporary directory. For instance, with Tomcat, in Tomcat's `webapps` directory, move any existing `orbeon` folder, as well as the `orbeon.war` file, if present, out of the way before proceeding.
3. Install the new `orbeon.war`. With Tomcat, this is often done by uncompressing the `orbeon.war` into an `orbeon` directory (`unzip -d orbeon orbeon.war`), and moving the `orbeon` directory inside the Tomcat `webapps` directory.
4. Restart your application server and test that your clean install works as expected "out of the box", that is without any of your custom configurations.
5. Put back configurations you had with your previous installation. Often, the only file you need to modify is `WEB-INF/resources/config/properties-local.xml`, but you might have created or made changes to other files, such as `WEB-INF/resources/config/form-builder-permissions.xml`, `WEB-INF/resources/config/log4j.xml` or `WEB-INF/web.xml`. If you had your `license.xml` file in the same directory, make sure to restore that as well.
6. Most installations of Orbeon Forms store data in a relational database using the built-in implementation of the persistence API. If this is your case, you might need to do some changes at the database level. Don't worry: those changes are typically very small.
   * Open the page [Using Form Runner with a relational database](/form-runner/persistence/relational-db).
   * You might have to run some upgrade DDL at the database level. In the page you just opened, find the section about the database you are using, and in the table with the DDL, given the version you are upgrading from and the version you are upgrading to, check if there is some DDL you need to run.
   * Open the "DDL to create from scratch" for the version you are upgrading to, and check that your database has all the indexes mentioned in this file. Make sure to check this, even if you didn't have to run any upgrade DDL.
7. Restart your application server and test that everything is working as expected with the new version of Orbeon Forms. We also recommend you review the [compatibility notes](#compatibility-notes-for-previous-versions) which might give you some indication of what you might to pay especially attention to when testing.

Finally, let us know if you have any question or encounter any issue while upgrading:

* Via Basecamp if using Orbeon Forms PE
* Via the Google Groups or Stack Overflow if using Orbeon Forms CE (see [community](https://groups.google.com/g/orbeon))

## Why upgrade to newer versions of Orbeon Forms?

### Maintenance updates

Orbeon Forms maintenance updates, for example Orbeon Forms 2024.1.1 or 2023.1.7, contain important bug-fixes but typically no new major features (in some rare cases, minor features can be introduced). We recommend you install the latest maintenance release for the version of Orbeon Forms you are using.

### Major releases

Major releases contain new features and bug-fixes. In some cases, bug-fixes cannot be backported to earlier versions of Orbeon Forms, for example in the case where the fix required an important change in the internal architecture of the software.

The only sure way to keep up to date is to eventually update to a newer major version of Orbeon Forms. We acknowledge that upgrading, while it should always be smooth, comes with some risks, as with any software upgrade.

You can reduce that risk by *not upgrading* to the first published major release, and wait until a later dot release. For example, if you are using Orbeon Forms 2023.1.7, instead of immediately upgrading to Orbeon Forms 2024.1, wait until Orbeon Forms 2024.1.1 or 2024.1.2 is available.

## Where do bug-fixes go?

The way we handle fixes, generally, is as follows:

* All new fixes go into the branch for the next major version of Orbeon Forms (for example, Orbeon Forms 2025.1).
* Most fixes (as opposed to new features) go into the previous major version of Orbeon Forms as well (for example Orbeon Forms 2024.1), and are released as a dot release at a later time (for example Orbeon Forms 2024.1.3).
* Some important fixes are backported to earlier versions of Orbeon Forms (for example 2022.1.x) at Orbeon's discretion.
* For Gold support customers, we backport certain features to customer branches on demand. But the more time passes between versions, the harder and riskier it becomes to backport fixes. This is because the codebase is more likely to change over time.

## Compatibility policy

* **Compatibility:** We strive to remain backward compatible between versions of Orbeon Forms and not to break features, whether on purpose or by accident.
* **Deprecation and removal:** Over time, we may mark some features as *deprecated*. After being deprecated for a while, these features can even be *removed* from the product. Over years, this means that backward compatibility is not always guaranteed.
* **Compatibility notes:** Release notes for each version might contain compatibility notes. When upgrading, please make sure to always review compatibility notes.

## Difficulty of upgrades

*NOTE: As a reminder, starting with Orbeon Forms 2016.1, we are using a versioning scheme with the number of the year first followed by the number of the major release during that year. See* [*Release History*](/release-history) *for details.*

* Between any two subsequent 4.x releases, or 20xx.x releases, upgrades are expected to be fairly straightforward.
* The longer the interval of time between two release, the harder the upgrade might be. For example, it will be easier to upgrade between 2023.1 and 2024.1 than between 2016.1 and 2024.1.
* Orbeon Forms 4.0 was a large release with many changes. In general upgrading between pre-4.0 releases and 4.x releases is more difficult than upgrades between two 4.x or 20xx.x releases.
* Since Orbeon Forms 4.0, we have switched to a faster release cycle, with releases every few months. So there are typically more changes between, say, 3.8 and 3.9, and especially 3.9 and 4.0, than between two subsequent 4.x or 20xx.x releases.

## Areas of compatibility

* **Form Runner / Form Builder DDL:** The relational database definitions are subject to change between releases. We provide scripts to upgrade the definitions between versions.
* **Form Runner form format:** We strive to keep the format, when form definitions are not modified by users, fully backward compatible.
  * Form Builder, upon loading and republishing forms, can upgrade the format of form definitions when needed.
  * Similarly, and since Orbeon Forms 4.6, the Form Runner Home page has an "Upgrade" feature to upgrade published form definitions.
* **XForms support:** We strive for maximum backward compatibility at the XForms source level. But because the XForms processing model is quite advanced, some subtle details are subject to change, such as the order in which some events are dispatched.
* **Look and feel and CSS:** Often users adapt the Orbeon Forms look and feel using custom CSS. It is hard to guarantee full backward compatibility here due to the lack of encapsulation provided by CSS. Upgrades can require custom CSS to be adapted. 4.0 in particular introduced the Bootstrap library for the user interface, and that was a major change from previous versions. 2026.1 upgraded that library from Bootstrap 2 to Bootstrap 5.
* **Configuration properties:** We strive to keep properties backward compatible. On rare occasions, configuration properties have changed in incompatible ways, in particular in 4.0 the Form Runner persistence providers configuration have changed.

## Compatibility notes for previous versions

When about to upgrade, we recommend you go through the release notes for all versions between the version you are using and the one you are upgrading to, paying particular attention to the compatibility notes sections. Here are the releases which contain backward compatibility notes:

* [2025.1](/release-notes/orbeon-forms-2025.1#compatibility-and-upgrade-notes)
* [2024.1](/release-notes/orbeon-forms-2024.1#compatibility-and-upgrade-notes)
* [2023.1.1](/release-notes/orbeon-forms-2023.1.1#compatibility-notes)
* [2023.1](/release-notes/orbeon-forms-2023.1#compatibility-and-upgrade-notes)
* [2022.1.1](/release-notes/orbeon-forms-2022.1.1#compatibility-notes)
* [2022.1](/release-notes/orbeon-forms-2022.1#compatibility-notes)
* [2021.1](/release-notes/orbeon-forms-2021.1#compatibility-notes)
* [2020.1](/release-notes/orbeon-forms-2020.1#compatibility-notes)
* [2019.2](/release-notes/orbeon-forms-2019.2#compatibility-notes)
* [2019.1](/release-notes/orbeon-forms-2019.1#compatibility-notes)
* [2018.2](https://blog.orbeon.com/2018/12/orbeon-forms-20182.html)
* [2018.1](https://blog.orbeon.com/2018/09/orbeon-forms-20181.html)
* [2017.1](https://blog.orbeon.com/2017/06/orbeon-forms-20171.html)
* [2016.2](https://blog.orbeon.com/2016/08/orbeon-forms-20162.html)
* [2016.1](https://blog.orbeon.com/2016/04/orbeon-forms-20161.html)
* [4.10](https://blog.orbeon.com/2015/08/orbeon-forms-410.html)
* [4.9](https://blog.orbeon.com/2015/05/orbeon-forms-49.html)
* [4.8](https://blog.orbeon.com/2015/01/orbeon-forms-48.html)
* [4.7](https://blog.orbeon.com/2014/09/orbeon-forms-47.html)
* [4.6.2](https://blog.orbeon.com/2014/08/orbeon-forms-462.html)
* [4.6.1](https://blog.orbeon.com/2014/07/orbeon-forms-461.html)
* [4.6](https://blog.orbeon.com/2014/06/orbeon-forms-46.html)
* [4.5](https://blog.orbeon.com/2014/04/orbeon-forms-45.html)
* [4.4](https://blog.orbeon.com/2013/11/orbeon-forms-44.html)
* [4.3](https://blog.orbeon.com/2013/08/orbeon-forms-43.html)
* [4.2](https://blog.orbeon.com/2013/05/orbeon-forms-42.html)
* [4.0](http://wiki.orbeon.com/forms/doc/developer-guide/release-notes/40#TOC-Compatibility-notes)
* [3.9](http://wiki.orbeon.com/forms/doc/developer-guide/release-notes/39#TOC-Compatibility-notes)

## Reliance on Orbeon Forms internals

### Recommendation

In general, we *strongly recommend* that you do not rely on Orbeon Forms internals, but only on published APIs.

This includes not modifying the content of any JAR files present in Orbeon Forms.

### When it happens

Our users sometimes customize Orbeon Forms by relying on the internals of Orbeon Forms. This might even be on Orbeon's advice, when no better solution are available at a given time. In such cases, upgrading can be more difficult, because the internals of Orbeon Forms are subject to change, and backward compatibility of look and feel is difficult to achieve with only CSS.

When this happens, we consider the reasons changes relying upon Orbeon Forms internals, and evaluate how this could be improved in the future. Examples include:

* Available
  * [Form Runner: Buttons and Processes](/form-runner/advanced/buttons-and-processes)
  * [Form Runner: Custom dialogs and model logic](/form-runner/advanced/custom)
* Considered
  * [Stable API for Form Runner](https://github.com/orbeon/orbeon-forms/issues/1095)

## What are the benefits of upgrading Orbeon Forms versions?

Each new version brings:

* stability, security and other bug-fixes
* new features

In addition, we can support newer version of Orbeon Forms much better than older versions.

In addition, if you are on the 3.x series of Orbeon Forms, the 4.x series brings:

* an improved look and feel
* a rewritten Form Builder relying on a better foundation
* more configurable features
* many new features

## See also

* [Orbeon Forms release history](/release-history)


# Configuration


# Properties

## Overview

Orbeon Forms is configured via *configuration properties*. They are setup in a file called `properties-local.xml` and stored in the Orbeon Forms WAR file as:

```
WEB-INF/resources/config/properties-local.xml
```

Orbeon Forms will do a certain number of things out of the box without you having to setup anything in your `properties-local.xml`. But if you want to change the default behavior (and it is likely you will want to in order to setup access control, database access, configure buttons, etc.), you will need to make changes to that file. This page describes the basics of that process.

## Setting and overriding properties

You can change properties by editing `properties-local.xml`. That file goes in the directory `WEB-INF/resources/config`, inside the Orbeon Forms web app.

If that file doesn't exist yet in your installation of Orbeon Forms, you can create it by renaming or copying the file `properties-local.xml.template` into `properties-local.xml`. At this point, your `properties-local.xml` will only contain an opening `<properties>` tag and closing `</properties>` tag, and you'll want to edit it to add properties between those two tags, as in:

```xml
<properties xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:oxf="http://www.orbeon.com/oxf/processors">
    <property as="xs:string"
              name="oxf.fr.persistence.provider.*.*.*"
              value="oracle"/>
</properties>
```

## Properties you must configure

You must set the [`oxf.crypto.password`](https://doc.orbeon.com/configuration/pages/-LEkBrAoJAmy6iucVDPz#oxf.crypto.password) property to something different from the default.

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

Orbeon Forms will cause an error when starting if the default value for `oxf.crypto.password` is used. This is to prevent you from using the default value in production.

In addition, a password strength checker will also cause an error if the password is too weak. Ideally, use a randomly-generated strong password.

## Documentation for specific properties

Each Orbeon Forms subsystem defines its own properties. They are documented in the following pages:

* [General properties](/configuration/properties/general)
* [Form Runner properties](/configuration/properties/form-runner)
  * [Detail page](/configuration/properties/form-runner/form-runner-detail-page)
    * [Attachments](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments)
    * [Email](/configuration/properties/form-runner/form-runner-detail-page/form-runner-email)
    * [PDF](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf)
  * [Persistence](/configuration/properties/form-runner/persistence)
  * [Summary page](/configuration/properties/form-runner/form-runner-summary-page)
* [Form Builder properties](/configuration/properties/form-builder)
* [XForms properties](/configuration/properties/xforms)

## What's in a property

A property is made of:

* a *type*, such as `xs:boolean`
* a *name*, such as `oxf.resources.versioned`
* a *value*, such as `true`
* optionally, and rarely, a *processor name*, such as `oxf:page-flow`, which refers to an XPL processor name

This is typically put together like this in `properties-local.xml`:

```xml
<property
  as="xs:boolean"
  name="oxf.resources.versioned"
  value="true"/>
```

Some properties support wildcards, for example:

```
oxf.fr.persistence.app.uri.*.*.*
```

You can also place longer property values inline \[SINCE Orbeon Forms 4.6]:

```xml
<property as="xs:string" name="oxf.fr.detail.process.save-final.*.*">
    require-uploads
    then validate-all
    then save
    then success-message("save-success")
    recover error-message("database-error")
</property>
```

Changes to most properties are taken into account as soon as you save your property file (e.g. `properties-local.xml`), however changes to some properties are only taken into account when the server is first started.

## Categories of properties

Properties fall in two categories:

* *standard*, which means they are defined by Orbeon Forms
* *custom*, which means they are defined by form authors, administrators or integrators

All standard properties have standard values defined in built-in property files, described in the pages linked above, and can be overridden when needed.

In general, here is how you deal with properties:

* you look up the documentation for a given property
* if needed, you set or override the property in `properties-local.xml`

## Wildcards in properties

Property names may be defined using wildcards. A property name is assumed to be built as a series of path elements separated by `.` characters. A path element may contain a `*` character instead of an actual path element value.

```xml
<property
  as="xs:anyURI"
  name="oxf.fr.persistence.app.uri.*.*.*"
  value="/fr/service/exist"/>

<property
  as="xs:anyURI"
  name="oxf.fr.persistence.app.uri.*.*.data"
  value="/fr/service/oracle"/>

<property
  as="xs:anyURI"
  name="oxf.fr.persistence.app.uri.orbeon.builder.form"
  value="/fr/service/resource"/>
```

In this example:

* If the property name `oxf.fr.persistence.app.uri.orbeon.builder.form` is requested, the value `/fr/service/resource` is returned (exact match).
* If the property name `oxf.fr.persistence.app.uri.orbeon.foobar.data` is requested, the value `/fr/service/oracle` is returned, because the path elements `orbeon`, `foobar` match wildcards, and `data` matches the last path element.
* If the property name `oxf.fr.persistence.app.uri.orbeon.foobar.form` is requested, the value `/fr/service/exist` is returned, because the path elements `orbeon`, `foobar` and `form` all match wildcards.

This allows creating hierarchical properties with generic defaults and more specific values.

In general these are used with Form Runner or Form Builder and in these cases:

* The first wildcard matches a forms "application name"
* The second wildcard matches a forms "form name"

There is a precedence order with wildcards:

1. an exact match is checked first and always wins if found
2. a wildcard match is done then, starting from the left

So:

* `foo.bar` wins over `*.*`, `*.bar`, and `foo.*`
* `foo.*` wins over `*.bar`

## Built-in property files

The default values for those properties are provided in the following files, which are stored in `orbeon-resources-private.jar`:

* `config/properties-dev.xml`
  * root of `dev` mode properties
* `config/properties-prod.xml`
  * root of `prod` mode properties
* `config/properties-base.xml`
  * base Orbeon Forms properties
* `config/properties-xforms.xml`
* `config/properties-form-runner.xml`
* `config/properties-form-builder.xml` In general, you shouldn't modify these files.

## Properties types

Properties have a documented type, which must be one of the following:

* `xs:string`
* `xs:boolean`
* `xs:integer`
* `xs:anyURI`
* `xs:QName`
* `xs:date`
* `xs:dateTime`
* `xs:NMTOKENS`

## Defining your own properties

In addition to the standard properties, you can define your own properties. You can then access them from:

* XPath expressions in XForms with [`xxf:property()`](/xforms/xpath/extension-functions/extension-core#xxfproperty).
* XPath expressions in XPL with `p:property()`, where the prefix `p` is mapped to the namespace `http://www.orbeon.com/oxf/pipeline`.
* XPath expressions in XSLT with `pipeline:property()`, where the prefix pipeline is mapped to namespace `java:org.orbeon.oxf.processor.pipeline.PipelineFunctionLibrary`.

In all cases, for security reasons, those functions won't return the value of properties that contain the string "password" in the name of the property.

## Different properties for dev vs. production

In general, you can define all your custom properties in `properties-local.xml`. However, if the value of a property needs to differ depending on the environment, e.g. the value is different for `dev` and `prod`, then you can define those properties twice, in `properties-local-dev.xml` *and* `properties-local-prod.xml`, and have different values defined for the property depending on the file. In that case, you would still keep your custom properties that don't differ depending on the environment in `properties-local.xml`.

* Properties you define in `properties-local-dev.xml` apply in `dev` run mode only, and in that case override properties in `properties-local.xml`.
* Properties you define in `properties-local-prod.xml` apply in `prod` run mode only, and in that case override properties in `properties-local.xml`.

## For contributors: properties subsystem initialization

The properties sub-system is initialized after the [Resource Manager](/xml-platform/resources/resource-managers) (the properties being read like any other Orbeon Forms resources). By default, the following top-level URL is loaded:

* web app in `prod` mode: `oxf:/config/properties-prod.xml`
* web app in `dev` mode: `oxf:/config/properties-dev.xml`
* command-line: `oxf:/properties.xml`

Property files support inclusions via XInclude. This is the mechanism used by Orbeon Forms to load all the secondary property files.


# General

## Default values

For the latest default values of general properties, see [`properties-base.xml`](https://github.com/orbeon/orbeon-forms/blob/master/src/main/resources/config/properties-base.xml).

## XML entity expansion

For security reasons, Orbeon Forms disables XML *external* entities. Other entities are enabled, but subject to a limit. This limit is set to 100,000 entity expansions.

[\[SINCE Orbeon Forms 2023.1.1\]](/release-notes/orbeon-forms-2023.1.1)

Orbeon Forms has a new global setting to control (internal) XML entity expansion. Previously, XML entity expansion, including character entities, was enabled but subject to a limit. Since this version, you can configure XML entity expansion. By default, for security reasons, and since entities are rarely used, this is set to 0. To restore the previous behavior, set this property to a positive number:

```xml
<property
    as="xs:integer"
    name="oxf.xml-parsing.entity-expansion-limit"
    value="0"/>
```

## URL rewriting

### oxf.url-rewriting.service.base-uri

|               |                                                               |
| ------------- | ------------------------------------------------------------- |
| Name          | `oxf.url-rewriting.service.base-uri`                          |
| Purpose       | specify the base URL for rewriting some internal service URLs |
| Type          | `xs:anyURI`                                                   |
| Default Value | Empty. Rewriting is done against the incoming request.        |

Usually Orbeon Forms uses the host, port, and context name as seen by the browser, such as:

```
http://www.mycompany.com/orbeon
```

to infer how to reach itself when calling some service URLs (see below for which URLs apply depending on the Orbeon\
Forms version). But in some cases, Orbeon Forms cannot reach to itself this way and an explicit base URL must be\
specified with this property.

Such cases include:

* accessing the server through different host names (like `https://foo/orbeon` and `https://bar/orbeon` reaching the same Orbeon Forms instance)
* accessing the embedded eXist database (for demo purposes) when the request goes through a reverse proxy
  * NOTE: The eXist database is removed [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1).

When you are in such configurations, please make sure to set `oxf.url-rewriting.service.base-uri` to point to the local\
servlet container instance, for example:

```xml
<property
    as="xs:anyURI"  
    name="oxf.url-rewriting.service.base-uri"              
    value="http://localhost:8080/orbeon"/>
```

#### Orbeon Forms 4.7 and newer

Since Orbeon Forms 4.7, this property is only used for the following:

* access to the embedded eXist database
* access to custom services located in the Orbeon web app (there are none by default)

You *don't need* to set this property if:

* you do not use the embedded eXist or custom services
* or you use the embedded eXist database or a custom service and
  * you are running your servlet container on a local computer for testing or deployment
  * or your external server name and port are accessible from the servlet container

When things don't work out of the box, typically when the network setup contains a front-end web server and/or prevents\
a network connection from the servlet container to itself, setting it to the following is usually enough:

```xml
<property
    as="xs:anyURI"
    name="oxf.url-rewriting.service.base-uri"
    value="http://localhost:8080/orbeon"/>
```

Make sure to adjust the port and prefix as needed.

#### Orbeon Forms 4.6.x and earlier

Up to and including Orbeon Forms 4.6.x, this property was used for all service calls, including calls to internal\
services used by Form Runner and Form Builder, such as loading i18n resources and accessing the persistence\
implementation.

With 4.6.x and earlier, you *don't need* to set this property if:

* you are running your servlet container on a local computer for testing or deployment
* or your external server name and port are accessible from the servlet container

When things don't work out of the box, typically when the network setup contains a front-end web server and/or prevents\
a network connection from the servlet container to itself, setting it to the following is usually enough:

```xml
<property
    as="xs:anyURI"
    name="oxf.url-rewriting.service.base-uri"
    value="http://localhost:8080/orbeon"/>
```

Make sure to adjust the port and prefix as needed.

## Encryption properties

### oxf.crypto.password

This property is used to create a private key used for encryption. You must change the default value of the password, even though a random seed is used.

```xml
<property
  as="xs:string"
  name="oxf.crypto.password"
  value="CHANGE THIS PASSWORD"/>
```

As of Orbeon Forms 2021.1, this property is used for:

* [Field-level encryption](/form-builder/advanced/field-level-encryption)
* [Orbeon Forms version encryption](https://doc.orbeon.com/configuration/properties/pages/-LEkBrB33jWKQzVAi5Ln#oxf.xforms.resources.encode-version) for cached assets URLs
  * if `oxf.xforms.resources.encode-version` is `true`, which is the default
* [Upload events encryption](https://github.com/orbeon/orbeon-forms/issues/4624)
* [Hashes for the `metadata` format](/form-runner/advanced/buttons-and-processes/actions-form-runner/actions-form-runner-send#sending-form-metadata)
* Hashing internal upload URLs to prevent against tampering
* Form data encryption for the ["Test PDF"](/form-builder/advanced/pdf-test) feature
  * SINCE Orbeon Forms 2021.1
* The [XForms `hmac()` function](https://www.w3.org/TR/xforms11/#fn-hmac)

The following uses are considered legacy and not in use by default in Orbeon Forms anymore:

* The `$instance` URL parameter encryption
* Form static/dynamic state with client state handling encryption

*NOTE: If the backwards compatibility property `oxf.xforms.password` is defined, then it is used first. However, it is deprecated, and we advise not using it as support might be removed in a future Orbeon Forms version.*

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

Orbeon Forms will cause an error when starting if the default value for `oxf.crypto.password` is used. This is to prevent you from using the default value in production.

In addition, a password strength checker will also cause an error if the password is too weak. Ideally, use a randomly-generated strong password.

### oxf.crypto.check-password-strength

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

This property enables or disables the password strength checker. The default is `true` and enables the checker.

```xml
<property
    as="xs:boolean"
    name="oxf.crypto.check-password-strength"
    value="true"/>
```

When this is set to `true`, passwords are checked upon first use for strength. This is a baseline check only. In any case, you should use strong passwords and keep them secret, especially for production use.

The following passwords are checked:

* [`oxf.crypto.password`](#oxf.crypto.password)
* [`oxf.fr.access-token.password`](/form-runner/access-control/tokens)
* [`oxf.fr.field-encryption.password`](/form-builder/advanced/field-level-encryption)

### oxf.crypto.key-length

This property specifies the length of the AES encryption key. The default is 128 bits.

```xml
<property
  as="xs:integer"
  name="oxf.crypto.key-length"
  value="128"/>
```

Higher strength encryption is usually not enabled by default in the JVM. See [Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files](http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html). When higher strength encryption is available, this value can be changed to 256, for example.

### oxf.crypto.hash-algorithm

This property specifies the default hash algorithm. The default is:

* Until Orbeon Forms 2022.1.2: `SHA1`
* Since Orbeon Forms 2022.1.3 and 2023.1: `SHA-256`

```xml
<property
  as="xs:string"
  name="oxf.crypto.hash-algorithm"
  value="SHA-256"/>
```

Not all encryption strengths are enabled by default in the JVM. See [Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files](http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html).

Orbeon forms uses hash algorithms in at least the following cases:

* to encode random identifiers, such as document ids in Form Runner
* for internal caching purposes
* XForms
  * keys for client-side scripts
  * keys for aggregated JavaScript and CSS resources
  * keys for dynamic xf:output resources
  * HMAC for server-side uploaded files

## Global properties

### oxf.cache.size

|               |                                               |
| ------------- | --------------------------------------------- |
| Name          | `oxf.cache.size`                              |
| Purpose       | set the size of the Orbeon Forms object cache |
| Type          | `xs:integer`                                  |
| Default Value | 1000                                          |

Orbeon Forms uses an efficient caching system. Orbeon Forms automatically determines what can be cached and when to expire objects. This size is reasonable for most applications. A bigger cache tends to make the application faster, but it uses more memory. To tune the cache size, see the suggestions in the [Performance and Tuning](http://wiki.orbeon.com/forms/doc/developer-guide/admin/performance-tuning) section.

### oxf.xpath.environment-variable.enabled

Access to environment variables with the [`environment-variable()` function](/xforms/xpath/standard-functions#environment-variable) is disabled by default. If you wish to make this XPath function available, set the following property to `true`.

```xml
<property 
    as="xs:boolean" 
    name="oxf.xpath.environment-variable.enabled"
    value="true"/>
```

### oxf.cache.xpath.size

|               |                                              |
| ------------- | -------------------------------------------- |
| Name          | `oxf.cache.xpath.size`                       |
| Purpose       | set the size of the Orbeon Forms XPath cache |
| Type          | `xs:integer`                                 |
| Default Value | 5000                                         |

This property configures the maximum number of compiled XPath expressions to keep in the XPath cache. To tune the cache size, see the suggestions in the [Performance and Tuning](http://wiki.orbeon.com/forms/doc/developer-guide/admin/performance-tuning) section.

*NOTE: A profiler run shows that 2000 cache entries takes, for fairly typical XPath expressions, about 5 MB of memory.*

### Showing the Orbeon Forms version number

\[SINCE Orbeon Forms 4.6.1]

This property controls whether Orbeon Forms outputs its version number to the client web browser:

* at the bottom of pages, in particular with Form Runner
* in the `<xh:meta name="generator" content="…">` element
* in combined JavaScript and CSS resource files built by the XForms engine

```xml
<property
  as="xs:boolean"
  name="oxf.show-version"
  value="false"/>
```

Default:

* `prod` mode: `false`
* `dev` mode: `true`

### XSLT output location mode

During development, the following XSLT transformer configuration helps with line number errors. The following values are allowed:

* `none`: no XSLT output line number information provided. This is recommended for deployment.
* `dumb`: minimal XSLT output line number information provided.
* `smart`: maximal XSLT output line number information provided. This is recommended for development.

```xml
<property
    as="xs:string"
    processor-name="oxf:builtin-saxon"
    name="location-mode"
    value="none"/>

<property
    as="xs:string"
    processor-name="oxf:unsafe-builtin-saxon"
    name="location-mode"
    value="none"/>
```

Default:

* `prod` mode: `none`
* `dev` mode: `smart`

## HTTP Server

### Errors and exceptions

The following property specifies whether the server is allowed to send detailed error and exceptions messages to the browser:

```xml
<property
    as="xs:boolean"
    name="oxf.http.exceptions"
    value="false"/>
```

Default:

* `prod` mode: exceptions are not sent to the browser
* `dev` mode: exceptions are sent to the browser

### Caching

[\[SINCE Orbeon Forms 2024.1\]](/release-notes/orbeon-forms-2024.1) The `oxf.http.service.cache-headers` property is new in Orbeon Forms 2024.1. The `oxf.http.page.cache-headers` property existed previously but was not publicly documented.

Orbeon Forms allows you to control the value of the `Cache-Control` HTTP caching header it sets in HTTP responses. For pages, i.e. the HTML sent to the browsers for your forms, the default value allows browsers to cache the page but requires the browser to revalidate the cached content with the server. For services, like APIs provided by Form Runner, caching is disabled. You can change these defaults with the following properties.

```xml
<property as="xs:string"  name="oxf.http.page.cache-headers">
    Cache-Control: private, max-age=0; Pragma:
</property>
<property as="xs:string"  name="oxf.http.service.cache-headers">
    Cache-Control: no-store
</property>
```

## HTTP Client

See [HTTP client configuration properties](/configuration/properties/general/properties-general-http-client).

## Epilogue and theme properties

### oxf.epilogue.theme

|               |                                  |
| ------------- | -------------------------------- |
| Name          | `oxf.epilogue.theme`             |
| Purpose       | specifies the theme stylesheet   |
| Type          | `xs:anyURI`                      |
| Default Value | `oxf:/config/theme-examples.xsl` |

This can be overwritten for a given app by placing a file `theme.xsl` inside the app directory.

### oxf.epilogue.theme.embeddable

|               |                                                                                   |
| ------------- | --------------------------------------------------------------------------------- |
| Name          | `oxf.epilogue.theme.embeddable`                                                   |
| Purpose       | specifies the theme stylesheet to use when within a portlet or in embeddable mode |
| Type          | `xs:anyURI`                                                                       |
| Default Value | `oxf:/config/theme-portlet-examples.xsl`                                          |

This can be overwritten for a given app by placing a file `theme-embeddable.xsl` inside the app directory.

### oxf.epilogue.theme.renderer

|               |                                                                                                                       |
| ------------- | --------------------------------------------------------------------------------------------------------------------- |
| Name          | `oxf.epilogue.theme.renderer`                                                                                         |
| Purpose       | specifies the theme stylesheet to use when using the XForms filter, whether in integrated or separate deployment mode |
| Type          | `xs:anyURI`                                                                                                           |
| Default Value | `oxf:/config/theme-plain.xsl`                                                                                         |

### oxf.epilogue.theme.error

|               |                                                         |
| ------------- | ------------------------------------------------------- |
| Name          | `oxf.epilogue.theme.error`                              |
| Purpose       | specifies the theme stylesheet to use on the error page |
| Type          | `xs:anyURI`                                             |
| Default Value | `oxf:/config/theme-error.xsl`                           |

### oxf.epilogue.use-theme

|               |                                            |
| ------------- | ------------------------------------------ |
| Name          | `oxf.epilogue.use-theme`                   |
| Purpose       | whether a theme stylesheet must be applied |
| Type          | `xs:boolean`                               |
| Default Value | `true`                                     |

### oxf.epilogue.output-xhtml

|               |                                               |
| ------------- | --------------------------------------------- |
| Name          | `oxf.epilogue.output-xhtml`                   |
| Purpose       | whether to output XHTML to the browser or not |
| Type          | `xs:boolean`                                  |
| Default Value | `false`                                       |

### oxf.epilogue.renderer-rewrite

|               |                                                                           |
| ------------- | ------------------------------------------------------------------------- |
| Name          | `oxf.epilogue.renderer-rewrite`                                           |
| Purpose       | whether the XForms renderer used in separate deployment must rewrite URLs |
| Type          | `xs:boolean`                                                              |
| Default Value | `false`                                                                   |

### oxf.epilogue.process-svg

|               |                                                             |
| ------------- | ----------------------------------------------------------- |
| Name          | `oxf.epilogue.process-svg`                                  |
| Purpose       | whether SVG content must be converted server-side to images |
| Type          | `xs:boolean`                                                |
| Default Value | `true`                                                      |

## Email processor properties

### Global SMTP host

Configure the SMTP host for all email processors. This global property can be overridden by local processor configurations.

```xml
<property
    as="xs:string"
    processor-name="oxf:email"
    name="smtp-host"
    value="mail.example.org"/>
```

### Global SMTP port

Configure the SMTP port for all email processors. This global property can be overridden by local processor configurations.

```xml
<property
    as="xs:string"
    processor-name="oxf:email"
    name="smtp-port"
    value="25"/>
```

### Global SMTP username

Configure the SMTP username for all email processors. This global property can be overridden by local processor configurations.

```xml
<property
    as="xs:string"
    processor-name="oxf:email"
    name="username"
    value="john"/>
```

### Global SMTP password

Configure the SMTP password for all email processors. This global property can be overridden by local processor configurations.

```xml
<property
    as="xs:string"
    processor-name="oxf:email"
    name="password"
    value="secret"/>
```

### Global SMTP encryption

Configure the SMTP encryption for all email processors. This global property can be overridden by local processor configurations.

```xml
<property
    as="xs:string"
    processor-name="oxf:email"
    name="encryption"
    value="tls"/>
```

### Test SMTP host

Configure a test SMTP host for all email processors. This global property when specified overrides all the other SMTP host configurations for all email processors, whether in the processor configuration or using the `smtp-host` property.

```xml
<property
    as="xs:string"
    processor-name="oxf:email"
    name="test-smtp-host"
    value="smtp.acme.org"/>
```

This property can easily be commented out for deployment, or placed in `properties-local-dev.xml` (see also [Run Modes](/configuration/advanced/run-modes)).

### Test recipient

Configure a test recipient email address for all email processors. This global property when specified overrides all the other SMTP recipient configurations for all email processors. \[SINCE Orbeon Forms 2024.1.1] If this property is specified, CC and BCC recipients are also ignored.

```xml
<property
    as="xs:string"
    processor-name="oxf:email"
    name="test-to"
    value="joe@example.org"/>
```

## Rarely used properties

### oxf.log4j-config

|                             |                                                          |
| --------------------------- | -------------------------------------------------------- |
| Name                        | `oxf.log4j-config`                                       |
| Purpose                     | specify the location of the Log4j 1.x configuration file |
| Type                        | `xs:anyURI`                                              |
| Default Value in Properties | `oxf:/config/log4j.xml`                                  |

For details about logging, see [Logging](/installation/logging).

*NOTE: You don't usually need to modify this property and the default location for `log4j.xml` should be preserved.*

### oxf.log4j2-config

\[SINCE Orbeon Forms 2021.1, 2020.1.6, 2019.2.4, 2019.1.2, 2018.2.5, 2018.1.4]

|                             |                                                          |
| --------------------------- | -------------------------------------------------------- |
| Name                        | `oxf.log4j2-config`                                      |
| Purpose                     | specify the location of the Log4j 2.x configuration file |
| Type                        | `xs:anyURI`                                              |
| Default Value in Properties | `oxf:/config/log4j2.xml`                                 |

For details about logging, see [Logging](/installation/logging).

*NOTE: You don't usually need to modify this property and the default location for `log4j2.xml` should be preserved.*

### oxf.pipeline.processors

|               |                                                                                        |
| ------------- | -------------------------------------------------------------------------------------- |
| Name          | `oxf.pipeline.processors`                                                              |
| Purpose       | specify the URL of the XML file with processor definitions for the XPL pipeline engine |
| Type          | `xs:anyURI`                                                                            |
| Default Value | `oxf:/processors.xml`                                                                  |

*NOTE: You don't usually need to modify this property.*

### oxf.validation.processor

|               |                                            |
| ------------- | ------------------------------------------ |
| Name          | `oxf.validation.processor`                 |
| Purpose       | control the automatic processor validation |
| Type          | `xs:boolean`                               |
| Default Value | Enabled                                    |

Many processors validate their configuration input with a schema. This validation is automatic and allows meaningful error reporting. To potentially improve the performance of the application, validation can be disabled in production environments.

*NOTE: It is strongly discouraged to disable validation, as validation can highly contribute to the robustness of the application.*

### oxf.validation.user

|               |                                 |
| ------------- | ------------------------------- |
| Name          | `oxf.validation.user`           |
| Purpose       | control user-defined validation |
| Type          | `boolean`                       |
| Default Value | Enabled                         |

User-defined validation is activated in the [XML Pipeline Definition Language](http://wiki.orbeon.com/forms/doc/developer-guide/xml-pipeline-language-xpl) with the attributes `schema-href` and `schema-uri`. To potentially improve the performance of the application, validation can be disabled in production environments.

*NOTE: It is strongly discouraged to disable validation, as validation can highly contribute to the robustness of the application.*

### sax.inspection

|               |                              |
| ------------- | ---------------------------- |
| Name          | `sax.inspection`             |
| Purpose       | enable inspection SAX events |
| Type          | `xs:boolean`                 |
| Default Value | `false`                      |

SAX is the underlying mechanism in Orbeon Forms by which processors receive and generate XML data. Given only the constraints of the SAX API, it is possible for a processor to generate an invalid sequence of SAX events. Another processor that receives that invalid sequence of events may or may not be able to deal with it without throwing an exception. Some processors try to process invalid SAX events, while others throw exceptions. This means that when a processor generating an invalid sequence of SAX events is used in a pipeline, the problem might go unnoticed, or it might cause some other processor downstream to throw an exception.

To deal more efficiently with those cases, the `sax.inspection` property can be set to `true`. When it is set to `true`, the pipeline engine checks the outputs of every processor at runtime and makes sure that valid SAX events are generated. When an error is detected, an exception is thrown right away, with information about the processor that generated the invalid SAX events.

There is a performance penalty for enabling SAX events inspection. So this property should not be enabled on a production system.

*NOTE: You don't usually need to enable this property.*


# HTTP client

## Proxy setup

To configure an HTTP proxy to be used for all the HTTP connections established by Orbeon Forms, add the following two properties:

```xml
<property
    as="xs:string"
    name="oxf.http.proxy.host"
    value="localhost"/>

<property
    as="xs:integer"
    name="oxf.http.proxy.port"
    value="8090"/>

<property
    as="xs:boolean"
    name="oxf.http.proxy.use-ssl"
    value="false"/>

<property
    as="xs:string"
    name="oxf.http.proxy.exclude"
    value=""/>

<property
    as="xs:string"
    name="oxf.http.proxy.username"
    value=""/>

<property
    as="xs:string"
    name="oxf.http.proxy.password"
    value=""/>

<property
    as="xs:string"
    name="oxf.http.proxy.ntlm.host"
    value=""/>

<property
    as="xs:string"
    name="oxf.http.proxy.ntlm.domain"
    value=""/>
```

By default, the host and port properties are commented and Orbeon Forms doesn't use a proxy. Some of the use cases where you will want to define a proxy include:

* Your network setup requires you to go through a proxy.
* You would like see what goes through HTTP by using a tool that acts as an HTTP proxy, such as [Charles](https://www.charlesproxy.com/).

To connect to the proxy over HTTPS, instead of HTTP which is the default, set the `oxf.http.proxy.use-ssl` property to `true`.

\[SINCE Orbeon Forms 4.6]

You can exclude host names from the proxy using the `oxf.http.proxy.exclude` property, which contains a space-delimited list of hostnames.

## SSL hostname verifier

When using HTTPS, you can specify how the hostname of the server is checked against the hostname in its certificate. You do so with the following property:

```xml
<property
    as="xs:string"
    name="oxf.http.ssl.hostname-verifier"
    value="strict"/>
```

The possible values are:

* `strict` — (the default) See [`StrictHostnameVerifier`](https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/conn/ssl/StrictHostnameVerifier.html).
* `browser-compatible` — See [`BrowserCompatHostnameVerifier`](https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.html).
* `allow-all` — See [`AllowAllHostnameVerifier`](https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/conn/ssl/AllowAllHostnameVerifier.html).

Typically, you'll leave this property to its default value (`strict`). However, you might need to set it to `allow-all` to be able to connect to a server with a self-signed certificate if the `cn` in the certificate doesn't match the hostname you're using to connect to that server.

## 2-way SSL

When using HTTPS, you might want Orbeon Forms to authenticate itself by presenting a client certificate. For this, you need the client to have a key and certificate in a keystore, and point Orbeon Forms to that keystore using the propertied below.

```xml
<property
    as="xs:anyURI"
    name="oxf.http.ssl.keystore.uri"
    value="oxf:/config/my.keystore"/>

<property
    as="xs:string"
    name="oxf.http.ssl.keystore.password"
    value="changeit"/>
```

* `oxf.http.ssl.keystore.uri`
  * Specifies the URI of the keystore file.
  * The URI can use the `file:` or \[SINCE Orbeon Forms 2021.1] `oxf:` protocol.
  * Relationship to the truststore:
    * \[SINCE Orbeon Forms 2021.1] Whether this property is specified or not, the server certificate is verified using the default truststore, which you override by setting the `javax.net.ssl.trustStore` property (more on this in the [JSSE Reference Guide](https://docs.oracle.com/en/java/javase/11/security/java-secure-socket-extension-jsse-reference-guide.html)).
    * \[UNTIL Orbeon Forms 2020.1] If you specify a keystore, it is also used as a truststore. This is the case even if connecting to server whose key is signed by a recognized certificate authority (CA), which means that you need to add the certificate of the CA who signed the key of the server you want to connect to the keystore.
    * If this property is blank, the default JSSE algorithm to find a truststore applies.
* `oxf.http.ssl.keystore.password`
  * Specifies the password needed to access the keystore file.

You might also want to:

* For Orbeon Forms to accept incoming connections using the same certificate, set up your servlet container, on Tomcat in the `server.xml` on the `<Connector>` used for HTTPS, to point to same keystore.
* \[SINCE Orbeon Forms 2021.1] Set the `oxf.http.ssl.keystore.*` system property to point to a truststore that contains the certificate of the certificate authority who signed the certificate of the server you want to connect to.

## Headers forwarding

When Orbeon Forms performs XForms submissions, or retrieves documents in XPL over HTTP, it has the ability to forward incoming HTTP headers. For example, if you want to forward the `Authorization` header to your services:

```xml
<property
    as="xs:string"
    name="oxf.http.forward-headers"
    value="Authorization"/>
```

For more, see [HTTP headers forwarding](/xforms/submission/submission-extensions#http-headers-forwarding).

*WARNING: For security reasons, you should be careful with header forwarding, as this might cause non trusted services to receive client headers.*

## Cookies forwarding

Similar to general headers forwarding, cookies can be forwarded. By default, the property is as follows:

```xml
<property
    as="xs:string"
    name="oxf.http.forward-cookies"
    value=""/>
```

If you need to forward, say, `JSESSIONID` and `JSESSIONIDSSO` to services, set this in your `properties-local.xml`:

```xml
<property
    as="xs:string"
    name="oxf.http.forward-cookies"
    value="JSESSIONID JSESSIONIDSSO"/>
```

When a username for HTTP Basic authentication is specified, cookies are not forwarded. The first cookie in the list, typically `JSESSIONID`, is interpreted by Orbeon Forms to be the session cookie. If the value of the session cookie doesn't match the current session, say because the provided `JSESSIONID` has expired or is invalid, then the value of the cookie from the incoming request isn't forwarded. Instead, in that case, the new value of the session cookie is:

* \[UP TO Orbeon Forms 2016.3] The session id.
* \[SINCE Orbeon Forms 2017.1] The concatenation of the following 3 values:
  1. The value of the `oxf.http.forward-cookies.session.prefix` property
  2. The session id
  3. The value of the `oxf.http.forward-cookies.session.suffix` property

By default, the value of the prefix and suffix properties is empty, as shown below, which works well with application servers like Tomcat that set the `JSESSIONID` directly to the session id.

```xml
<property 
    as="xs:string"
    name="oxf.http.forward-cookies.session.prefix"         
    value=""/>
<property
    as="xs:string"
    name="oxf.http.forward-cookies.session.suffix"
    value=""/>
```

On the other hand, some application servers, add a prefix and suffix to the session id. For instance, WebSphere uses the *cache id* as prefix, and the colon character (`:`) followed by the *clone id* as suffix. So, on WebSphere, assuming that in your situation the *cache id* is always `0000`, and the *clone id* (found in WebSphere's `plugin-cfg.xml`) is `123`, you will want to set those properties as shown below. Note how the value of the *clone id* follows the colon character in the value of the suffix property.

```xml
<property 
    as="xs:string"
    name="oxf.http.forward-cookies.session.prefix"         
    value="0000"/>
<property
    as="xs:string"
    name="oxf.http.forward-cookies.session.suffix"
    value=":123"/>
```

*WARNING: For security reasons, you should be careful with cookies forwarding, as this might cause non trusted services to receive client cookies.*

## Stale checking

This property is tied to the [HttpClient stale checking](http://hc.apache.org/httpclient-3.x/apidocs/org/apache/commons/httpclient/params/HttpConnectionParams.html#setStaleCheckingEnabled%28boolean%29):

> Defines whether stale connection check is to be used. Disabling stale connection check may result in slight performance improvement at the risk of getting an I/O error when executing a request over a connection that has been closed at the server side.

By default, Orbeon checks for stale HTTP connections. You can disabling stale connection checking by setting the following property to `false` (it is `true` by default):

```xml
<property
    as="xs:boolean"
    name="oxf.http.stale-checking-enabled"
    value="false"/>
```

## Socket timeout

This property is tied to the [HttpClient SO timeout](http://hc.apache.org/httpclient-3.x/apidocs/org/apache/commons/httpclient/params/HttpConnectionParams.html#setSoTimeout%28int%29):

> Sets the default socket timeout (SO\_TIMEOUT) in milliseconds which is the timeout for waiting for data. A timeout value of zero is interpreted as an infinite timeout.

By default, Orbeon doesn't set a timeout with HttpClient. Setting a timeout can be potentially dangerous as it can lead to service calls that take longer to run than the timeout you specified to fail in a way that can be unpredictable, as it is possible for your services to sometimes return before the timeout and sometimes after. If, nevertheless, you need to set a timeout, you can do so by adding the following property, e.g. here setting a timeout at 1 minute:

```xml
<property
    as="xs:integer"
    name="oxf.http.so-timeout"
    value="60000"/>
```

*NOTE: These two headers are computed values and it is only possible to override them with constant values by using the properties above. In general we don't recommend overriding these headers by using the properties above.*

## Request chunking

```xml
<property
    as="xs:boolean"
    name="oxf.http.chunk-requests"
    value="false"/>
```

## Expired and idle connections

\[SINCE Orbeon Forms 2019.1]

Since the HTTP client uses connection pooling, some connections can be come stale, which can cause errors at inopportune times. Enabling expired and idle connections checking can help reduce this issue.

The `oxf.http.expired-connections-polling-delay` property sets the expired connection checking polling delay. The default is 5,000 milliseconds (5 seconds).

```xml
<property 
    as="xs:integer" 
    name="oxf.http.expired-connections-polling-delay"      
    value="5000"/>
```

The `oxf.http.idle-connections-delay` property sets the idle connection time to live. The default is 30,000 milliseconds (30 seconds).

```xml
<property 
    as="xs:integer" 
    name="oxf.http.idle-connections-delay"
    value="30000"/> 
```

If `oxf.http.expired-connections-polling-delay` is commented out or not present, neither checks are performed.

If `oxf.http.idle-connections-delay` is commented out or not present, but `oxf.http.expired-connections-polling-delay` is present, then only the check for expired connections takes place.

Keeping expired and idle connections checking enabled can improve the reliability of connections to remote servers.

## See also

* [General configuration properties](/configuration/properties/general)


# Form Runner

## Children pages

* [Detail page](/configuration/properties/form-runner/form-runner-detail-page)
  * [Attachments](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments)
  * [Email](/configuration/properties/form-runner/form-runner-detail-page/form-runner-email)
  * [PDF](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf)
* [Persistence](/configuration/properties/form-runner/persistence)
* [Summary page](/configuration/properties/form-runner/form-runner-summary-page)

## Default values

For the latest default values of Form Runner properties, see [properties-form-runner.xml](https://github.com/orbeon/orbeon-forms/blob/master/src/main/resources/config/properties-form-runner.xml).

## Form Runner properties documented elsewhere

* [Custom dialogs and model Logic](/form-runner/advanced/custom)
  * `oxf.fr.detail.model.custom`
  * `oxf.fr.detail.dialogs.custom`
* [Wizard View](/form-runner/component/wizard)
  * `oxf.fr.detail.view.appearance`
  * `oxf.fr.detail.buttons.inner`
  * `oxf.xforms.xbl.fr.wizard.validate`
  * `oxf.xforms.xbl.fr.wizard.separate-toc`
  * `oxf.xforms.xbl.fr.wizard.section-status`
  * `oxf.xforms.xbl.fr.wizard.subsections-nav`
  * `oxf.xforms.xbl.fr.wizard.subsections-toc`
* [Autosave](/form-runner/persistence/autosave)
  * `oxf.fr.detail.autosave-delay`
  * `oxf.fr.persistence.*.autosave`
* [Simple data migration](/form-runner/features/simple-data-migration)
  * `oxf.fr.detail.data-migration`
* [Configuration Properties \~ Persistence](/configuration/properties/form-runner/persistence)
  * `oxf.fr.persistence.provider`
  * `oxf.fr.persistence.[provider].uri`
  * `oxf.fr.persistence.[provider].active`
  * `oxf.fr.persistence.[provider].autosave`
  * `oxf.fr.persistence.[provider].permissions`
  * `oxf.fr.persistence.[provider].versioning`
  * `oxf.fr.persistence.[provider].data-format-version`
* [Form Runner Access Control](/form-runner/access-control)
  * `oxf.fr.support-owner-group`
  * `oxf.fr.authentication.method`
  * `oxf.fr.authentication.container.roles`
  * `oxf.fr.authentication.container.roles.split`
  * `oxf.fr.authentication.header.username`
  * `oxf.fr.authentication.header.group`
  * `oxf.fr.authentication.header.roles`
  * `oxf.fr.authentication.header.roles.split`
  * `oxf.fr.authentication.header.roles.property-name`
  * `oxf.fr.authentication.header.sticky`
* [Form Runner Published Forms page](/form-runner/form-runner-pages/published-forms-page)
  * `oxf.fr.home.page-size`
  * `oxf.fr.home.table.link-to`
* [TIFF Production](/form-runner/features/tiff-production)
  * `oxf.fr.detail.tiff.compression.type`
  * `oxf.fr.detail.tiff.compression.quality`
  * `oxf.fr.detail.tiff.scale`
  * `oxf.fr.detail.tiff.filename`

## Language

### Default language

The following property determines Form Runner's default language:

```xml
<property
    as="xs:string"
    name="oxf.fr.default-language.*.*"
    value="en">
```

When wildcards are specified, this property can control the default language for a given app or form.

The property without wildcards can also be used to control the default language of pages which don't involve a specific form, such as the Form Runner Home page

```xml
<property
    as="xs:string"
    name="oxf.fr.default-language"
    value="en">
```

For more details, see [Language selection at runtime](/form-runner/features/localization#language-selection-at-runtime)

### Available languages

For a given form, you can filter which languages are available in the language selector with a space-separated list of language codes:

```xml
<property
  as="xs:string"
  name="oxf.fr.available-languages.*.*"
  value="en fr"/>
```

The language selector by default shows all languages available in the form definition. When this property is specified, only the intersection of the languages is shown in the selector. For example:

* Example 1
  * form languages: `en fr jp`
  * property: `en fr`
  * resulting languages: `en fr`
* Example 2
  * form languages: `en fr jp`
  * property: `en jp kr`
  * resulting languages: `en jp`

If the property is blank or contains the wildcard `*`, all the form languages are available.

\[SINCE Orbeon Forms 4.3]

For pages which don't involve a specific form, such as the Form Runner Home page, the following property controls the available languages:

```xml
<property
  as="xs:string"
  name="oxf.fr.available-languages"
  value="en fr"/>
```

For more details, see [Language selection at runtime](/form-runner/features/localization#language-selection-at-runtime)

## Timezone

\[SINCE Orbeon Forms 2021.1]

The Summary and Home pages display timestamps showing the created and last modified dates of forms and data.

You can configure an explicit timezone to display these timestamps:

```xml
<property
      as="xs:string"
      name="oxf.fr.default-timezone"
      value="America/Los_Angeles"/>
```

```xml
<property
      as="xs:string"
      name="oxf.fr.default-timezone"
      value="Asia/Kolkata"/>
```

If the property is blank (the default), the Java environment's default timezone is used.

\[UNTIL Orbeon Forms 2020.1]

The Java environment's default timezone is used.

## Remote servers

\[SINCE Orbeon Forms 4.7]

The `oxf.fr.home.remote-servers` property allows you to configure one or more remote Orbeon Forms servers for pushing and pulling form definitions. This property is used by:

* The [Forms Admin page](/form-runner/form-runner-pages/forms-admin-page) to display remote server options in the UI
* The [Remote server APIs](/form-runner/api/other-apis/remote) to programmatically push to or pull from remote servers

### Configuration format

The property value is a JSON array containing one or more server configuration objects:

```xml
<property as="xs:string" name="oxf.fr.home.remote-servers">
  [
    {
      "name"      : "prod",
      "label"     : "Production",
      "url"       : "https://prod.example.com/orbeon",
      "ui-access" : true,
      "api-access": true
    },
    {
      "name"      : "staging",
      "label"     : "Staging",
      "url"       : "https://staging.example.com/orbeon",
      "ui-access" : true,
      "api-access": false
    }
  ]
</property>
```

### Configuration fields

Each remote server object supports the following fields:

* `name` (optional): \[SINCE Orbeon Forms 2025.1] A unique identifier for the remote server, used by the API. If not specified, the server can only be accessed through the UI.
* `label` (required): A human-readable label displayed in the Forms Admin page UI.
* `url` (required): The base URL of the remote Orbeon Forms instance (without trailing slash)
* `ui-access` (optional): \[SINCE Orbeon Forms 2025.1] Whether the server is accessible from the Forms Admin page UI. Defaults to `true`.
* `api-access` (optional): \[SINCE Orbeon Forms 2025.1] Whether the server is accessible via the Remote server APIs. Defaults to `false`.

### Authentication on the remote server

Services on the remote server must be protected with Basic authentication. If the remote server needs to be accessible both by users through another form of authentication and services through Basic authentication, then this is typically configured using an [authorization service](/xml-platform/controller/authorization-of-pages-and-services#authorization-service).

### Backward compatibility

\[SINCE Orbeon Forms 4.4]

\[UNTIL Orbeon Forms 4.6]

Use the `oxf.fr.production-server-uri` property:

```xml
<property
  as="xs:anyURI"
  name="oxf.fr.production-server-uri"
  value="http://remote.server:8080/orbeon/"/>
```

\[SINCE Orbeon Forms 4.7]

If the `oxf.fr.production-server-uri` property is set and not empty, it takes precedence over the `oxf.fr.home.remote-servers` property for backward compatibility.

## Summary page

See [Summary page configuration properties](/configuration/properties/form-runner/form-runner-summary-page).

## Detail page

See [Detail page configuration properties](/configuration/properties/form-runner/form-runner-detail-page).

## Show Orbeon Forms version

\[UNTIL Orbeon Forms 4.6, use `oxf.show-version` starting Orbeon Forms 4.6.1]

```xml
<property
    as="xs:boolean"
    name="oxf.fr.version.*.*"
    value="true"/>
```

Whether to show the Orbeon Forms version at the bottom.

## Default logo

With this property, you can set the default logo URI. This logo appears on the Summary, Detail and Home pages for a given form. You can omit (or comment out) this property or set its value to the empty string if you don't want a default logo at all.

\[SINCE Orbeon Forms 2024.1.1] When the URI ends with `.svg`, Orbeon Forms generates markup that implements a fallback strategy - it first attempts to load the SVG version, then automatically falls back to a PNG version (using the same path but with `.png` extension) if SVG is not supported by the renderer. In practice, this means browsers will display the SVG version, while PDF generation will use the PNG version.

```xml
<property as="xs:anyURI"  name="oxf.fr.default-logo.uri.*.*">
    /apps/fr/style/orbeon-logo.svg
</property>
```

If you use two `*` wildcards, as in the example above, the property also sets the logo on the [Published Forms page](/form-runner/form-runner-pages/published-forms-page) and the [Forms Admin page](/form-runner/form-runner-pages/forms-admin-page)

1. Place your logo file(s) under one of the following recommended locations:
   * `WEB-INF/resources/forms/assets`: logo for all forms
   * `WEB-INF/resources/forms/APP/assets`: logo for app name APP
   * `WEB-INF/resources/forms/APP/FORM/assets`: logo for app name APP and form name FORM
2. Define the `oxf.fr.default-logo.uri.*.*` property to point to the file(s) you added. The path points to location under the `WEB-INF/resources` directory.

For example, to change the default logo for all forms and pages to an image called `my-logo.png`, place the image at the proper location and use the following property:

```xml
<property as="xs:anyURI"  name="oxf.fr.default-logo.uri.*.*">
    /forms/assets/my-logo.png
</property>
```

NOTE: Since Orbeon Forms 4.0, this property doesn't have an impact on the Form Builder logo. To override the Form Builder logo, you can use custom CSS (see the [`oxf.fr.css.custom.uri`](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-runner/styling/css/README.md#adding-your-own-css-files) configuration).

## Adding your own CSS

See the [CSS page](/form-runner/styling/css).

## Adding your own JavaScript

\[SINCE Orbeon Forms 4.4]

1. Place your JavaScript file(s) under one of the following recommended locations:
   * `WEB-INF/resources/forms/assets`: scripts for all forms
   * `WEB-INF/resources/forms/APP/assets`: scripts for app name APP
   * `WEB-INF/resources/forms/APP/FORM/assets`: scripts for app name APP and form name FORM
2. Define the [`oxf.fr.js.custom.uri`](#adding-your-own-adding-your-own-javascript-files) property to point to the file(s) you added. The path points to location under the `WEB-INF/resources` directory.

```xml
<property as="xs:string" name="oxf.fr.js.custom.uri.*.*">
    /forms/acme/assets/acme.js
    /forms/acme/sales/assets/acme-sales.js
</property>
```

You can add more than one file, and just separate the paths by whitespace in the property.

\[SINCE Orbeon Forms 2017.1]

In addition to [`oxf.fr.js.custom.uri`](#adding-your-own-adding-your-own-javascript-files), you can also use the following properties, which apply only to the Summary and Detail pages respectively:

* [`oxf.fr.summary.js.custom.uri`](/configuration/properties/form-runner/form-runner-summary-page#adding-your-own-javascript-files)
* [`oxf.fr.detail.js.custom.uri`](/configuration/properties/form-runner/form-runner-detail-page#adding-your-own-javascript-files)

## Overriding resources

In some cases, it might make sense to change some of the resources provided out of the box by Form Runner. For instance, the Detail page can have a submit button, which in English has a label "Submit". For your application, another label might make more sense, for instance "Send". To override Form Runner resources, you define properties with a name that has the following structure:

1. The name start with `oxf.fr.resource`.
2. Followed by the name of the application and form name for which you want to redefine the resource. You can use `*` for either if you want the redefinition to apply to all the applications or all the forms. For instance: `*.*`, or `my-app.my-form`.
3. The 2-letter code for the language for which you want to override the resource. For instance: `en`.
4. A dot-separated path corresponding to the path of the resource you want to override as defined by Form Runner [`resources.xml`](https://github.com/orbeon/orbeon-forms/blob/master/form-runner/jvm/src/main/resources/apps/fr/i18n/resources.xml).
5. Resources are aggressively caches, so you need to restart your application server (or redeploy the web app) after changing a property that overrides resources.

For instance, to change the label of the submit button to be "Send" in English for all applications and forms, write:

```xml
<property
    as="xs:string"
    name="oxf.fr.resource.*.*.en.detail.buttons.send"
    value='&lt;i class="icon-arrow-right"/&gt; Send'/>
```

This also works for resources that don't exist yet. For your own resources, start with a prefix specific to your company or project. For example:

```xml
<property
  as="xs:string"
  name="oxf.fr.resource.*.*.en.acme.my-resource-1"
  value="Resource 1 in English"/>

<property
  as="xs:string"
  name="oxf.fr.resource.*.*.fr.acme.my-resource-1"
  value="Resource 1 en français"/>
```

*NOTE: You can add new resources as shown above, but you cannot create new languages.*

### Date and time formats

Form Runner uses 3 date/time format resources to display dates and times throughout its user interface, including the Summary page, Home page, and form/data cards. The format used depends on how far in the past the date is:

* `within-day` - for dates within the last 24 hours
* `day-this-year` - for dates earlier this year
* `short-date` - for dates in previous years

The example below shows how to override all 3 formats to use an ISO-8601 style format `[Y0001]-[M01]-[D01] [H01]:[m01]:[s01]` for the English language (`en`). These formats are used for displaying Created and Last Modified timestamps in the Summary page, Last Modified timestamps in the Home page, and date information in the Published Forms and Form Data cards.

```xml
<property
    as="xs:string"
    name="oxf.fr.resource.*.*.en.common.formats.within-day"
    value="[Y0001]-[M01]-[D01] [H01]:[m01]:[s01]"/>
<property
    as="xs:string"
    name="oxf.fr.resource.*.*.en.common.formats.day-this-year"
    value="[Y0001]-[M01]-[D01] [H01]:[m01]:[s01]"/>
<property
    as="xs:string"
    name="oxf.fr.resource.*.*.en.common.formats.short-date"
    value="[Y0001]-[M01]-[D01] [H01]:[m01]:[s01]"/>
```

After setting these properties, make sure to restart your application server, as resources are aggressively cached for performance reasons.

## Email settings

See [Email](/configuration/properties/form-runner/form-runner-detail-page/form-runner-email).

## Sections and grids

### Appearance of repeated sections

\[SINCE Orbeon Forms 2016.1]

The following property allows you to set the appearance of repeated sections to `full` (the default) or `minimal` for all forms or for a subset of forms:

```xml
<property
    as="xs:string"
    name="oxf.xforms.xbl.fr.section.appearance.*.*"
    value="minimal"/>
```

See also the [`appearance`](/form-runner/component/section#repeated-mode) attribute of the [section component](/form-runner/component/section).

### Appearance of repeated grids

\[SINCE Orbeon Forms 2016.1]

The following property allows you to set the appearance of repeated grids to `full` (the default) or `minimal` for all forms or for a subset of forms:

```xml
<property
    as="xs:string"
    name="oxf.xforms.xbl.fr.grid.appearance.*.*"
    value="minimal"/>
```

See also the [`appearance`](/form-runner/component/grid#repeated-mode) attribute of the [grid component](/form-runner/component/grid).

### Insert position of repeated sections

\[SINCE Orbeon Forms 2016.2]

The following property allows you to select where new repetitions are added when using the "Add Another" or "+" button. Allowed values are `index` (default for the `full` appearance) and `bottom` (default for the `minimal` appearance):

```xml
<property
    as="xs:string"
    name="oxf.xforms.xbl.fr.section.insert.*.*"
    value="index"/>
```

See also the [`insert`](/form-runner/component/section#repeated-mode) attribute of the [section component](/form-runner/component/section).

### Insert position of repeated grids

\[SINCE Orbeon Forms 2016.2]

The following property allows you to select where new repetitions are added when using the "Add Another" or "+" button. Allowed values are `index` (default for the `full` appearance) and `bottom` (default for the `minimal` appearance):

```xml
<property
    as="xs:string"
    name="oxf.xforms.xbl.fr.grid.insert.*.*"
    value="index"/>
```

See also the [`insert`](/form-runner/component/grid#repeated-mode) attribute of the [grid component](/form-runner/component/grid).

### Section collapsing

\[SINCE Orbeon Forms 2016.1]

The following property allows you to set whether a section content can be collapsed by clicking on its title for all forms or for a subset of forms:

```xml
<property
    as="xs:boolean"
    name="oxf.xforms.xbl.fr.section.collapsible.*.*"
    value="false"/>
```

By default, sections are allowed to collapse.

The following property controls the same behavior in noscript mode:

\[UNTIL Orbeon Forms 2018.1]

```xml
<property
    as="xs:boolean"
    name="oxf.xforms.xbl.fr.section.noscript.collapsible.*.*"
    value="false"/>
```

A value of `false` may make sections more accessible and less confusing to screen reader users.

The following property controls the whether collapsing/opening of sections uses an animation. The default is `true`:

```xml
<property
    as="xs:boolean"
    name="oxf.xforms.xbl.fr.section.animate.*.*"
    value="false"/>
```

A value of `false` can be more efficient with slower browsers or large forms.

### Grid markup

#### Current behavior (Orbeon Forms 2022.1 and later)

By default, grids in your forms are rendered using the modern [CSS grid layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout) with `div` elements. This provides better compatibility with modern browsers and improved layout capabilities. If your custom CSS relies on the older HTML table-based structure (with `table`, `tr`, and `td` elements), you can revert to the previous behavior by setting the following property:

```xml
<property
    as="xs:string"
    name="oxf.xforms.xbl.fr.grid.markup.*.*"
    value="html-table"/>
```

#### Older versions (before Orbeon Forms 2022.1)

Before Orbeon Forms 2022.1, grids were rendered using HTML tables by default. This was because older browsers, in particular IE11, provided only limited support for CSS grids. Starting with Orbeon Forms 2020.1.7 and 2021.1.1, anticipating the drop of IE11 support, you could opt-in to use CSS grid layout by setting the following property. For more details on grid rendering in different versions, see the [Grids CSS documentation](/form-runner/styling/grids).

```xml
<property
    as="xs:string"
    name="oxf.xforms.xbl.fr.grid.markup.*.*"
    value="css-grid"/>
```

### Deprecated properties

Before Orbeon Forms 2016.1, you could use the following properties, deprecated since Orbeon Forms 2016.1. Section collapsing:

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.ajax.section.collapse.*.*"
    value="false"/>
```

Section collapsing in noscript mode:

\[UNTIL Orbeon Forms 2018.1]

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.noscript.section.collapse.*.*"
    value="false"/>
```

Section collapsing animation:

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.ajax.section.animate.*.*"
    value="true"/>
```

## Noscript properties

\[DEPRECATED SINCE Orbeon Forms 2016.3]

\[UNTIL Orbeon Forms 2018.1]

### Show noscript link

```xml
<property
    as="xs:boolean"
    name="oxf.fr.noscript-link.*.*"
    value="true"/>
```

Whether to show the link to the noscript/full version.

### Noscript: use table layout

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.noscript.table.*.*"
    value="true"/>
```

Whether forms in noscript mode are allowed to use a layout based on tables. If `false`, no tables are used. WYSIWYG is lost, but the form may be more accessible. The default is `true`.


# Detail page

## Children pages

* [Attachments](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments)
* [Email](/configuration/properties/form-runner/form-runner-detail-page/form-runner-email)
* [Initial data](/configuration/properties/form-runner/form-runner-detail-page/form-runner-initial-data)
* [PDF](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf)
* [Table of contents](/configuration/properties/form-runner/form-runner-detail-page/form-runner-toc)

## HTML page layout

\[SINCE Orbeon Forms 2019.2]

Available modes:

* `fixed` (default)
* `fluid`

When in `fluid` mode, the form sections and grids take the entire web browser's viewport size. This also applies when using the [wizard view](/form-runner/features/wizard-view).

This can also be configured for a particular form in Form Builder's Form Settings dialog.

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.html-page-layout.*.*"
    value="fixed"/>
```

## Density

[\[SINCE Orbeon Forms 2024.1\]](/release-notes/orbeon-forms-2024.1)

Available values:

* `compact`: This is the current default with Orbeon Forms until 2023.1.
* `comfortable`: This is the default for forms created with Orbeon Forms 2024.1 and later.
* `roomy`: Choose this for even more spacing between controls.

This sets the spacing between elements in the interface.

This can also be configured for a particular form in Form Builder's Form Settings dialog.

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.density.*.*"
    value="compact"/>
```

See also:

* Blog post: [Form Density Setting](https://www.orbeon.com/2024/11/form-density)

## Color scheme

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)

Available values:

* `light` (default): Light color scheme.
* `dark`: Dark color scheme.
* `system`: Automatically selects between light and dark schemes based on browser/system settings.

This property allows you to switch between the light and dark color schemes.

*NOTE: This is a work in progress, and enhancements will be included in future releases. At the moment, Form Builder only supports the light color scheme.*

For Form Runner Detail pages, use:

```xml
<property
    as="xs:string"
    name="oxf.fr.style.color-scheme.*.*"
    value="dark"/>
```

For other pages (Summary, Form Builder Forms, Published Forms, Forms Admin, and Landing pages), use the property without wildcards:

```xml
<property
    as="xs:string"
    name="oxf.fr.style.color-scheme"
    value="dark"/>
```

![Dark color scheme](/files/OkPNXeBiIK4c3fxDTTLr)

## Adding your own CSS files

\[SINCE Orbeon Forms 2017.1]

In addition to `oxf.fr.css.custom.uri`, you can also use the following property, which apply only to the Detail page:

```xml
<property as="xs:string" name="oxf.fr.detail.css.custom.uri.*.*">
    /forms/acme/assets/acme-detail.css"
</property>
```

See also [Adding your own CSS](/configuration/properties/form-runner#adding-your-own-css).

## Adding your own JavaScript files

\[SINCE Orbeon Forms 2017.1]

In addition to `oxf.fr.js.custom.uri`, you can also use the following property, which apply only to the Detail page:

```xml
<property as="xs:string" name="oxf.fr.detail.js.custom.uri.*.*">
    /forms/acme/assets/acme-detail.js"
</property>
```

See also [Adding your own JavaScript](/configuration/properties/form-runner#adding-your-own-javascript).

## Table of contents

See [Table of contents configuration properties](/configuration/properties/form-runner/form-runner-detail-page/form-runner-toc).

## Position of error summary

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.error-summary.*.*"
    value="bottom"/>
```

Where to place the error summary: `top`, `bottom`, `both`, or `none`.

## Buttons on the Detail page

### Choosing which buttons are shown

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.buttons.*.*"
    value="close clear print pdf save submit"/>
```

The property configures which buttons are included on the Detail page, and in what order they are shown. For more information, see [Buttons and Processes](/form-runner/advanced/buttons-and-processes).

### Hiding and disabling buttons

\[SINCE Orbeon Forms 2016.2]

The following properties, where you replace `BUTTON` by a specific button name, control whether a particular button is visible (button visibility) or disabled (or "readonly"):

```xml
oxf.fr.detail.button.BUTTON.visible.*.*
```

```xml
oxf.fr.detail.button.BUTTON.enabled.*.*
```

The value of these properties is an XPath expression. For example the following properties hide, show, and disable buttons depending on whether the wizard shows its table of contents or its body (as of Orbeon Forms 2022.1):

```xml
  <property as="xs:string"  name="oxf.fr.detail.button.wizard-prev.visible.*.*"               >
      fr:owns-lease-or-none-required() and fr:is-wizard-body-shown()
  </property>
  <property as="xs:string"  name="oxf.fr.detail.button.wizard-prev.enabled.*.*">
      not(fr:is-wizard-first-page())
  </property>
  <property as="xs:string"  name="oxf.fr.detail.button.wizard-next.visible.*.*">
      fr:owns-lease-or-none-required() and fr:is-wizard-body-shown()
  </property>
  <property as="xs:string"  name="oxf.fr.detail.button.wizard-next.enabled.*.*">
      not(fr:is-wizard-last-page())
  </property>
  <property as="xs:string"  name="oxf.fr.detail.button.wizard-toc.visible.*.*">
      fr:owns-lease-or-none-required() and fr:is-wizard-separate-toc() and fr:is-wizard-body-shown()
  </property>
```

You can access control values in the data in the following ways, where `foo` is the name of the control:

* Use an expression of the type `//foo`. Note that control elements might not be unique in case of repeats or section templates, and so this returns as many XML elements as there are values in the data, including within repeats and within section templates.
* Use `fr:control-string-value('foo')`. This only works for controls that are not in a section template and returns zero or one value. If the control is repeated, only the first value is returned.
* \[SINCE Orbeon Forms 2023.1.6] You can use the variable notation `$foo` for controls that are not in a section template. If the control is repeated, only the first value is returned.

Example searching data elements:

```xml
<property as="xs:string"  name="oxf.fr.detail.button.save-final.visible.*.*">
    xxf:non-blank(//foo)
</property>
```

Example with `fr:control-string-value()`:

```xml
<property as="xs:string"  name="oxf.fr.detail.button.save-final.visible.*.*">
    xxf:non-blank(fr:control-string-value('foo'))
</property>
```

### Loading indicator for buttons

\[SINCE Orbeon Forms 2016.1]

The property `oxf.fr.detail.loading-indicator.BUTTON.*.*`, where you replace `BUTTON` by a specific button name, allows you to configure which loading indicator, if any, is to be used for that button. The value of the property can be either:

* Empty, which is the default, and means "no loading indicator".
* `modal`, greys out the background, shows a spinner in the center of the screen, and prevents any user input as long as the action triggered by the button is being processed.
* `inline`, shows a spinner inside the button itself.

In the following example, the `send` button is made modal:

```xml
<property 
    as="xs:string"  
    name="oxf.fr.detail.loading-indicator.send.*.*"                 
    value="modal"/>
```

In general, we would expect this property to be used as follows:

* `modal` for buttons performing actions for which allowing users to change the value of fields after the button is pressed wouldn't make any sense, would be confusing, or outright dangerous. This would for instance be the case for *submit* or *publish* buttons.
* `inline` for buttons performing actions that are expected to take a little bit of time, like a *save* operation.
* Empty for any other button.

In all cases, should an action take any noticeable amount of time, Orbeon Forms will always show a loading bar at the top of the page, so users know one of their actions is being processed.

By default, as shown in the below video:

* The `modal` loading indicator used for the `submit` button.
* The `inline` loading indicator for the *save* buttons (`save-draft` (up to 2020.1.x), `save-progress` (2021.1 and newer), and `save-final`).

![Loading indicators](/files/-LEkBwGjf5eBo6n7Dci4)

## Controlling the appearance of control labels

\[SINCE Orbeon Forms 2016.2]

By default, with Form Runner, control labels appear *inline* above the control. The following property allows overriding this behavior:

```xml
<property
    as="xs:string"
    name="oxf.xforms.label.appearance.*.*"
    value="full"/>
```

Allowed values:

* `full`: labels show inline above the control (the default)
* `full minimal`: labels show inline above the control, but for text, date, and time input fields only, labels show as an HTML *placeholder* within the field when the field is empty

*LIMITATION: The `minimal` appearance is not supported on combined "Date and Time" fields and on text fields with "Character Counter" appearance.*

*NOTE: Only one `minimal` appearance can be used between `oxf.xforms.label.appearance` and `oxf.xforms.hint.appearance`. If both include `minimal`, the label wins.*

For more about placeholders, see [Use HTML5 placeholders, in XForms](https://blog.orbeon.com/2012/01/use-html5-placeholders-in-xforms.html).

## Controlling the appearance of control hints

\[SINCE Orbeon Forms 2016.2]

By default, with Form Runner, control hints appear *inline* under the control. The following property allows overriding this behavior:

```xml
<property
    as="xs:string"
    name="oxf.xforms.hint.appearance.*.*"
    value="full"/>
```

Allowed values:

* `full`: hints show inline below the control (the default)
* `full minimal`: hints show inline below the control, but for text, date, and time input fields only, hints show as an HTML *placeholder* within the field when the field is empty
* `tooltip`: hints show as tooltips upon mouseover
* `tooltip minimal`: hints show as tooltips upon mouseover, but for input fields only, hints show as an HTML *placeholder* within the field when the field is empty

Here is how hints appear depending on the type of control they are associated with:

![](/files/-LEkBtUVPhNrIEbjNXU9)

*LIMITATION: The `minimal` appearance is not supported on combined "Date and Time" fields and on text fields with "Character Counter" appearance.*

*NOTE: Only one `minimal` appearance can be used between `oxf.xforms.label.appearance` and `oxf.xforms.hint.appearance`. If both include `minimal`, the label wins.*

For more about placeholders, see [Use HTML5 placeholders, in XForms](https://blog.orbeon.com/2012/01/use-html5-placeholders-in-xforms.html).

## Display hints inline

\[DEPRECATED SINCE Orbeon Forms 2016.2]

This property set whether the control hints are shown inline, rather than as tool-tips. The default is `true`.

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.hints.inline.*.*"
    value="true"/>
```

Since Orbeon Forms 2016.2, this property is deprecated. Use `oxf.fr.detail.hint.appearance` instead. For backward compatibility, when this property is present, it overrides `oxf.xforms.hint.appearance` and sets it to:

* `full` if set to `true`
* `tooltip` if set to `false`

## Order of LHHA elements

\[SINCE Orbeon Forms 2016.2]

This property sets the respective order, in the generated HTML markup, of label/help/hint/alert and the control element.

*NOTE: It is not recommended to change the default value of this property, which was introduced in the days where CSS couldn't do all it can do now. We recommend styling using CSS instead.*

```xml
<property 
    as="xs:string" 
    name="oxf.fr.detail.lhha-order.*.*"                               
    value="help label control alert hint"/>
```

## Initial keyboard focus

\[SINCE Orbeon Forms 4.9]

This property controls whether Form Runner attempts to set focus on the first control upon form load. The default is `true`.

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.initial-focus.*.*"
    value="true"/>
```

In some cases, such as [embedding](/form-runner/link-embed/java-api), it can be desirable to disable this by setting the property to `false`.

## Focusable controls

\[SINCE Orbeon Forms 2016.3]

The following properties determine which control types are focusable in in the following scenarios:

* initial focus (if enabled by `oxf.fr.detail.initial-focus`)
* switching sections in the table of contents
* switching sections in the wizard table of contents or navigation
* clearing the form data with the "Clear" button
* moving, inserting, or deleting repetitions in repeated grids and sections

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.focus.includes.*.*"
    value=""/>
    
<property
    as="xs:string"
    name="oxf.fr.detail.focus.excludes.*.*"
    value="xf:trigger"/>
```

Until Orbeon Forms 2016.2, only Text Fields (`<xf:input>`) were focusable in these cases. Since Orbeon Forms 2016.3, the default is to allow focus on any input control, including text fields, text areas, dropdown menus, and more. However, buttons are explicitly excluded.

The values of these properties follow the [`include` and `exclude` attributes](/xforms/core/focus#includes-and-excludes) on the `<xf:setfocus>` action.

## Validation of static lists of choices

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

This property allows you to automatically add a validation error when a static list of choices contains invalid values. The default is `false`.

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.validate-selection-controls-choices.*.*"
    value="true"/>
```

This can be useful in the following cases:

* to catch errors where selection control values are set using calculations
* to validate work in progress data added with the [Persistence API](/form-runner/api/persistence/crud)

This property might be enabled by default in the future.

This is also automatically enabled when importing form data through the Import page.

## Validation mode

\[SINCE Orbeon Forms 2016.3]

The following property controls whether validation happens as the user types or explicitly when activating a button:

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.validation-mode.*.*"
    value="explicit"/>
```

Values:

* `incremental`: validate as the user types (default)
* `explicit`: validate upon explicit activation of a button

The main purpose of the `explicit` mode is to mimic old-style forms, where validation traditionally happened upon pressing a "Submit" button.

By default, in `explicit` mode, validation occurs:

* when the `validate` Form Runner action runs
* with the Wizard view, in validated mode, when the user attempts to navigate to the next page or select a page in the wizard's table of contents

## Captcha

See [Captcha components](/form-runner/component/captcha).

## Running processes upon page load

\[SINCE Orbeon Forms 2017.2]

Running processes in the background is an Orbeon Forms PE feature.

The following property controls what process(es) to run when the page loads in "new" or "edit" mode:

```
oxf.fr.detail.process.
  after-controls|after-data|before-data.
  background|foreground.
  new|edit|view|pdf|email.
  $app.
  $form
```

where `$app` and `$form` represent a Form Runner application name and/or form name or `*` wildcards, as is usual with Form Runner configuration properties.

The following process names apply:

* `after-controls`: run after the controls are ready:
  * The process runs when controls are "live", which means you can set their values and list of choices.
* `after-data`: run when the data is ready:
  * It has been loaded from the database if needed ("edit" mode).
  * Its initial values and calculations are up to date.
* `before-data`: run before the data's initial values are calculated:
  * The process runs before the data is ready.
  * You cannot set control values and list of choices as a result.

*WARNING: When running the process upon `after-data`, controls are not yet initialized. Because data validation depends on controls being present, data validation does not function in this case. If you need to validate data, for example before saving it or sending it, you must use the `after-controls` process name.*

Background options:

* `background`: run only in the background, that is within a service such as the ["run form in the background"](/form-runner/api/other-apis/run-form-background) service
* `foreground`: run only in the foreground, that is when the user is interacting with the page
* `*`: run in both cases

Mode options:

* `new`: run in "new" mode only
* `edit`: run in "edit" mode only
* etc.
* `*`: run in all modes

*NOTE: When running in the background, only the `new` and `edit` modes are supported.*

See also [Run form in the background](/form-runner/api/other-apis/run-form-background).

## Warning the user when data is unsafe

\[SINCE Orbeon Forms 2018.2]

When data is *unsafe*, meaning that is has been modified but not saved yet, Form Runner by default shows a warning when attempting to navigate away from the current page or to close the current browser tab or window.

<figure><img src="/files/-LPcEBRtt44POZiqUWHf" alt="" width="490"><figcaption><p>Chrome warning when leaving a page</p></figcaption></figure>

In some cases, in particular when [embedding a form](/form-runner/link-embed/java-api), this can be an inconvenience. The following property allows disabling this behavior.

\[SINCE Orbeon Forms 2021.1] In addition to `true` or `false`, since the value of the property is an [AVT](https://github.com/orbeon/orbeon-forms-doc/blob/master/xforms/core/attribute-value-templates.md), you can also, when needed, dynamically disable the warning by providing an expression between curly braces. For instance the following would only warn users if the content of the field named `description` has more than 50 characters and if users have made changes to the form since it was loaded:

```xml
<property 
    as="xs:string" 
    name="oxf.fr.detail.warn-when-data-unsafe.*.*"
    value="{string-length(fr:control-string-value('description')) > 50}"/>
```

\[UNTIL Orbeon Forms 2020.1] The type of the property must be `xs:boolean`, and, consequently, the value must be either `true` or `false`.

```xml
<property 
    as="xs:boolean" 
    name="oxf.fr.detail.warn-when-data-unsafe.*.*"
    value="false"/>
```

See also [the `set-data-status` action](/form-runner/advanced/buttons-and-processes/actions-form-runner#set-data-status).

## Warning the user when data has changed since initially loaded

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)

When multiple users edit the same form instance simultaneously, or when a single user edits the same form from different browser tabs or sessions, data conflicts can occur. By default, Orbeon Forms will overwrite previously saved data without warning, which can result in lost changes.

It is possible to configure Form Runner to check whether the data has changed since initially loaded by setting the following property to `save-only`:

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.conflict-detection.*.*"
    value="save-only"/>
```

When the conflict detection feature is enabled, saving the data will trigger a check to determine whether the data has changed since the form was loaded.

If no conflicts are detected, saving proceeds as usual. If a conflict is detected, users are presented with a dialog offering two options:

* Save and overwrite: Proceeds with saving the current user's changes, overwriting any changes made by other sessions or tabs
* Discard and reload: Discards the current user's unsaved changes and reloads the form with the latest data from the database

<figure><img src="/files/qnpEN9yk8bRxGKBzZYsx" alt="" width="510"><figcaption><p>Conflict detected</p></figcaption></figure>

### Limitations

Currently, conflict detection only occurs when saving data (`save-only` mode). Future versions may include polling-based conflict detection to warn users of conflicts before they attempt to save.

## Initial data

See [Initial data properties](/configuration/properties/form-runner/form-runner-detail-page/form-runner-initial-data).

## View mode

### Buttons on the view page

You configure which buttons are shown on the view page with the following property:

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.buttons.view.*.*"
    value="back workflow-edit pdf"/>
```

You can use all the buttons available on the Detail page. In addition, the following buttons apply:

* `workflow-edit`
  * Label: "Edit"
  * Action: Navigate back to the Detail page in "edit" mode.

### Showing alerts in view and PDF modes

\[SINCE Orbeon Forms 2019.1]

The following property allows you to show alerts in the `view` and `pdf` modes. By default, the value is `false` and the alerts do not show.

```xml
<property 
    as="xs:boolean" 
    name="oxf.fr.detail.static-readonly-alert.*.*"
    value="true"/>
```

*NOTE: Alerts show under the fields as usual. Setting this property to `true` doesn't cause the Error Summary to show.*

### Showing hints in view and PDF modes

\[SINCE Orbeon Forms 2017.1]

The following property allows you to show hints in the `view` and `pdf` modes. By default, the value is `false` and the hints do not show.

```xml
<property 
    as="xs:boolean" 
    name="oxf.fr.detail.static-readonly-hint.*.*"
    value="true"/>
```

### Calculations in readonly modes

\[SINCE Orbeon Forms 2021.1]

The following property allows disabling Calculated Value formulas in readonly modes (Review, PDF). By default, the value is `false` and the calculations take place.

```xml
<property 
    as="xs:boolean" 
    name="oxf.fr.detail.readonly.disable-calculate.*.*"
    value="true"/>
```

See also [Form Settings](/form-builder/form-settings#formulas).

### Grid tab order

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

You can configure the tab order in grids with the following property:

```xml
<property 
    as="xs:string" 
    name="oxf.xforms.xbl.fr.grid.tab-order.*.*"
    value="columns"/>
```

Valid values are `rows` and `columns`.

See also [Grid Tab Order](/form-builder/form-editor/grid-settings#grid-tab-order) in the Grid Settings dialog.

## PDF mode

See [PDF configuration properties](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf).

## See also

* [Summary page configuration properties](/configuration/properties/form-runner/form-runner-summary-page)


# Attachments

## Maximum attachment size

\[SINCE Orbeon Forms 2017.1]

The following property sets the maximum size in bytes of an uploaded attachment. For example, if you set it to `1000000` (1 MB), and the user attempts to upload a larger attachment, an error is reported.

```xml
<property 
    as="xs:string"  
    name="oxf.fr.detail.attachment.max-size-per-file.*.*"                      
    value="1000000"/>
```

If the value is blank (the default), then the value of the following backward compatibility property is used:

```xml
<property
    as="xs:integer" 
    processor-name="oxf:request"   
    name="max-upload-size"          
    value="100000000"/>
```

The value of `oxf.fr.detail.attachment.max-size-per-file` can be overridden:

* for a specific form, from the Form Builder "Form Settings" dialog
* for a specific control, using a common constraint in the Form Builder "Control Settings" dialog

This property was previously named `oxf.fr.detail.attachment.max-size`. The old name is still supported for backward compatibility.

## Maximum aggregate attachment size (forms)

\[SINCE Orbeon Forms 2017.1]

The following property sets the maximum aggregate size in bytes of all uploaded attachments for a given instance of form data. For example, if you set it to `1000000` (1 MB), and the form has two attachment controls, and you upload a 600 KB attachment using the first control, then only 400 KB can be uploaded using the second control, even if a larger maximum size per control was set using the `oxf.fr.detail.attachment.max-size-per-file` property or a Form Builder setting. If you attempt to upload a larger attachment, an error is reported.

```xml
<property 
    as="xs:string"  
    name="oxf.fr.detail.attachment.max-size-aggregate-per-form.*.*"                      
    value="10000000"/>
```

The value of `oxf.fr.detail.attachment.max-size-aggregate-per-form` can be overridden:

* for a specific form, from the Form Builder "Form Settings" dialog

This property was previously named `oxf.fr.detail.attachment.max-size-aggregate`. The old name is still supported for backward compatibility.

## Maximum aggregate attachment size (controls)

[\[SINCE Orbeon Forms 2024.1\]](/release-notes/orbeon-forms-2024.1)

The following property sets the maximum aggregate size in bytes of all uploaded attachments for each individual attachment control. This property will typically be used to limit the total size of attachments for multiple attachment controls, although it will also be checked for single attachment controls. If you attempt to upload a larger attachment, an error is reported.

```xml
<property 
    as="xs:string"  
    name="oxf.fr.detail.attachment.max-size-aggregate-per-control.*.*"                      
    value="10000000"/>
```

The value of `oxf.fr.detail.attachment.max-size-aggregate-per-control` can be overridden:

* for a specific control, from the Form Builder "Control Settings" dialog

## Maximum number of files (controls)

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)

The following property sets the maximum number of files that can be uploaded for each individual multiple attachment control. If you attempt to upload more files than allowed, an error is reported.

```xml
<property 
    as="xs:string"  
    name="oxf.fr.detail.attachment.max-files-per-control.*.*"                      
    value="10"/>
```

The value of `oxf.fr.detail.attachment.max-files-per-control` can be overridden:

* for a specific form, from the Form Builder "Form Settings" dialog
* for a specific control, from the Form Builder "Control Settings" dialog

## Allowed file types

\[SINCE Orbeon Forms 2017.1]

The following property specifies which file types (also known as "mediatypes") are allowed for attachments. For example, the following values of `image/png image/jpeg` specify that JPEG images and PDF files are allowed but no other files.

```xml
<property 
    as="xs:string"  
    name="oxf.fr.detail.attachment.mediatypes.*.*"                    
    value="image/jpeg application/pdf"/>
```

*NOTE: This property impacts regular Form Runner file attachments, but also Form Runner image attachments. If you have image attachments and they were created with a build of Orbeon Forms prior to 2017.1, make sure to include `image/*` in that property as well or new image attachments entered by users will be rejected. Image attachments created with 2017.1 or newer limit the mediatypes to `image/*` out of the box. You can also edit the form definition and set explicitly, on each image attachment control, the "Supported File Types" validation.*

The format is as follows:

* the value is a list of space- or comma-separated mediatype ranges
* a mediatype range is one of:
  * `*/*`: all mediatypes allowed
  * `type/*`: all mediatypes with prefix `type` are allowed (for example `image/*`)
  * `type/subtype`: specific mediatype such as `image/jpeg`, `application/atom+xml`, `video/mp4`, etc.

The value of `oxf.fr.detail.attachment.mediatypes` can be overridden:

* for a specific form, from the Form Builder "Form Settings} dialog
* for a specific control, using a common constraint the Form Builder "Control Settings" dialog

## Upload error appearance

[\[SINCE Orbeon Forms 2025.1.1\]](/release-notes/orbeon-forms-2025.1.1)

When a file upload is rejected (for example, because the file is too large or has a disallowed mediatype), by default Orbeon Forms shows the error in a dialog. With this property, you can choose to show the error message inline, directly inside the attachment control.

```xml
<property
    as="xs:string"
    name="oxf.xforms.xbl.fr.attachment.upload-error-appearance.*.*"
    value="dialog"/>
```

| Value    | Description                                               |
| -------- | --------------------------------------------------------- |
| `dialog` | Show upload errors in a dialog (default).                 |
| `inline` | Show upload errors inline, inside the attachment control. |

<figure><img src="/files/ikB1B6ZBEMyw6hKzrLss" alt="" width="449"><figcaption><p>Inline upload error</p></figcaption></figure>

The value of `oxf.xforms.xbl.fr.attachment.upload-error-appearance` can be overridden:

* for a specific form, from the Form Builder "Form Settings" dialog
* for a specific control, from the Form Builder "Control Settings" dialog

## See also

* Configuration properties
  * [Detail page](/configuration/properties/form-runner/form-runner-detail-page)
  * [Email](/configuration/properties/form-runner/form-runner-detail-page/form-runner-email)
  * [Initial data](/configuration/properties/form-runner/form-runner-detail-page/form-runner-initial-data)
  * [PDF](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf)
  * [Table of contents](/configuration/properties/form-runner/form-runner-detail-page/form-runner-toc)
* Blog post: [Improved constraints on attachments uploads](https://blog.orbeon.com/2017/04/improved-constraints-on-attachments.html)


# Email properties

## Email transport configuration

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)

You can choose between SMTP and SendGrid as the email transport mechanism:

```xml
<property
    as="xs:string"
    name="oxf.fr.email.transport.*.*"
    value="sendgrid"/>
```

Supported values:

* `smtp`: Use SMTP server (default, and the only transport available before Orbeon Forms 2025.1)
* `sendgrid`: Use SendGrid API

### SMTP

The following properties control the connection to the SMTP server when using the `smtp` transport:

* `host`: required SMTP host name
* `port`: optional SMTP port override. If not specified, the defaults are:
  * plain SMTP: 25
  * TLS: 587
  * SSL: 465
* `encryption`:
  * blank: none (plain SMTP)
  * `tls`: use TLS
  * `ssl`: use SSL
* `username`: SMTP username (required if TLS or SSL is used, optional otherwise)
* `credentials`: SMTP password

```xml
<property
    as="xs:string"
    name="oxf.fr.email.smtp.host.*.*"
    value="my.outgoing.smtp.server.org"/>

<property
    as="xs:string"
    name="oxf.fr.email.smtp.port.*.*"
    value="587"/>

<property
    as="xs:string"
    name="oxf.fr.email.smtp.encryption.*.*"
    value="tls"/>

<property
    as="xs:string"
    name="oxf.fr.email.smtp.username.*.*"
    value="jdoe"/>

<property
    as="xs:string"
    name="oxf.fr.email.smtp.credentials.*.*"
    value="secret"/>
```

\[SINCE Orbeon Forms 2026.1]

The following optional properties control SMTP timeouts:

* `connection-timeout`: socket connection timeout in milliseconds (by default, no timeout)
* `timeout`: socket read timeout in milliseconds (by default, no timeout)
* `write-timeout`: socket write timeout in milliseconds (by default, no timeout)
* `quit-wait`: whether to wait for the server's response to the `QUIT` command (`xs:boolean`, default: `true`)

### SendGrid

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)

When using SendGrid as the email transport, you need to configure your SendGrid API key:

```xml
<property
    as="xs:string"
    name="oxf.fr.email.transport.*.*"
    value="sendgrid"/>

<property
    as="xs:string"
    name="oxf.fr.email.sendgrid.api-key.*.*"
    value="YOUR_SENDGRID_API_KEY"/>
```

The `api-key` property is required when using the SendGrid transport. You can obtain your API key from the SendGrid dashboard. All other email configuration properties (addresses, attachments, etc.) work the same way regardless of which transport you choose.

## Email addresses properties

* `from`:
  * sender email address(es) appearing in the email sent
* `reply-to`:
  * SINCE Orbeon Forms 2020.1
  * message `Reply-To` address(es)
* `to`:
  * recipient email address(es) of the email sent
* `cc`:
  * SINCE Orbeon Forms 2017.1
  * carbon copy recipient email address(es) of the email sent
* `bcc`:
  * SINCE Orbeon Forms 2017.1
  * blind carbon copy email address(es) of the email sent

List of emails are space- or comma- separated.

```xml
<property
    as="xs:string"
    name="oxf.fr.email.from.*.*"
    value="john@example.org"/>

<property
    as="xs:string"
    name="oxf.fr.email.reply-to.*.*"
    value="john@acme.org"/>

<property
    as="xs:string"
    name="oxf.fr.email.to.*.*"
    value="mary@example.org,nancy@example.org"/>
    
<property
    as="xs:string"
    name="oxf.fr.email.cc.*.*"
    value="mary@example.org,nancy@example.org"/>

<property
    as="xs:string"
    name="oxf.fr.email.bcc.*.*"
    value="mary@example.org,nancy@example.org"/>
```

## Format of email addresses

In the examples above, only raw email addresses are shown. Multiple addresses must be separated by commas.

\[SINCE Orbeon Forms 2021.4]

The configuration properties can now contain a name, following the standard syntax:

```
John Smith <john@example.org>
```

Or:

```
"John Smith" <john@example.org>
```

Keep in mind that if the addresses are stored in an XML attribute in your `properties-local.xml`, you need to escape some characters:

```xml
<property
    as="xs:string"
    name="oxf.fr.email.from.*.*"
    value="John Smith &lt;john@example.org&gt;"/>
```

Similarly, if you use quotes, you need to escape them:

```xml
<property
    as="xs:string"
    name="oxf.fr.email.from.*.*"
    value="&quot;John Smith&quot; &lt;john@example.org&gt;"/>
```

When more than one email is present, if you specify names, you must use commas exclusively as a separator (with whitespace allowed around commas):

```xml
<property
    as="xs:string"
    name="oxf.fr.email.from.*.*"
    value="&quot;John Smith&quot; &lt;john@example.org&gt;, Alice &lt;alice@acme.org&gt;"/>
```

## Attachment properties

* `attach-pdf`: whether the PDF representation is attached to the email
* `attach-tiff`: whether the TIFF representation is attached to the email
* `attach-xml`: whether the XML data is attached to the email

```xml
<property
    as="xs:boolean"
    name="oxf.fr.email.attach-pdf.*.*"
    value="true"/>

<property
    as="xs:boolean"
    name="oxf.fr.email.attach-tiff.*.*"
    value="true"/>

<property
    as="xs:boolean"
    name="oxf.fr.email.attach-xml.*.*"
    value="true"/>
```

\[SINCE Orbeon Forms 2016.1]

The following property controls whether file and image form attachments are attached to the email.

* `all`: all form attachments are included (this is the default)
* `none`: no form attachments is included
* `selected`: only form attachments selected in the Form Builder with "Include as Email Attachment" are included

```xml
<property
    as="xs:string"
    name="oxf.fr.email.attach-files.*.*"
    value="all"/>
```

\[SINCE Orbeon Forms 2018.1]

The following properties control the name of the PDF, TIFF and XML attachments:

* `oxf.fr.email.pdf.filename`:
  * filename of the PDF attachment, when present
* `oxf.fr.email.tiff.filename`
  * filename of the TIFF attachment, when present
* `oxf.fr.email.xml.filename`
  * filename of the XML attachment, when present

The property contains an XPath expression which generates the filename. The expression runs in the context of the current form data but does *not* have access to controls. Only a limited set of Form Runner XPath functions can be used, in particular:

* `fr:form-title()`
* `fr:app-name()`
* `fr:form-name()`
* `fr:form-version()`
* `fr:document-id()`
* `fr:mode()`
* `fr:is-readonly-mode()`
* `fr:is-design-time()`

*NOTE: Control values must be extracted by searching for element values within the XML document. In the future, we hope to provide a function for that purpose.*

```xml
<property as="xs:string" name="oxf.fr.email.pdf.filename.*.*">
    concat(
        fr:form-title(),
        ' - ',
        //case-id,
        '.pdf'
    )
</property>

<property as="xs:string" name="oxf.fr.email.tiff.filename.*.*">
    concat(
        fr:form-title(),
        ' - ',
        //case-id,
        '.tiff'
    )

<property as="xs:string" name="oxf.fr.email.xml.filename.*.*">
    concat(
        fr:form-title(),
        ' - ',
        //case-id,
        '.xml'
    )
</property>
```

## Email subject and body

NOTE: Since Orbeon Forms 2018.1, you can set a form's email subject and body in the Form Builder user interface. You can also use template placeholders. See [Email settings](/form-builder/advanced/email-settings).

With any Orbeon Forms version, the following properties can be used to set default and per app/form email subject and body templates.

```xml
<property 
    as="xs:string"
    name="oxf.fr.resource.*.*.en.email.subject"
    value="Here is your confirmation: "/>

<property 
    as="xs:string"
    name="oxf.fr.resource.*.*.en.email.body"
    value="Hi, here is an email from Orbeon Forms!"/>
```

## Styling HTML emails

\[SINCE Orbeon Forms 2019.1]

When using HTML for an email body template in [Email settings](/form-builder/advanced/email-settings), you can provide inline CSS that will be included in the email messages. For example"

```xml
<property as="xs:string" name="oxf.fr.email.css.custom.inline.*.*">
    ul li { list-style-type: none; margin-left: 0; }
</property>
```

This is known to work with Gmail at least.

## See also

* [Email settings](/form-builder/advanced/email-settings)
* Configuration properties
  * [Detail page](/configuration/properties/form-runner/form-runner-detail-page)
  * [Attachments](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments)
  * [Initial data](/configuration/properties/form-runner/form-runner-detail-page/form-runner-initial-data)
  * [PDF](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf)
  * [Table of contents](/configuration/properties/form-runner/form-runner-detail-page/form-runner-toc)


# Initial data

When creating new form data (for instance going to the URL `/fr/orbeon/bookshelf/new`), the initial data in the form can come from the following sources:

1. Data specified in Form Builder when creating the form definition:
   * statically, by entering data in fields in Form Builder
   * dynamically, by using the "Initial Value" formula in "Control Settings"
2. Data `POST`ed to the "new form" page.
3. Data retrieved by calling a service.

## Initial data posted to the page

The data provided in the form definition is used by default, and the `POST`ed data is used if present.

Initial data can be `POST`ed in two ways:

1. As a direct `POST` of the XML document
2. As an HTML form `POST` parameter called `fr-form-data`

For #2, this behaves as if a browser was submitting an HTML form that looks like the following, with the value of the `fr-form-data` request parameter being the Base64-encoded XML document:

```xml
<form method="post" action="/path/to/new">
    <input type="hidden" name="fr-form-data" value="Base64-encoded XML"/>
</form>
```

\[SINCE Orbeon Forms 4.8]

The format of the data follows the Orbeon Forms 4.0.0 format by default. You can change this behavior to `POST` data in the latest internal format by specifying the `data-format-version=edge` request parameter. This is useful if you obtained the data from, for example, a [`send()` action](/form-runner/advanced/buttons-and-processes/actions-form-runner#send) using `data-format-version = "edge"`.

Use the authorization mechanism for services (see [Authorization of pages and services](/xml-platform/controller/authorization-of-pages-and-services)) to enable submitting initial instances to the new page:

* Your external application must provide credentials (e.g. BASIC authorization, a secret token, etc.) when `POST`ing to Form Runner. -Your authorizer service must validate those credentials.

\[SINCE Orbeon Forms 2017.1]

If `data-format-version=edge` is *not* specified, then the data `POST`ed is assumed to be in 4.0.0 format.

\[SINCE Orbeon Forms 2022.1]

When `POST`ing data as described above, the data can now be incomplete. Say the 4.0.0 format of your form data is:

```xml
<form>
  <contact>
    <first-name/>
    <last-name/>
    <email/>
    <phone/>
  </contact>
  <message>
    <order-number/>
    <topic/>
    <comments/>
  </message>
</form>
```

Let's say that you just want to pass the `<last-name>` and `<order-number>` comments. You can now just `POST`:

```xml
<form>
  <contact>
    <last-name>Washington</last-name>
  </contact>
  <message>
    <order-number>3141592</order-number>
  </message>
</form>
```

All other elements are automatically added.

*NOTE: If the `POST`ed data contains extra XML elements in no namespace that are not supported by the form, an error is returned. However, extra XML elements in a custom namespace are allowed.*

*Compatibility: If the data posted contains extra elements in no namespace, those elements were ignored prior to Orbeon Forms 2022.1. With Orbeon Forms 2022.1 and newer, their presence causes an error.*

## Initial data from service

With the following properties, you can configure Form Runner to call an HTTP service instead of using the default instance provided as part of the form:

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.new.service.enable.*.*"
    value="false"/>

<property
    as="xs:string"
    name="oxf.fr.detail.new.service.uri.*.*"
    value="/fr/service/custom/my-app/new"/>
```

Set the first property above to `true` to enable this behavior and have the second property point to your service.

The service is called with a `GET` HTTP method.

The service must either:

* return a successful HTTP response containing XML data in the `4.0.0` format for the given form
* return an empty body, in which case no error is produced (see also issue [#3935](https://github.com/orbeon/orbeon-forms/issues/3935))
* return an error HTTP response or malformed XML response, in which case an error is produced and the form doesn't initialize

The following property defines a space-separated list of request parameters to be passed to the service. Say the new page was invoke with request parameters `foo=42` and `bar=84`, if you set the value of this property to `foo bar`, these two request parameters will be passed along as request parameters to the service. The request parameters can either get to the new page in a `POST` or `GET` request. The service is always called with a `GET`, consequently request parameters will be passed on the URI.

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.new.service.passing-request-parameters.*.*"
    value="foo bar"/>
```

The `oxf.fr.persistence.*.data-format-version` property does not affect `oxf.fr.detail.new.service.enable` and the data returned by the service must still be in `4.0.0` format in all cases.

Enabling `oxf.fr.detail.new.service.enable` doesn't change the behavior with regard to `POST`ed instance: even if you are calling a service to get the initial instance, the `POST`ed instance will be used when a document is `POST`ed to the corresponding "new form" page.

## See also

* Configuration properties
  * [Detail page](/configuration/properties/form-runner/form-runner-detail-page)
  * [Attachments](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments)
  * [PDF](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf)
  * [Table of contents](/configuration/properties/form-runner/form-runner-detail-page/form-runner-toc)


# PDF

## Header and footer configuration

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

The following property controls the header and footer configuration for automatic PDF files. The property is in JSON format. For details, see [Header and footer configuration](/form-runner/features/pdf-automatic#header-and-footer-configuration).

```xml
<property as="xs:string" name="oxf.fr.detail.pdf.header-footer.*.*">
    {
      ...
    }
</property>
```

## Custom PDF filename

\[SINCE Orbeon Forms 4.9]

The following property dynamically controls the name of the PDF file produced on the Detail page. By default, if the property value is blank, the PDF filename is a random id assigned to the current form session.

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.pdf.filename.*.*"
    value=""/>
```

The value of the property, if not empty, is an XPath expression which runs in the context of the root element of the XML document containing form data. The trimmed string value of the result of the expression is used to determine the filename.

Example:

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.pdf.filename.*.*"
    value="//customer-id"/>
```

If the form contains a `customer-id` field, the PDF filename will be the value of that field followed by `.pdf`. If the field is blank, the default, random id filename is used, as if the property had not been specified.

\[SINCE Orbeon Forms 2022.1 and 2021.1.5]

The property has the following default:

```xml
<property as="xs:string" name="oxf.fr.detail.pdf.filename.*.*">
    concat(
        fr:form-title(),
        ' - ',
        substring(fr:document-id(), 1, 16),
        '.pdf'
    )
</property>
```

A similar default is present for other types of exports (TIFF, Excel, XML, etc.).

## Hyperlinks in automatic mode

\[SINCE Orbeon Forms 4.6]

The following property controls whether hyperlinks are enabled in the generated PDF. By default, they are enabled:

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.pdf.hyperlinks.*.*"
    value="true"/>
```

When set to `true`:

* HTTP and HTTPS URLs in input field and text areas are automatically hyperlinked.
* Hyperlinks in rich text controls are preserved.
* Hyperlinks in the rest of the form, if any, are preserved.

When set to `false`:

* HTTP and HTTPS URLs in input field and text areas are not hyperlinked, but placeholders are added.
* Hyperlinks in rich text controls are removed and placeholders are left.
* Hyperlinks in the rest of the form, if any, are removed and placeholders are left.
* Placeholders consist of an HTML `<a>` without an `href` attribute. This helps with CSS styling.

For example, the default style for hyperlinks only highlights and underlines `<a>` elements with an `href` attribute:

```css
a[href] {
    text-decoration: underline;
    &:link, &:visited {
        color: @linkColor !important;
    }
}
```

## Avoiding fields being cut across pages

\[SINCE Orbeon Forms 2020.1.5] When a field can be partially, but not entirely, shown at the bottom of a page, instead of showing part of the field on one page and part on the other page ("cutting" the field), it is often better to move that field along with its label to the next page. However, in some cases, when the field contains a lot of text, moving the whole field to the next page means that the "previous page" ends up with a lot of blank space, which is a suboptimal use of the page, and can even be confusing.

So Orbeon Forms implements the following heuristic in cases where the whole field can't fit on the current page: if the field's content is "short", the whole field is pushed to the next page, and if the content is "longer" the field is "cut", with part of the field on one page, and part on the next page. The threshold separating what is considered to be "short" and "longer" is defined with the following property, in the number of characters, and the default value is 512.

```xml
<property
    as="xs:integer"
    name="oxf.fr.detail.pdf.long-content-threshold.*.*"
    value="512"/>
```

## Barcode

\[Orbeon Forms PE] The following property specifies whether a barcode must be included on PDF files produced from a PDF template. Adding a barcode to a PDF produced without a PDF template isn't supported at this point (see [RFE #2190](https://github.com/orbeon/orbeon-forms/issues/2190)).

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.pdf.barcode.*.*"
    value="false"/>
```

## Font embedding in automatic mode

See [Automatic PDF](/form-runner/features/pdf-automatic).

## Font embedding in template mode

See [PDF templates](/form-runner/features/pdf-templates).

## Disabling the PDF button when form is invalid

\[BEFORE Orbeon Forms 4.2]

With version 4.0 and earlier, the PDF button is always disabled if invalid data is present in the form.

\[SINCE Orbeon Forms 4.2]

The PDF button is always enabled, allowing users to generate a PDF for the current form, even if some data in the form is invalid. If instead, you wish to disable the PDF button when the form is invalid, set the following property to `true` (it is set to `false` by default):

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.pdf.disable-if-invalid.*.*"
    value="false"/>
```

## Automatic PDF page size and orientation

\[SINCE Orbeon Forms 2019.2]

Available page orientations:

* `portrait` (default)
* `landscape`

```xml
<property 
    as="xs:string" 
    name="oxf.fr.detail.rendered-page-orientation.*.*" 
    value="portrait"/>
```

Available page sizes:

* `letter` (default)
* `a4`
* `legal`

```xml
<property 
    as="xs:string" 
    name="oxf.fr.detail.rendered-page-size.*.*" 
    value="letter"/>
```

These can also be configured for a particular form in Form Builder's Form Settings dialog.

### Calculations in readonly modes

\[SINCE Orbeon Forms 2021.1]

See the [`oxf.fr.detail.readonly.disable-calculate` property](/configuration/properties/form-runner/form-runner-detail-page#calculations-in-readonly-modes).

## See also

* [PDF Production](/form-runner/features/pdf-production)
* [Automatic PDF](/form-runner/features/pdf-automatic)
* [PDF Templates](/form-runner/features/pdf-templates)
* [PDF configuration properties](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf)
* [Testing PDF production](/form-builder/advanced/pdf-test)
* [TIFF Production](/form-runner/features/tiff-production)
* [Sending PDF and TIFF content: Controlling the format](/form-runner/advanced/buttons-and-processes/actions-form-runner/actions-form-runner-send)
* Blog post: [New layout choices for PDF and browser views](https://blog.orbeon.com/2019/11/new-layout-choices-for-pdf-and-browser.html)
* Configuration properties
  * [Detail page](/configuration/properties/form-runner/form-runner-detail-page)
  * [Attachments](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments)
  * [Email](/configuration/properties/form-runner/form-runner-detail-page/form-runner-email)
  * [Initial data](/configuration/properties/form-runner/form-runner-detail-page/form-runner-initial-data)
  * [Table of contents](/configuration/properties/form-runner/form-runner-detail-page/form-runner-toc)


# Table of contents

## Introduction

The table of contents is a list of sections in the form. It is shown at the top or left of the form, and allows users to navigate to a particular section.

![Table of contents on top](/files/KWgupIjorTkqzUoxmIUg)

Note the following:

* The following configuration properties only relate to the use of the table of contents when the Wizard view is not used.
* When using the Wizard view, a table of contents is handled by the Wizard itself and the global Table of Contents discussed here is disabled.
* In View and PDF modes, the Wizard view is never in use.

## Show table of contents

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

```xml
<property
    as="xs:integer"
    name="oxf.fr.detail.toc.min-sections.*.*"
    value="0"/>
```

If the number of table of contents entries (that is, of form sections) is greater than this value, then show the Table of Contents at the top of the form.

This can be omitted or set to -1 to never show the TOC.

[\[DEPRECATED SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

The following property does the same as `oxf.fr.detail.toc.min-sections` but is deprecated. Use `oxf.fr.detail.toc.min-sections` instead since Orbeon Forms 2023.1.

```xml
<property
    as="xs:integer"
    name="oxf.fr.detail.toc.*.*"
    value="0"/>
```

## Position of the table of contents

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.toc.position.*.*"
    value="top"/>
```

Where to place the table of contents: `top`, `left`, or `none`. The default is `top`. If neither `top` nor `left` is specified, the table of contents is not shown.

![Table of contents on the left](/files/GDVOTZh8ftbSGLmfeeny)

In PDF mode, the `left` position is not supported and the table of contents is always shown at the top.

![Table of contents on top in PDF files](/files/Ri1iQ0c98rxKSE4WXWT6)

## Modes in which the table of contents is shown

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

```xml
<property
    as="xs:string"
    name="oxf.fr.detail.toc.modes.*.*"
    value="new edit"/>
```

The table of contents is shown in the specified modes only. The default is `new edit view`. The following modes are available: `new`, `edit`, `view`, `pdf`, `tiff`.

## See also

* Configuration properties
  * [Detail page](/configuration/properties/form-runner/form-runner-detail-page)
  * [Attachments](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments)
  * [Email](/configuration/properties/form-runner/form-runner-detail-page/form-runner-email)
  * [Initial data](/configuration/properties/form-runner/form-runner-detail-page/form-runner-initial-data)
  * [PDF](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf)


# Persistence

## Levels of configuration

Storage for the persistence hierarchy can be configured at multiple levels:

* Globally
* For each *application*
  * The application is referred to with an *app name.*
* For each *form definition* within an application
  * The form definition is referred to with a *form name.*
* For each *resource type* (form definition vs. form data)
  * The resource type is referred to with an identifier:
    * *form*: form definition as XHTML, with optional associated resources (images, PDF template, and other attachments)
    * *data*: form data as XML, with optional associated attachments

This allows you for example to store certain form definition on disk, while storing the associated data, as filled-out by users, in one or more databases.

## Persistence layer configuration

Orbeon Forms 4.0 introduces a level of indirection in the persistence layer configuration: you map the parameters *app*, *form*, and *form definition or data* to a *provider*, and then map that provider to its REST persistence API. This is configured via properties starting with `oxf.fr.persistence.provider`.

By default, SQLite (SINCE Orbeon Forms 2023.1, previously eXist) is configured for all apps and forms, including form definitions and form data. This is done by associating the `sqlite` provider:

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.provider.*.*.*"
    value="sqlite"/>
```

In each such provider mapping, there are 3 configurable pieces of information. They represent, in this order:

1. A string to specify the Form Runner application name, like `orbeon` or `acme`.
2. A Form Runner form name, like `registration` or `address`.
3. Whether the configuration regards form data (`data`), or the form configuration files (`form`).

Each of those can be a wildcard (`*`). Wildcards allow you to setup Orbeon Forms to use different persistence providers for different app, form, and resource type combinations.

The properties are interpreted hierarchically and you may specify the configuration more or less specifically:

| Property                                          | Explanation                                            |
| ------------------------------------------------- | ------------------------------------------------------ |
| `oxf.fr.persistence.provider.*.*.*`               | form definitions and form data for all applications    |
| `oxf.fr.persistence.provider.*.*.data`            | form data for all applications                         |
| `oxf.fr.persistence.provider.*.*.form`            | form definitions for all applications                  |
| `oxf.fr.persistence.provider.orbeon.*.form`       | form definitions for all forms in application "orbeon" |
| `oxf.fr.persistence.provider.orbeon.*.data`       | form data for all forms in application "orbeon"        |
| `oxf.fr.persistence.provider.orbeon.contact.*`    | form definitions and data for "orbeon/contact"         |
| `oxf.fr.persistence.provider.orbeon.contact.form` | form definitions for "orbeon/contact"                  |
| `oxf.fr.persistence.provider.orbeon.contact.data` | form data for "orbeon/contact"                         |

*NOTE: This means that you can't name a persistence provider `provider`.*

For example some built-in demo forms as well as Form Builder load their form definitions directly from the Orbeon Forms web application. This is done by using more specific properties (without wildcards):

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.provider.orbeon.builder.form"
    value="resource"/>
<property
    as="xs:string"
    name="oxf.fr.persistence.provider.orbeon.dmv-14.form"
    value="resource"/>
```

One thing you might notice is that, as far as configuring persistence, you treat Form Builder itself just like another form, with app name `orbeon` and form name `builder`.

If you want to change the default provider to Oracle, and since a provider named `oracle` is predefined, the following configuration will do just that:

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.provider.*.*.*"
    value="oracle"/>
```

Because wildcards are used, this property does not override the configuration for the built-in demo forms as well as Form Builder! This is desirable, because the Form Builder implementation itself is not usually something you want to store somewhere else.

In the case of Oracle, the wildcards also allow you to setup Orbeon Forms to use different schemas for different app, form, and resource type combinations. For instance you could store everything related to an `hr` app in one Oracle database schema and everything related to another `finance` app in another Oracle database schema. For more on this, see how to setup the persistence layer for [multiple schemas](/form-runner/persistence/relational-db).

Each provider supports standard properties, as follows:

| Property                                                                                             | Explanation                                                         |
| ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| [`oxf.fr.persistence.[provider].uri`](#property_uri)                                                 | The location, via HTTP, of the provider implementation.             |
| [`oxf.fr.persistence.[provider].active`](#property_active)                                           | Whether the provider is active                                      |
| [`oxf.fr.persistence.[provider].autosave`](#property_autosave)                                       | Whether [autosave](/form-runner/persistence/autosave) is supported. |
| [`oxf.fr.persistence.[provider].permissions`](#property_permissions)                                 | Whether user/group permissions are supported                        |
| [`oxf.fr.persistence.[provider].versioning`](#property_versioning)                                   | Whether versioning is supported                                     |
| [`oxf.fr.persistence.[provider].data-format-version`](#property_data-format-version)                 | The data format version used in the database                        |
| [`oxf.fr.persistence.[provider].escape-non-ascii-characters`](#property_escape-non-ascii-characters) | Whether non-ASCII characters should be escaped                      |

### `uri` property <a href="#property_uri" id="property_uri"></a>

The `uri` property specifies the location, via HTTP, of the provider implementation.

```xml
<property
    as="xs:anyURI"
    name="oxf.fr.persistence.[provider].uri"
    value="[URI to reach the provider]"/>
```

### `active` property <a href="#property_active" id="property_active"></a>

\[SINCE Orbeon Forms 4.4]

The `active` property specifies whether the provider is active.

This is used as follows (confirmed for Orbeon Forms 4.4 to 4.10):

* If active, the Form Runner Home page queries the persistence implementation to obtain the list of published forms and enable administrative operations.
* See issue [#2327](https://github.com/orbeon/orbeon-forms/issues/2327).

```xml
<property
    as="xs:boolean"
    name="oxf.fr.persistence.[provider].active"
    value="[true|false]"/>
```

### `autosave` property <a href="#property_autosave" id="property_autosave"></a>

\[SINCE Orbeon Forms 4.4]

The `autosave` property specifies whether [autosave](/form-runner/persistence/autosave) is supported.

This is used as follows (confirmed for Orbeon Forms 4.4 to 4.10):

* If `true`, Form Runner enables autosave if the other [conditions](/form-runner/persistence/autosave) are met. Otherwise, Form Runner will not attempt to enable autosave.

```xml
<property
    as="xs:boolean"
    name="oxf.fr.persistence.[provider].autosave"
    value="[true|false]"/>
```

### `permissions` property <a href="#property_permissions" id="property_permissions"></a>

\[SINCE Orbeon Forms 4.4]

The `permissions` property specifies whether user/group permissions are supported.

If `true`, Form Runner assumes that permissions are supported by the provider implementation.

This is used as follows (confirmed for Orbeon Forms 4.4 to 4.10):

* The Summary page sends a 403 if the user doesn't have access based on role, and the persistence provider is known not to support permissions.

```xml
<property
    as="xs:boolean"
    name="oxf.fr.persistence.[provider].permissions"
    value="[true|false]"/>
```

### `versioning` property <a href="#property_versioning" id="property_versioning"></a>

\[SINCE Orbeon Forms 4.5]

The `versioning` property specifies whether versioning is supported.

If `true`, Form Runner assumes that versioning is supported by the provider implementation.

This is used as follows (confirmed for Orbeon Forms 4.5 to 4.10):

* At form publishing time, Form Builder proposes the option to overwrite the existing published form definition, or to create a new version.

```xml
<property
    as="xs:boolean"
    name="oxf.fr.persistence.[provider].versioning"
    value="[true|false]"/>
```

See also [Limitations](/form-runner/features/versioning#limitations).

### `data-format-version` property <a href="#property_data-format-version" id="property_data-format-version"></a>

\[SINCE Orbeon Forms 2017.1]

The `data-format-version` property specifies which data format version is in the database.

Allowed values:

* `4.0.0`
* `4.8.0`
* `2019.1.0` \[SINCE Orbeon Forms 2019.1]

The values must match exactly.

The default is `4.0.0` for backward compatibility.

This property must be changed very carefully. All form data in the database for a given provider must be in the same format and it is not possible, at this point, to change the value of this property if there is existing data in the database.

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.*.data-format-version"
    value="4.8.0"/>
```

*NOTE: Since Orbeon Forms 2017.1, the `oxf.fr.detail.new.service.enable` property always assumes data in `4.0.0` format even if the property above is set to a different value.*

### `escape-non-ascii-characters` property <a href="#property_escape-non-ascii-characters" id="property_escape-non-ascii-characters"></a>

\[SINCE Orbeon Forms 2021.1]

We recommend you set up your database to store text as Unicode, to avoid potential problems when users enter non-ASCII characters, like accents, characters from non-latin languages, or even emojis. In cases when this isn't possible, you can set the following property to `true`, to instruct Orbeon Forms to escape all non-ASCII characters in form data before they are sent to the database.

This escaping only applies to form data, and not to indices created based on this data, which means that if you are using the Form Runner Summary page, or the search API, and that a field value shown in the Summary page or returned by the search API contains a character that your database is unable to store, while your data is still safe, the Summary page or the result of the search API might contain an incorrect value.

For instance, say a user enters the character `é` in a field, and that you have this property enabled, then the form data will contain `&#233;`, which is the escaped version of `é`, however, if that field is indexed (because you've enabled *Show on Summary page* or *Allow bulk edit* in the *Control Settings* for that field), then the character `é` will be stored in the index table, which might be a problem if your database encoding cannot accommodate the storage of this character.

Amongst the [built-in implementations of the persistence API](/form-runner/persistence/db-support), this property is supported for all relational databases, but not for eXist.

```xml
<property
    as="xs:boolean"
    name="oxf.fr.persistence.*.escape-non-ascii-characters"
    value="false"/>
```

## Multiple databases of the same type

Say you have two MySQL databases (or a single MySQL database with different schemas and users configured) and you would like to configure Orbeon Forms to store form definitions and form data to one or the other of the databases. Assume the following JDBC datasources:

* `mysql_foo`
* `mysql_bar`

You associate each datasource with a Form Runner *persistence provider* with the following properties:

```
<property as="xs:anyURI"  name="oxf.fr.persistence.mysql_foo.uri"         value="/fr/service/mysql"/>
<property as="xs:string"  name="oxf.fr.persistence.mysql_foo.datasource"  value="mysql_foo"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_foo.autosave"    value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_foo.permissions" value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_foo.versioning"  value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_foo.lease"       value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_foo.reindex"     value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_foo.reencrypt"   value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_foo.sort"        value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_foo.active"      value="true"/>

<property as="xs:anyURI"  name="oxf.fr.persistence.mysql_bar.uri"         value="/fr/service/mysql"/>
<property as="xs:string"  name="oxf.fr.persistence.mysql_bar.datasource"  value="mysql_bar"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_bar.autosave"    value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_bar.permissions" value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_bar.versioning"  value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_bar.lease"       value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_bar.reindex"     value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_bar.reencrypt"   value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_bar.sort"        value="true"/>
<property as="xs:boolean" name="oxf.fr.persistence.mysql_bar.active"      value="true"/>
```

Then if you would like:

* form definitions and form data for the Form Runner app `foo` to go to `mysql_foo`
* form definitions and form data for the Form Runner app `bar` to go to `mysql_bar`

Add:

```
<property as="xs:string"  name="oxf.fr.persistence.provider.foo.*.*"      value="mysql_foo"/>
<property as="xs:string"  name="oxf.fr.persistence.provider.bar.*.*"      value="mysql_bar"/>
```

## Configuration properties for specific persistence providers

Each provider may have specific configuration properties. For the latest settings, see [`properties-form-runner.xml`](https://github.com/orbeon/orbeon-forms/blob/master/src/main/resources/config/properties-form-runner.xml#L17).

## Persistence proxy properties

Some properties apply to the persistence proxy itself, rather than to a specific persistence provider.

### Disabling version history

\[SINCE Orbeon Forms 2026.1]

The following property allows you to disable [revision history](/form-runner/features/revision-history) for a specific application and form:

```xml
<property
    as="xs:boolean"
    name="oxf.fr.persistence.proxy.disable-version-history.*.*"
    value="true"/>
```

When set to `true`, each time form data is saved, the persistence proxy disables all revision history for that document.

Use cases include:

* When users don't want a history of their edits to be kept.
* When regulations don't allow keeping edit history.

Note that if this property is enabled on a form that already has existing data, the revision history for that existing data won't be purged. Revision history will only be disabled for data saved after the property is enabled.

## Storing attachments in the filesystem or on S3

By default, attachments are stored in the database. Alternatively, you can configure Form Runner to store attachments in the filesystem ([\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)) or S3 ([\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)). This is useful for larger attachments, for example.

To store attachments in the filesystem, you can do so globally by setting the following property:

```xml
<property 
    as="xs:string"
    name="oxf.fr.persistence.provider.*.*.*.attachments"
    value="filesystem"/>
```

To store attachments in S3, use the `s3` value:

```xml
<property 
    as="xs:string"
    name="oxf.fr.persistence.provider.*.*.*.attachments"
    value="s3"/>
```

### Filesystem

For filesystem storage, the base path where attachments are stored is configured with:

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.filesystem.directory"
    value="/path/to/attachments"/>
```

This property doesn't have any default value and is mandatory.

The full path where the attachment file is stored is determined using an internal scheme, which includes the application and form names, among other information. For example, with the above property value, an attachment would be stored at the following location:

`/path/to/attachments/acme/sales/data/194714ff845faf31d08f745ca62cb0c622788aae/1/928cf991678ea0449bf0988dce956b4a88eb34c8.bin`

### S3

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1) To store attachments in S3, it is mandatory to specify an S3 configuration, using the following property:

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.s3.s3-config"
    value="s3-config-name"/>
```

See [S3 storage](/form-runner/features/s3) for more information about the properties needed to configure an S3 storage.

For S3, the base path is configured using the following property:

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.s3.base-path"
    value="path/to/attachments"/>
```

By default, this property is set to an empty string, which means that attachments are stored at the root of the configured S3 bucket. As with filesystem storage, the full path to the attachment objects includes the application and form names, as well as other information.

For example, for an S3 bucket named `s3-bucket` and a base path set to `path/to/attachments`, an attachment would be stored at the following S3 location:

`s3-bucket/path/to/attachments/acme/sales/data/194714ff845faf31d08f745ca62cb0c622788aae/1/928cf991678ea0449bf0988dce956b4a88eb34c8.bin`

For an empty base path, it would be stored at the following location:

`s3-bucket/acme/sales/data/194714ff845faf31d08f745ca62cb0c622788aae/1/928cf991678ea0449bf0988dce956b4a88eb34c8.bin`

### Multiple attachment providers of the same type

Multiple filesystem and S3 attachment providers can be configured, following the same principles as described [above](#multiple-databases-of-the-same-type)

For example, if you need to store attachments in two different filesystem paths for the Form Runner apps `foo` and `bar`, you can do so by using the following properties:

```xml
<property 
    as="xs:string"
    name="oxf.fr.persistence.provider.foo.*.*.attachments"
    value="filesystem_foo"/>
<property
    as="xs:string"
    name="oxf.fr.persistence.provider.bar.*.*.attachments"
    value="filesystem_bar"/>

<property as="xs:anyURI" name="oxf.fr.persistence.filesystem_foo.uri"       value="/fr/service/filesystem"/>
<property as="xs:string" name="oxf.fr.persistence.filesystem_foo.directory" value="/path/to/foo_attachments"/>

<property as="xs:anyURI" name="oxf.fr.persistence.filesystem_bar.uri"       value="/fr/service/filesystem"/>
<property as="xs:string" name="oxf.fr.persistence.filesystem_bar.directory" value="/path/to/bar_attachments"/>
```

Similarly, you can configure multiple S3 attachment providers.

### Dynamic base path configuration

In addition to static paths, you can also use an [AVT](/xforms/core/attribute-value-templates) to dynamically configure the base path. For instance, the following would use a base path specified by an environment variable:

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.filesystem.directory"
    value="{environment-variable('ATTACHMENTS_BASE_DIRECTORY')}"/>
```

Note that, specifically in the context of the `oxf.fr.persistence.*.directory` property, it is not necessary to set `oxf.xpath.environment-variable.enabled` to true in order to use the `environment-variable()` function.

For S3, the `oxf.fr.persistence.s3.base-path` property is also interpreted as an AVT.

## Filename of stored attachments

### Default filename

By default, attachments are stored using a filename format of `[attachment-id].bin`, where `[attachment-id]` represents a unique hexadecimal identifier for each attachment. The original filename and file extension are not retained. Example:

`883a36f20b8054187e0994022269a321ba3ec07e.bin`

### Custom filename

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1) The default behavior typically works well, particularly when attachments are stored in the database. However, when attachments are stored on the local filesystem or S3, you may want to preserve all or part of the original filename, including the file extension. You can achieve this using the following property:

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.attachments.filename.*.*"
    value="concat(fr:attachment-id(), '.bin')"/>
```

The following [XPath functions](/xforms/xpath/extension-functions/extension-form-runner#attachment-functions) can be used to extract information from the attachment:

* `fr:attachment-id()`: attachment unique ID (must be included)
* `fr:attachment-filename()`: original attachment filename (e.g. `portrait.jpg`)
* `fr:attachment-mediatype()`: attachment file type (e.g. `application/pdf`, `image/jpeg`, etc.)
* `fr:attachment-size()`: attachment size in bytes
* `fr:attachment-control-name()`: name of the attachment control

Note about the `fr:attachment-id()` function:

* This function must be called at least once in the XPath expression specified in the `oxf.fr.persistence.attachments.filename` property. If the function's complete output is not found during expression evaluation, an error will occur and the attachment will not be saved.
* Currently, this function can only be invoked within the context of the `oxf.fr.persistence.attachments.filename` property, while other attachment functions can be called from additional locations.

Example:

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.attachments.filename.*.*"
    value="concat(
        format-date(current-date(), '[Y]-[M01]-[D01]'),
        '-',
        fr:attachment-id(),
        '-',
        fr:attachment-filename()
    )"/>
```

This would lead to filenames that look like this:

`2025-06-11-883a36f20b8054187e0994022269a321ba3ec07e-portrait.jpg`

## See also

* [Form Runner persistence API](/form-runner/api/persistence)
* [S3 storage](/form-runner/features/s3)


# Summary page

## Adding your own CSS files

\[SINCE Orbeon Forms 2017.1]

In addition to `oxf.fr.css.custom.uri`, you can also use the following property, which apply only to the Summary page:

```xml
<property
    as="xs:string"
    name="oxf.fr.summary.css.custom.uri.*.*"
    value="/forms/acme/assets/acme-summary.css"/>
```

See also [Adding your own CSS](/configuration/properties/form-runner#adding-your-own-css).

## Adding your own JavaScript files

\[SINCE Orbeon Forms 2017.1]

In addition to `oxf.fr.js.custom.uri`, you can also use the following property, which apply only to the Summary page:

```xml
<property
    as="xs:string"
    name="oxf.fr.summary.js.custom.uri.*.*"
    value="/forms/acme/assets/acme-summary.js"/>
```

See also [Adding your own JavaScript](/configuration/properties/form-runner#adding-your-own-javascript).

## Summary page size

```xml
<property
    as="xs:integer"
    name="oxf.fr.summary.page-size.*.*"
    value="10"/>
```

Number of rows shown in the Summary page.

## Created and Last Modified columns

By default, the Summary page shows a Created and Modified columns:

![Created and Last Modified](/files/-LEkC44_SHbiBTsv9V9Q)

You can remove either one of those columns by setting the appropriate property value to `false`.

\[SINCE Orbeon Forms 2026.1] These properties can also be overridden per form using the [Summary Page tab](/form-builder/form-settings/summary-page) of the Form Settings dialog.

```xml
<property
    as="xs:boolean"
    name="oxf.fr.summary.show-created.*.*"
    value="true"/>

<property
    as="xs:boolean"
    name="oxf.fr.summary.show-last-modified.*.*"
    value="true"/>
```

## Show the workflow stage

\[SINCE Orbeon Forms 2020.1]

See [Workflow stage](/form-runner/features/workflow-stage#showing-the-workflow-stage-in-the-summary-page).

\[SINCE Orbeon Forms 2026.1] This property can also be overridden per form using the [Summary Page tab](/form-builder/form-settings/summary-page) of the Form Settings dialog.

## Show created by and last modified by users

\[SINCE Orbeon Forms 2021.1]

You can add either one of those columns by setting the appropriate property value to `true`.

\[SINCE Orbeon Forms 2026.1] These properties can also be overridden per form using the [Summary Page tab](/form-builder/form-settings/summary-page) of the Form Settings dialog.

```xml
<property 
    as="xs:boolean" 
    name="oxf.fr.summary.show-created-by.*.*"                           
    value="true"/>
<property 
    as="xs:boolean" 
    name="oxf.fr.summary.show-last-modified-by.*.*"                   
    value="true"/>
```

## Buttons on the Summary page

See [Configuring Summary page buttons](/form-runner/advanced/buttons-and-processes/summary-page-buttons-and-processes#configuring-summary-page-buttons).

## Versioning

\[SINCE Orbeon Forms 2020.1 and 2019.2.3] The following property allows you to configure whether the [Summary page](https://github.com/orbeon/orbeon-forms-doc/blob/master/configuration/properties/form-runner/feature/summary-page.md) shows data:

* if set to `true`: for one version at a time, which is the default;
* if set to `false`: for all data across form definition versions, as it used to be the case up until Orbeon Forms 2018.1.

Even when set to `false`, if the `form-version` request parameter is passed to the Summary page, it will not ignore the request parameter and will only show data created with that version of the form definition.

```xml
<property 
    as="xs:boolean"
    name="oxf.fr.summary.show-version-selector.*.*"
    value="false"/>
```

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1) When the property is set to `false`, links to the Summary page from the Published Forms and Landing pages will not include the `form-version` request parameter.

## See also

* [Detail page configuration properties](/configuration/properties/form-runner/form-runner-detail-page)
* [Summary Page tab of the Form Settings dialog](/form-builder/form-settings/summary-page)


# Form Builder

## Default values

For the latest default values of Form Builder properties, see [properties-form-builder.xml](https://github.com/orbeon/orbeon-forms/blob/master/src/main/resources/config/properties-form-builder.xml).

## Toolbox

### Groups of controls

You configure the contents of the toolbox by configuring properties in this format:

```xml
<property as="xs:string"  name="oxf.fb.toolbox.group.$GROUPNAME.uri.*.*">
    $URLS
</property>
```

In that property, the value of `$GROUPNAME` determines a grouping of the controls in the toolbox. The controls are defined by a list of XBL file URLs specified by `$URLS`. For example:

```xml
<property as="xs:string" name="oxf.fb.toolbox.group.text.uri.*.*">
    oxf:/forms/orbeon/builder/xbl/text-controls.xbl
    oxf:/xbl/orbeon/tinymce/tinymce.xbl
    oxf:/xbl/orbeon/explanation/explanation.xbl
</property>
```

*NOTE: With Orbeon Forms 4.5 and earlier, values must be placed in the `value` attribute.*

```xml
<property
    as="xs:string"
    name="oxf.fb.toolbox.group.text.uri.*.*"
    value="oxf:/forms/orbeon/builder/xbl/text-controls.xbl
           oxf:/xbl/orbeon/tinymce/tinymce.xbl
           oxf:/xbl/orbeon/explanation/explanation.xbl"/>
```

To properly show up in the toolbox, XBL files need to include the appropriate [component metadata](/form-builder/advanced/extensibility/metadata).

### Other toolbox features

In addition to controls, the toolbox has other features which you can enable or disable with the following properties:

```xml
<property as="xs:boolean" name="oxf.fb.menu.schema"                  value="true"/>
<property as="xs:boolean" name="oxf.fb.menu.pdf"                     value="true"/>
<property as="xs:boolean" name="oxf.fb.menu.edit-source"             value="true"/>
<property as="xs:boolean" name="oxf.fb.menu.permissions"             value="true"/>
<property as="xs:boolean" name="oxf.fb.menu.permissions.owner-group" value="true"/>
<!-- [SINCE Orbeon Forms 4.6] -->
<property as="xs:boolean" name="oxf.fb.menu.services.http"           value="true"/>
<property as="xs:boolean" name="oxf.fb.menu.services.database"       value="true"/>
<property as="xs:boolean" name="oxf.fb.menu.actions"                 value="true"/>
```

* \[SINCE Orbeon Forms 2022.1 and 2021.1.6] `oxf.fb.menu.permissions.owner-group` allows you, by setting the value of the property to `false`, to disable the two lines in the permissions dialog related to owner and group permissions. This is useful if you're always using a custom implementation of the persistence API that doesn't support owner and group permissions and you prefer to hide those options from form authors to avoid possible confusions.

## Publish dialog

### Links to new and Summary pages

\[SINCE Orbeon Forms 4.6]

By default, the Publish dialog proposes, upon successful publication of a form definition, shortcuts to navigate to the published form's New or Summary pages.

![Publish dialog](/files/pGDN2EGncRJHWVrFIHBr)

This property allows specifying which of these actions are available:

```xml
<property
    as="xs:string"
    name="oxf.fb.publish.buttons"
    value="new summary"/>
```

### The Version dropdown

\[SINCE Orbeon Forms 2022.1, 2021.1.3]

When versioning is supported, you can use the following property to control the behavior of the Version dropdown. If versioning isn't supported, this property has no effect. Also, the property has no effect if no version of this form has ever been published: in that case, version 1 will be published. The property can be set to:

* `default-to-select` – The dropdown is enabled, and users need to select whether they want to create a new version or overwrite an existing version. This is the default.
* `default-to-next` – The dropdown is enabled, and defaults to the choice to create a new version.
* `default-to-latest` – The dropdown is enabled, and defaults to overwriting the latest version.
* `force-next` – The dropdown is read-only, and a new version will be created on publish.
* `force-latest` – The dropdown is read-only, and the latest published version will be overwritten on publish.

```xml
<property
    as="xs:string"
    name="oxf.fb.publish.version"
    value="default-to-select"/>
```

## Maximum number of columns

```xml
<property
    as="xs:integer"
    name="oxf.fb.grid.max-columns"
    value="4"/>
```

Use this property to change the default maximum number of grid columns form authors can create. The more columns there are, the more narrow each column is, and when columns become too narrow, some less "elastic" controls might not have enough space to render properly. You want to set this property to a "reasonable" value to reduce the chance of form authors ending up with columns that are too narrow to accommodate certain controls.

## Pages in the new form wizard

\[SINCE Orbeon Forms 2025.1.2]

```xml
<property
    as="xs:string"
    name="oxf.fb.new-form.wizard-steps"
    value="templates general-settings"/>
```

When you create a new form, the new form wizard shows up to two pages:

* `templates`: a page where you can pick a template to start from
* `general-settings`: a page where you enter the application name, form name, title, and description

This property lists, as a space-separated list of tokens, which of these pages to show:

* With both tokens (the default), the wizard shows the templates page followed by the general settings page.
* With only `templates`, the wizard shows just the templates page.
* With only `general-settings`, the wizard shows just the general settings page, and new forms are created as blank forms.
* With neither token, the wizard doesn't show at all, and new forms are created directly as blank forms.

## Closing sections

```xml
<property
    as="xs:integer"
    name="oxf.fb.section.close"
    value="100"/>
```

Closing sections in Form Builder can improve responsiveness. This property sets the number of controls after which Form Builder will close all sections except the first one when loading a form. Below that number of controls, all sections are open by default.

## Action buttons in Form Builder

### Action buttons on the Form Builder Summary page

This controls which buttons appear on the Form Builder Summary page.

```xml
<property as="xs:string"  name="oxf.fr.summary.buttons.orbeon.builder">
    home delete duplicate new
</property>
```

### Action buttons on the Form Builder Detail page

This controls which buttons appear on the Form Builder Detail page.

```xml
<property as="xs:string"  name="oxf.fr.detail.buttons.orbeon.builder">
    summary new test publish save
</property>
```

## Available languages

```xml
<property
    as="xs:string"
    name="oxf.fr.available-languages.orbeon.builder"
    value="en fr es it de"/>
```

This controls which Form Builder user interface languages appear in the language selector.

## Permissions dialog

The permissions dialog allows form authors, amongst other things, to assign permissions to users having a specific roles. In that dialog, form authors can type in a text field the name of the role(s) they want to assign those permissions to. If you're systematically using certain roles, want to save form authors from having to type them, and prevent possible mistakes in the process, you can use the following property to list the role names you want the Form Builder permissions dialog to always show in that dialog:

```xml
<property as="xs:string" name="oxf.fb.permissions.role.always-show">
    ["Organization Owner"]
</property>
```

The value of this property is an array of strings in the JSON format. For instance, the following screenshot shows the dialog with the above property set (see the line for "Organization owner"), and where the form author added a line for an "admin" role.

![Permissions dialog with Organization Owner role](/files/-LEkC8VkBVZfI1Qa34NQ)

See also [Access control for deployed forms](/form-runner/access-control/deployed-forms).

## Access control

If you'd like to have multiple classes of Form Builder users where some can edit, say, forms in the `hr` app, while others can edit forms in the `sales` app, see [Access control for editing forms](/form-runner/access-control/editing-forms#form-builder-permissions).

## Formatted text configuration

[\[SINCE Orbeon Forms 2023.1.1\]](/release-notes/orbeon-forms-2023.1.1)

You can configure the TinyMCE editor used for Formatted Text (AKA [Rich Text Editor](/form-runner/component/rich-text-editor)) component for the following:

* Explanatory Text content in the form area
* control labels with "Use HTML" in Form Builder dialogs
* control hints with "Use HTML" in Form Builder dialogs
* control help with "Use HTML" texts in Form Builder dialogs

Use the following property:

```xml
<property as="xs:string"  name="oxf.xforms.xbl.fr.tinymce.config.orbeon.builder">
    {{
        "inline"             : false,
        "hidden_input"       : false,
        "language"           : "en",
        "statusbar"          : false,
        "menubar"            : false,
        "plugins"            : "lists link fullscreen",
        "toolbar"            : "bold italic | bullist numlist outdent indent | link fullscreen",
        "browser_spellcheck" : true,
        "doctype"            : "&lt;!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">",
        "encoding"           : "xml",
        "entity_encoding"    : "raw",
        "forced_root_block"  : "div",
        "verify_html"        : true,
        "visual_table_class" : "fr-tinymce-table",
        "skin"               : false,
        "convert_urls"       : false
    }}
</property>
```

The example above switches to the `iframe` mode (instead of the `inline` mode) and adds the fullscreen plugin.

For backward compatibility, if a non-blank `oxf.fb.tinymce.config` property is present, it will be used.

Note that `oxf.xforms.xbl.fr.tinymce.config.orbeon.builder` is evaluated as an AVT, while `oxf.fb.tinymce.config` is not.

## Explanatory Text TinyMCE configuration

[\[DEPRECATED SINCE Orbeon Forms 2023.1.1\]](/release-notes/orbeon-forms-2023.1.1)

Prefer the `oxf.xforms.xbl.fr.tinymce.config.orbeon.builder` property above.

Note that `oxf.xforms.xbl.fr.tinymce.config.orbeon.builder` controls the appearance of the Explanatory Text, as well as labels, hints, and help texts in Form Builder dialogs, while `oxf.fb.tinymce.config` only controls the appearance of the Explanatory Text.

\[SINCE Orbeon Forms 2018.1]

When form authors edit an Explanatory Text, Form Builder utilizes the TinyMCE component. You can [configure TinyMCE](https://www.tiny.cloud/docs/configure/) by supplying your own configuration in JSON as the value for the `oxf.fb.tinymce.config` property. If you do not set the `oxf.fb.tinymce.config` property, Form Builder defaults to a configuration, which can be found at the bottom of [`TinyMce.scala`](https://github.com/orbeon/orbeon-forms/blob/master/web-facades/src/main/scala/org/orbeon/facades/TinyMce.scala).

```xml
<property as="xs:string"  name="oxf.fb.tinymce.config">
    {
        "mode"              : "exact",
        "language"          : "en",
        ...
    }
</property>
```

## See also

* [Form Builder toolbox properties](#toolbox)
* [Formatted Text / Rich Text Editor](/form-runner/component/rich-text-editor)


# XForms

## Default values

For the latest default values of XForms properties, see [`properties-xforms.xml`](https://github.com/orbeon/orbeon-forms/blob/master/src/main/resources/config/properties-xforms.xml).

## XForms properties documented elsewhere

* [Input Control](/xforms/controls/input)
  * `oxf.xforms.label.appearance`
  * `oxf.xforms.hint.appearance`
  * `oxf.xforms.sanitize`
* [Content-Security-Policy header](/configuration/advanced/security/content-security-policy)
  * `oxf.xforms.inline-resources`

## Encryption and passwords

### Encryption password

Before Orbeon Forms 4.0, the `oxf.xforms.password` property was defined. It has since been renamed `oxf.crypto.password`. For more information, see [General Configuration Properties](/configuration/properties/general). `oxf.xforms.password` is still supported for backward compatibility. However, it is deprecated and we advise not using it as support might be removed in a future Orbeon Forms version.

### XForms items encoding

With Orbeon Forms 4.0, XForms item values (like in checkboxes, dropdown menus, etc.) are no longer encrypted, but they are encoded by position. The following property can be used to enable or disable this behavior:

```xml
<property
  as="xs:boolean"
  name="oxf.xforms.encrypt-item-values"
  value="true"/>
```

In general, this should be set to `true`, but you can set it to `false` if you need to access the value of selection controls through JavaScript on the client and if the item values are not confidential.

## XPath

### XPath function library

\[SINCE Orbeon Forms 2016.2]

By default, the XForms engine exposes standard XForms functions and a number of extension functions (see [XPath expressions](/xforms/xpath)).

The following property allows adding a custom extension XPath function library:

```xml
<property 
    as="xs:string" 
    name="oxf.xforms.function-library" 
    value="org.orbeon.oxf.fr.library.FormRunnerFunctionLibrary"/>
```

When this property is present, the XForms engine attempts to load an extension function library. It does this in two ways:

1. First, it tries to access a Scala object extending `org.orbeon.saxon.functions.FunctionLibrary`.
2. If that fails, it tries to access a Java class and calls a static `instance()` method on it to obtain an `org.orbeon.saxon.functions.FunctionLibrary`.

Scala example:

```scala
object FormRunnerFunctionLibrary extends FunctionLibrary {
  // Expose XPath functions here
}
```

Java example:

```java
class FormRunnerFunctionLibrary {

    private static FunctionLibrary _instance = null;

    public static synchronized FunctionLibrary instance() {
        if (_instance == null)
            _instance = new FormRunnerFunctionLibrary();
        return _instance;
    }
  
  // Expose XPath functions here
}
```

You can also turn specify this property specifically for a given form by adding an `xxf:function-library` attribute on the first model:

```xml
<xf:model xxf:function-library="org.orbeon.oxf.fr.library.FormRunnerFunctionLibrary">
```

### Exposing XPath data types

The following property controls whether instance types annotations are exposed to XPath 2.0 expressions:

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.expose-xpath-types" 
    value="true"/>
```

* If set to `false` (the default), instance types are not made available to XPath expressions.
* If set to `true`, they are made available.

More information: [Type annotations](/xforms/xpath/type-annotations).

### XPath expression analysis

See [XPath Analysis](/xforms/xpath/expression-analysis).

## File location information

\[SINCE Orbeon Forms 4.4]

The following property specifies whether the XForms engine should keep file location formation:

```xml
<property 
    as="xs:string" 
    name="oxf.xforms.location-mode" 
    value="none"/>
```

If the value is `smart`, location data is kept.

Default:

* prod mode: `none`
* dev mode: smart

Keeping location data is useful during development. However, this consumes more memory, especially for very large forms.

*NOTE: Before Orbeon Forms 4.4, location data was always kept.*

## Submission

The following property controls some aspects of XForms submission in Orbeon Forms:

```xml
<property 
    as="xs:boolean" 
    name="optimize-get-all" 
    value="true"/>
```

* If set to `true` (the default), Orbeon Forms optimizes submissions with replace="all" and the get method by sending URL of the submission action directly to the web browser. This however means that submission errors cannot be caught by XForms event handlers after Orbeon Forms has started connecting to the submission URL, as should be the case following the XForms specification.
* If set to `false`, Orbeon Forms buffers the reply so that errors can be handled as per XForms. However, this solution is less efficient.

The following two properties control optimized XForms submissions:

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.oxf.xforms.local-submission-forward" 
    value="true"/>
```

* If set to `true` (the default), Orbeon Forms optimizes "local" (i.e. submissions performed to a URL controlled by Orbeon Forms itself) submissions with replace="all", by using the Java Servlet API's forward capability instead of actually performing an HTTP request.
* If set to `false`, Orbeon Forms always uses the HTTP or HTTPS protocol (or other protocol specified), which is less efficient but more flexible.

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.oxf.xforms.local-submission-include" 
    value="false"/>
```

* If set to `true` (the default is false), Orbeon Forms optimizes "local" (i.e. submissions performed to a URL controlled by Orbeon Forms itself) submissions with replace="instance", replace="text" or replace="none", by directly using the Java Servlet API's include capability instead of actually performing an HTTP request.
* If set to `false`, Orbeon Forms always uses the HTTP or HTTPS protocol (or other protocol specified), which is less efficient but more flexible.

## Instance inclusion

The following property controls optimized instance inclusion:

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.local-instance-include" 
    value="false"/>
```

* If set to `true` (the default is false), Orbeon Forms optimizes "local" (i.e. submissions performed to a URL controlled by Orbeon Forms itself) instance inclusions, by directly using the Java Servlet API's include capability instead of actually performing an HTTP request.
* If set to `false`, Orbeon Forms always uses the HTTP or HTTPS protocol (or other protocol specified), which is less efficient but more flexible.

Note that for any optimized submission or inclusion to occur, the following is required:

* URL must be an absolute path, e.g. /foo/bar. Using an explicit protocol (`http://foo.com/bar`) disables optimized submissions.
* No elements must be passed.
* The submission must be synchronous.

## JavaScript and CSS Resources

The following properties are documented in [JavaScript and CSS assets](/configuration/advanced/javascript-css-assets):

* `oxf.xforms.minimal-resources`
* `oxf.xforms.combine-resources`
* `oxf.xforms.resources.baseline`
* `oxf.xforms.assets.baseline`
* `oxf.xforms.assets.baseline.excludes`

## Noscript mode

\[DEPRECATED SINCE Orbeon Forms 2016.3]

\[UNTIL Orbeon Forms 2017.2]

The following property controls whether noscript mode is enabled:

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.noscript" 
    value="false"/>
```

The following property controls whether noscript mode is supported:

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.noscript-support" 
    value="true"/>
```

The noscript mode is enabled only if both properties are true.

*NOTE: The reason there are two properties is that in the future, the XForms engine might be able to determine by itself whether noscript mode is enabled based on what controls and XBL components are in use.*

## Controls

### XForms 1.1-compatible of switch/case

[XForms 1.1 specifies](http://www.w3.org/TR/xforms/#ui-switch-module) that a non-visible case behaves as non-relevant.

A property allows enabling XForms 1.1-compatible behavior. (Orbeon Forms did not support this previously and considered that non-visible cases were hidden but still relevant.)

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.xforms11-switch" 
    value="true"/>
```

You can also set this property on a per-switch basis:

```xml
<xf:switch xxf:xforms11-switch="true">
    ...
</xf:switch>
```

This property also applies in a similar way to hidden dialogs.

NOTE: In the future, it is expected that:

* This will be enabled by default to be XForms 1.1-compliant out of the box.
* Setting the property to `false` will revert to the pre-March 2010 behavior, as there are cases where keeping hidden cases relevant makes sense.

### XForms repeat updates upon xf:insert and xf:delete

#### With Orbeon Forms 2019.1 and newer

The `xf:insert` and `xf:delete` actions do not attempt to update repeats immediately after completion. Repeats, like any other UI controls, update during the following UI refresh.

There is no configuration to change this behavior.

#### With Orbeon Forms 2018.1 and 2018.2

The `xf:insert` and `xf:delete` actions do not attempt to update repeats immediately after completion. Repeats, like any other UI controls, update during the following UI refresh.

Temporarily, the following property can be used to restore the Orbeon Forms 2017.2 behavior:

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.update-repeats"
    value="true"/>
```

However, please note that the behavior is deprecated and support for the Orbeon Forms 2017.2 behavior is expected to be removed altogether in a subsequent Orbeon Forms release.

See also [#3503](https://github.com/orbeon/orbeon-forms/issues/3503).

#### Until Orbeon Forms 2017.2

`xf:insert` and `xf:delete` actions attempted to update repeats immediately after completion, independently from regular UI refreshes.

### Label, help, hint, alert (LHHA) elements

By default, LHHA elements are represented as follows:

* `<xf:label>` use the HTML `<label>` element
* `<xf:hint>` use the HTML `<span>` element
* `<xf:help>` use the HTML `<span>` element
* `<xf:alert>`
  * \[SINCE Orbeon Forms 2022.1] Use the HTML `<button>` element; this is done so users can tab to the help icon, making it accessible to the keyboard to users who predominantly use a keyboard, either by choice or because they have difficulties using a pointing device.
  * \[UP TO Orbeon Forms 2021.1] Use the HTML `<span>` element, and in Orbeon Forms 2021.1.2 and subsequent point releases, you can manually change the value of this property to `button` to make the help icon accessible with the keyboard.

You can configure the following properties in your `properties-local.xml` to change the default configuration.

```xml
<property as="xs:string" name="oxf.xforms.label-element" value="label"/>
<property as="xs:string" name="oxf.xforms.hint-element"  value="span"/>
<property as="xs:string" name="oxf.xforms.help-element"  value="button"/>
<property as="xs:string" name="oxf.xforms.alert-element" value="span"/>
```

If an element is configured to be a label, a `for` attribute pointing to the control is set by the XForms engine.

### Order of control and LHHA elements

*NOTE: For Form Runner, see the* [*`oxf.fr.detail.lhha-order` property*](/configuration/properties/form-runner/form-runner-detail-page#order-of-lhha-elements) *instead.*

The following property controls the order of label, help, hint, alert, and control elements output by the XForms engine:

```xml
<property 
    as="xs:string" 
    name="oxf.xforms.order" 
    value="label control help alert hint"/>
```

The property uses the order of the predefined tokens `label`, `control`, `help`, `alert`, and `hint` to set the order.

The order applies to most controls, such as `<xf:input>`, etc. Some specific control or appearances do not use this property:

* `<xxf:dialog>`
* `<xf:group appearance="xxf:fieldset">`

Individual controls also support this property locally:

```xml
<xf:input xxf:order="label help control hint alert">
```

### Two months view

By default, YUI date picker shows as follows:

![Default date picker](/files/-LEkC3nZbOrbk-NTyZ30)

You can set the `oxf.xforms.datepicker.two-months` property to `true`, and the date picker will show two months at a time:

![Date picker with two months displayed at a time](/files/-LEkC3nbq6iebmS-glrL)

By default, the property is set to `false`, (only one month is shown). You can override by adding the following to your `properties-local.xml`:

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.datepicker.two-months" 
    value="false"/>
```

### Navigator

With the `oxf.xforms.datepicker.navigator` property set to `true` (the default), when you click on the month headers, a small dialog allows you to type a year and select a month from a drop-down. This is particularly convenient if the date you want to capture has a chance to be further in the future or in the past (such as a birth date).

![Month and year selection in YUI date picker with navigator and two months properties enabled](/files/-LEkC3nfWM9gwxToEMsr)

You disable the navigator by setting the following property to `false` (it is `true` by default):

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.datepicker.navigator" 
    value="false"/>
```

### Upload

#### Maximum upload size

\[SINCE Orbeon Forms 2017.1]

The following property sets the maximum size in bytes of an uploaded file. For example, if you set it to `1000000` (1 MB), and the user attempts to upload a larger file, an error is reported.

```xml
<property 
    as="xs:string"  
    name="oxf.xforms.upload.max-size-per-file"                             
    value="1000000"/>
```

If `oxf.xforms.upload.max-size-per-file` is blank or missing (the default), then the value of the following backward compatibility property is used:

```xml
<property
    as="xs:integer" 
    processor-name="oxf:request"   
    name="max-upload-size"          
    value="100000000"/>
```

The value of `oxf.xforms.upload.max-size-per-file` can be overridden for a specific control using the `xxf:upload-max-size-per-file()` validation function.

This property was previously named `oxf.xforms.upload.max-size`. The old name is still supported for backward compatibility.

Whenever possible, it is recommended to use the Form Runner property [`oxf.fr.detail.attachment.max-size-per-file`](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments#maximum-attachment-size) instead of this XForms property.

#### Maximum aggregate upload size (forms)

\[SINCE Orbeon Forms 2017.1]

The following property sets the maximum aggregate size in bytes of all uploaded files for a given instance of form data. For example, if you set it to `1000000` (1 MB), and the form has two upload controls, and you upload a 600 KB upload using the first control, then only 400 KB can be uploaded using the second control, even if a larger maximum size per control was set using the `oxf.xforms.upload.max-size-per-file` property or the `xxf:upload-max-size-per-file()` validation function. If you attempt to upload a larger file, an error is reported.

```xml
<property 
    as="xs:string"  
    name="oxf.xforms.upload.max-size-aggregate-per-form"                   
    value="1000000"/>
```

This property was previously named `oxf.xforms.upload.max-size-aggregate`. The old name is still supported for backward compatibility.

Whenever possible, it is recommended to use the Form Runner property [`oxf.fr.detail.attachment.max-size-aggregate-per-form`](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments#maximum-aggregate-attachment-size-forms) instead of this XForms property.

#### Maximum aggregate upload size (controls)

[\[SINCE Orbeon Forms 2024.1\]](/release-notes/orbeon-forms-2024.1)

The following property sets the maximum aggregate size in bytes of all uploaded attachments for each individual attachment control. This property will typically be used to limit the total size of attachments for multiple attachment controls, although it will also be checked for single attachment controls. If you attempt to upload a larger attachment, an error is reported.

```xml
<property 
    as="xs:string"  
    name="oxf.xforms.upload.max-size-aggregate-per-control"                   
    value="1000000"/>
```

Whenever possible, it is recommended to use the Form Runner property [`oxf.fr.detail.attachment.max-size-aggregate-per-control`](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments#maximum-aggregate-attachment-size-controls) instead of this XForms property.

#### Allowed file types

\[SINCE Orbeon Forms 2017.1]

The following property specifies which file types (also known as "mediatypes") are allowed for uploaded files. For example, the following values of `image/png image/jpeg` specify that JPEG images and PDF files are allowed but no other files.

```xml
<property 
    as="xs:string"  
    name="oxf.xforms.upload.mediatypes"                           
    value="image/jpeg application/pdf"/>
```

The format is as follows:

* the value is a list of space- or comma-separated mediatype ranges
* a mediatype range is one of:
  * `*/*`: all mediatypes allowed
  * `type/*`: all mediatypes with prefix `type` are allowed (for example `image/*`)
  * `type/subtype`: specific mediatype such as `image/jpeg`, `application/atom+xml`, `video/mp4`, etc.

If `oxf.xforms.upload.mediatypes` is blank or missing, all mediatypes are allowed.

The value of `oxf.xforms.upload.mediatypes` can be overridden for a specific control using the `xxf:upload-mediatypes()` validation function.

See also [`oxf.fr.detail.attachment.mediatypes`](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments#allowed-file-types)

#### Upload progress

When you use an `<xf:upload>` control, as soon users select a file, the file is uploaded in the background from the browser to Orbeon Forms. While the file is uploaded, a progress bar is show in the browser, in place of the file selection control, as in this screenshot:

![Upload Progress](/files/-LEkC3oHHT1TStuQkRGJ)

To know how much of the file has been uploaded so far, the browser sends an Ajax request to the server, at a regular interval, asking the server what percentage of the file it has received. By default, the browser sends a request every 2 seconds. You can change this by overriding the following property. You set the value of this property as a compromise: low enough so the progress bar updates at a regular interval giving users a more accurate indication of how far along they are in the upload, and high enough to limit the number a queries made to Orbeon Forms, and thus limit the load on the server.

```xml
<property
    as="xs:integer"
    name="oxf.xforms.delay-before-upload-progress-refresh"
    value="2000"/>
```

## XForms inspector

You can enable the [XForms Inspector](/form-runner/component/xforms-inspector) for all the page in your site by setting the following property to `true` (the default is `false`):

```xml
<property 
    as="xs:boolean" 
    name="oxf.epilogue.xforms.inspector" 
    value="true"/>
```

## Appearance of radio buttons and checkboxes in review and PDF modes

\[SINCE Orbeon Forms 4.6]

Since Orbeon Forms 4.5, radio buttons and checkboxes in review and PDF modes (or for any static-readonly control appearance) shows all items as checkboxes (see the [blog post](https://blog.orbeon.com/2014/03/review-and-pdf-improvements.html)).

If you don't like this behavior, you can set the following two properties:

```xml
<!-- For checkboxes -->
<property 
    as="xs:string" 
    name="oxf.xforms.readonly-appearance.static.select"  
    value="minimal"/>
    
<!-- For radio buttons -->
<property 
    as="xs:string" 
    name="oxf.xforms.readonly-appearance.static.select1" 
    value="minimal"/>
```

These cause the radio buttons and checkboxes to display only the values selected, as text, like for dropdown menus and other section controls.

## Formatting

### For xf:output

When an `<xf:output>` is bound to a node and that node has a type, the type influences the formatting of the value. For instance, if the node has a type `xs:date`, instead of being shown as "2009-03-11", the value might be shown as "Wednesday March 11, 2009".

*NOTE: This also applies to `<xf:input>` in readonly modes.*

Out of the box, Orbeon Forms formats differently values of different types. You can change how values are formatted by setting the properties below. The value of each property is an XPath expression executed on the node bound to the `<xf:output>`. The XPath expression is expected to return a string containing the value which will be shown to the user.

```xml
<property as="xs:string" name="oxf.xforms.format.output.date"     value="..."/>
<property as="xs:string" name="oxf.xforms.format.output.time"     value="..."/>
<property as="xs:string" name="oxf.xforms.format.output.dateTime" value="..."/>
<property as="xs:string" name="oxf.xforms.format.output.decimal"  value="..."/>
<property as="xs:string" name="oxf.xforms.format.output.integer"  value="..."/>
<property as="xs:string" name="oxf.xforms.format.output.float"    value="..."/>
<property as="xs:string" name="oxf.xforms.format.output.double"   value="..."/>
```

Here are some examples of outputs with the default properties:

* `2004-01-07`
  * `xs:date`
  * Wednesday January 7, 2004
* `2004-01-07T04:38:35.123`
  * `xs:dateTime`
  * Wednesday January 7, 2004 04:38:35 UTC
* `04:38:35.123`
  * `xs:time`
  * 4:38:35 pm
* `123456.789`
  * `xs:decimal`
  * 123,456.79
* `123456.789`
  * `xs:integer`
  * 123,456
* `123456.789`
  * `xs:float` or `xs:double`
  * 123,456.789

The default formatting properties for `date`, `time`, and `dateTime` now use the current language by the `xxf:lang()` function, for example:

```xml
<property as="xs:string" name="oxf.xforms.format.output.date">
    if (. castable as xs:date) then
        format-date(xs:date(.), '[FNn] [MNn] [D], [Y]', xxf:lang(), (), ())
    else
        .
</property>
```

This means that the language that is used for the formatting is the language in effect where the control is in used, via the `xml:lang` attribute.

### For xf:input

*NOTE: With Orbeon Forms 2022.1, there is no longer support for binding `<xf:input>` to `xs:date`, `xs:time`, and `xs:dateTime` types. Instead, you should use the `<fr:date>`, `<fr:time>`, and `<fr:datetime>` controls.*

When an `<xf:input>` is bound to a node and that node has a type, the type influences the formatting of the value. For instance, if the node has a type `xs:date`, instead of being shown as "2009-03-11", the value might be shown as "Wednesday March 11, 2009".

Like for `<xf:output>`, values shown by `<xf:input>` depend on the type of the node bound to the `<xf:input>`. In this case however the `<xf:input>` must be able to not only show a value coming from an instance in a text field, but also parse a new value in that format typed in by users in the text field. Because the `<xf:input>` is able to both format and parse values, what you can do with an `<xf:input>` is more restrictive compared to what you can do with an `<xf:output>`.

You can configure formatting for `<xf:input>` with the two properties below. The value is a "mask" and follows the syntax of the Java [SimpleDateFormat](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/SimpleDateFormat.html).

The following masks are supported:

For dates (property `oxf.xforms.format.input.date`):

| Format            | Example    | Description                          |
| ----------------- | ---------- | ------------------------------------ |
| `[M]/[D]/[Y]`     | 11/5/2023  | also called "North American format"  |
| `[D]/[M]/[Y]`     | 5/11/2023  | also called "European format"        |
| `[D].[M].[Y]`     | 5.11.2023  | variation with dot separator         |
| `[D]-[M]-[Y]`     | 5-11-2023  | variation with dash separator        |
| `[M01]/[D01]/[Y]` | 11/05/2023 | force two digits for months and days |
| `[Y]-[M01]-[D01]` | 2023-11-05 | ISO format                           |

For times, see [Time component](/form-runner/component/time).

An `<xf:input>` bound to a node of type `xs:dateTime` is shown as two text fields: one for the date and one for the time. In that case, the date text field uses the formatting defined by `oxf.xforms.format.input.date` and the time text field uses the formatting defined by `oxf.xforms.format.input.time`.

The format is set as follows by default, which covers, in particular, US date and time formats:

```xml
<property 
    as="xs:string"
    name="oxf.xforms.format.input.date"
    value="[M]/[D]/[Y]"/>
```

To change to a European style days-first format for the date and a 24-hour time, you can set the following:

```xml
<property 
    as="xs:string"
    name="oxf.xforms.format.input.date"
    value="[D]/[M]/[Y]"/>
```

## Error handling

See [XForms Error Handling](/xforms/core/error-handling).

## Automatic inclusion of XBL bindings

If you write your own XBL components, you need to include the XBL in every page that uses them. To avoid this, you can define a mapping between the namespace in which your XBL components are, and a directory containing the XBL file. Then, following some naming conventions (more on this below), your XBL will be automatically found by Orbeon Forms, without you having to explicitly include it in every page that uses it.

Properties starting with `oxf.xforms.xbl.mapping` specify a mapping between directory name an a URI:

```xml
<property
  as="xs:string"
  name="oxf.xforms.xbl.mapping.acme"
  value="http://www.acme.com/xbl"/>
```

Consider an example, with the property above set:

1. Say element `<acme:button>` is found by the XForms engine, in your own `http://www.acme.com/xbl` namespace
2. Orbeon Forms looks for a property with a name that starts with `oxf.xforms.xbl.mapping` and with a value is equal to the namespace in question (here `http://www.acme.com/xbl`). In this case it finds the property `oxf.xforms.xbl.mapping.acme`.
3. Orbeon Forms extracts the part of the property name after `oxf.xforms.xbl.mapping`. In this case it is: `acme`.
4. This is used to resolve a resource called `oxf:/xbl/acme/button/button.xbl`.
   * The first part of the path is always `xbl`.
   * This is followed by the directory name found in step 3, here: `acme`.
   * This is followed by a directory with the same name as the local name of your component, containing an XBL file also with the same name as your component, here: `button/button.xbl`.
5. The resource, if found, is automatically included in the page for XBL processing

By default, all the `<fr:*>` elements are handled this way, and a mapping is already defined for those components.

## Ajax requests

### Retry mechanism for Ajax requests

Orbeon Forms relies on client-side code (running on the browser) communicating with server-side code (running on your application server). As needed, the client sends a request to the server. In case of communication failure or if the client does not receive an answer from the server after a given timeout, then the client resends the request. The default value of the timeout for Ajax requests is 30 seconds. You can change this value by setting the following property. A value of `-1` disables the retry mechanism.

```xml
<property 
    as="xs:integer" 
    name="oxf.xforms.delay-before-ajax-timeout" 
    value="30000"/>
```

The first time the client retries to send a request, it does so right away. However, the second time it waits for 5 seconds, the third time for 10 seconds, the fourth time for 15 seconds, and so on, until it reaches a maximum delay between retries of 30 seconds. You can configure the "delay increment" (by default 5 seconds) and the "maximum delay" (by default 30 seconds) with the following properties:

```xml
<property 
    as="xs:integer" 
    name="oxf.xforms.retry.delay-increment"     
    value="5000"/>
<property 
    as="xs:integer" 
    name="oxf.xforms.retry.max-delay"           
    value="30000"/>
```

Orbeon Forms handles the case where a request was successfully received and executed by the server, but the response didn't make it to the client. In those cases, the client resends the request to the server. The server detects that this particular request has been already executed, so it doesn't execute it again, and instead resends the same response that was generated the first time around.

### Login page detection

\[SINCE Orbeon Forms 4.5]

You can set the following property to a regexp. When set to a non-empty value, if an Ajax request get an unexpected page which isn't an Orbeon Forms error and matches the regexp, users will be notified, and Orbeon Forms will reload the form, which in turn is likely to take them to the login page. By default, this property is set to the empty string, meaning that Orbeon Forms doesn't try to detect login pages, and always retries Ajax requests met with an unexpected response that aren't Orbeon Forms error pages. For some background on this, see our blog post [Detecting login pages in Ajax requests](https://blog.orbeon.com/2013/12/detecting-login-pages-in-ajax-requests.html).

```xml
<property 
    as="xs:string"  
    name="oxf.xforms.login-page-detection-regexp"  
    value=""/>
```

## Preprocessing step

The XForms engine supports a preprocessing step. By default, this step is disabled. You can enable it with the following properties:

```xml
<property 
    as="xs:boolean" 
    name="oxf.epilogue.xforms.preprocessing" 
    value="true"/>
    
<property as="xs:anyURI"
    name="oxf.epilogue.xforms.preprocessing.uri"
    value="oxf:/my/preprocessing/pipeline.xpl"/>
```

The second property must point to an XPL file with a `data` input and data output. The pipeline can transform the incoming XForms.

## ARIA support in dialogs

If your forms leverage dialogs and your users are likely to use a screen reader, you might want to enable the support for [ARIA](https://www.w3.org/TR/wai-aria/) in dialogs by setting the following property to `true`. By default, the property is set to `false`, as enabling it has a cost in performance on IE.

```xml
<property 
    as="xs:boolean" 
    name="oxf.xforms.use-aria" 
    value="false"/>
```

## Cache control

The following properties, usually set as attributes, control server-side caching:

* `xxf:no-updates`
  * controls whether the dynamic state is cached
  * default: `false`
  * used to disable updating the cache for non-interactive XForms processing, such as when producing PDF output
* `xxf:single-use-static-state`
  * [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)
  * controls whether the static state must be discarded after a single use
    * if JavaScript resources are inline, the state is immediately discarded and not put in cache
    * if JavaScript resources are not inline, the state is put in cache and discarded after the static JavaScript resources are accessed
  * default: `false`
  * used to disable updating the static state cache for one-time XForms processing, such as when testing a form

## See also

* [Content-Security-Policy header](/configuration/advanced/security/content-security-policy)


# Advanced


# Session management

## Who should read this

You'll most likely be interested in the information on this page if:

* You are able to load a form produced by Orbeon Forms, but then Ajax requests sent by the browser occasionally or systematically get an HTTP 440 response.
* In your environment, requests from the browser don't immediately reach the app server running Orbeon Forms, but instead go through some other software. For instance, this is the case if you're using a reverse proxy or have your own code embedding forms produced by Orbeon Forms in your web pages.
* You want to learn more about session management in Orbeon Forms.

\[BEFORE Orbeon Forms 2022.1.5], Orbeon Forms returned an HTTP 403 error code instead of 440 for this scenario. The rest of this page refers to the 440 code; that same information is valid with the 403 code if you are using an earlier version.

## `JSESSIONID` and `UUID`

### Normal operation

1. The first time a browser requests a web page from Orbeon Forms, Orbeon Forms creates a session, and the HTTP response has a header with `Set-Cookie: JSESSIONID=123`, where `123` is a unique identifier. (The specific cookie name may differ depending on how you configured your app server, but typically `JSESSIONID` is the default and for simplicity we'll use that name in the rest of this document.) From that point, any subsequent requests issued by the browser will have a header that looks like `Cookie: JSESSIONID=123`.
2. When Orbeon Forms generates a web page for a form, it produces a unique UUID, and stores *state* in the session related to this UUID. If the user reloads the form, Orbeon Forms generated a different UUID.
3. When the Orbeon Forms client-side code sends an Ajax request, it includes that UUID in the body of the request, and the browser passes the `JSESSIONID`. On the server, Orbeon Forms uses that information to find the *state* it stored in step 1. The UUID sent by Orbeon Forms in the Ajax request looks like:

   ```xml
   <xxf:uuid>abc</xxf:uuid>
   ```

### Orbeon Forms requirement

For Orbeon Forms to operate normally, for a given web page loaded by the browser from Orbeon Forms, the `JSESSIONID` set in the HTTP response produced by Orbeon Forms (with `Set-Cookie: …`), or if none is set the `JSESSIONID` on in the HTTP request received by Orbeon Forms must also be the one Orbeon Forms receives in all subsequent Ajax requests issued by that page.

![Which HTTP requests/responses we are interested in](/files/-LEkC7YvuzcRRYZeuDtS)

Note that those requirements apply to the HTTP requests and responses sent to and coming from Orbeon Forms. As illustrated in the above diagrams, if you have reverse proxy or embedding code those will be different from the HTTP requests and responses made by and received by the browser, and the `JSESSIONID` (or equivalent) used between the browser and the proxy / embedding code is likely to be different from the `JSESSIONID` used between the proxy / embedding code and Orbeon Forms. Again, Orbeon Forms' requirement apply to the latter.

## HTTP 440

When Orbeon Forms receives an Ajax request (see the third step in the previous section), if it can't find the state associated to the UUID in the session, it responds with an [HTTP 440 error](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_client_errors) (Login Timeout).

### Normal cases

The following 2 cases can happen resulting in a 440 response, but those situations should happen very rarely:

1. The user's session has expired – As long as users keep a form open in their browser, by default the Orbeon Forms client-side code makes sure to send a "session heartbeat" Ajax request before the session expires to keep it alive. However:
   * It's possible for you to disable the [session heartbeat](/configuration/advanced/state-handling#session-heartbeat) feature. If you do, it is possible for the session to expire even if users keep a browser window with the form open.
   * In situations where the browser looses connectivity to the server for an extended period of time, the session heartbeat mechanism might not be able to contact the server before the session expires. For instance, this could happen if a user were to load a form on her laptop, close the lid, commute home, open the lid, and try to continue filling out the same form.
2. A user loads the form, the server running Orbeon Forms is restarted, and then the user tries to interact with the form.

In both cases, if, after getting the error, users reload the form and get the error again, then you might have hit one of the "problematic cases" described in the section below.

### Problematic cases

If you're getting 440 errors more regularly, and you have software sitting between the browser and Orbeon Forms, it is a sign that this software does not handle cookies properly. That software can be:

* A reverse proxy, for load balancing, instance used to provide authentication, single sign-on, serve assets more efficiently, or provide security services.
* Code running in a web app used to embed a form produced by Orbeon Forms into a page produced by the web app.

In those cases, you can solve the issue by either:

* When using a [load balancer](/configuration/advanced/clustering) that dispatches requests across multiple servers, ensure session affinity (sticky sessions) is configured so that the initial page load and all subsequent Ajax requests from the same page load are routed to the same server instance. Some load balancers may, even with session affinity configured, decide to send a subsequent request to a different server if they determine that the original target server is too slow to respond and might not be operational. Unless you have set up session replication, you must ensure that your load balancer configuration prevents this failover behavior from occurring.
* If you have own Java code to embed forms created with Form Builder, switch to using the built-in [Form Runner Java Embedding API](/form-runner/link-embed/java-api), which will handle cookies properly.
* Debug and fix the issue is the said software, armed with a better understanding of the Orbeon Forms' requirements when it comes to cookies, based on this information on this page.
* If using Tomcat, in Tomcat's directory edit `conf/context.xml`, and add `sessionCookiePath="/"` on the root element, so it looks as follows: `<Context sessionCookiePath="/">`. This will make the job of any reverse proxy or embedding code you might have much simpler, and could help you get around bugs in that code.


# State handling

## Audience

The audience for this page is developers or advanced system administrators.

## What is this about?

The Orbeon Forms XForms engine requires keeping server state while the user is interacting with a form. Such state includes the current values of XForms instances, selected repeated elements, and more. With Orbeon Forms, by default XForms state information is stored on the server.

## How does XForms state handling work?

See [Review XForms cache architecture #1718](https://github.com/orbeon/orbeon-forms/issues/1718) for some pointers.

## Configuring state handling

### XForms document and static state caches

A property controls whether the XForms engine should try to optimize state reconstruction by using a cache. This property is typically set to `true`:

```xml
<property
    as="xs:boolean"
    name="oxf.xforms.cache.document"
    value="true">
```

*NOTE: We might get rid of this property in the future as there is no use case for setting it to false.*

If `oxf.xforms.cache.document` is set to `true`, the number of XForms documents that can be held in that document cache at a given time is configured with the following property:

```xml
<property
    as="xs:integer"
    name="oxf.xforms.cache.documents.size"
    value="50">
```

*NOTE: This property is global and cannot be overridden on a page by page basis.*

*NOTE: If a user loads the same XForms page twice, two entries are created in the document cache.*

The static state (or static analysis) of an XForms page is stored in a separate cache, controlled with this property:

```xml
<property
    as="xs:integer"
    name="oxf.xforms.cache.static-state.size"
    value="50">
```

This property should be set to a number equal to or greater than the number of distinct XForms pages you have.

If you have, say, in production, 100 separate form definition, the size should ideally be 100.

It is tempting to make the cache very large, but because it is a strict cache, there is a risk of running out of memory. So you should make the cache just a little bit larger than the number of forms in production.

### XForms state store configuration

When entries are evicted from the XForms document cache or the XForms static state cache, they are migrated to the XForms state store.

The XForms state store is configured via [`oxf:/config/ehcache.xml`](https://github.com/orbeon/orbeon-forms/blob/master/src/main/resources/config/ehcache.xml).

## Session heartbeat

See also the [blog post](https://blog.orbeon.com/2008/01/keeping-your-session-alive_29.html).

If you happen to leave a browser window open on your computer, chances are that you will get back to that window and keep using the application. The last thing you want to happen when you come back is lose your session and therefore your data.

This is not always a correct guess of course: you may just happen to leave a window or tab open without planning to use it again. Conversely you may have a page which is not actually visible, for example in your browser history, yet you will come back to it. This approach wouldn't be good for banking applications either. Still, in many situations, such as filling-out large forms, it sounds like a good idea to keep your session alive for open pages.

To achieve this goal you could make all server sessions longer. However this is harder to configure for the application developer, and this won't discriminate between pages that are actually open on a client and the ones that are not. And while it may be ideal to have infinitely long sessions, unfortunately many applications are not ready for this kind of approach.

So Orbeon Forms supports a "session heartbeat" feature. Here is how this works:

* When this feature is enabled (the default), an open XForms page in a client browser regularly pings the server through Ajax to keep the current server session alive.
* The ping delay is automatically computed based on the server's session timeout. The client pings the server at 80% of the session expiration time after the last interaction with the server.
* We are careful not to hit the XForms engine too much, in fact we do a very limited amount of work on the server for each ping, so they should run fast.
* XForms state information for pages hit with the heartbeat just migrates to the disk store over time if RAM is used by other pages, so keeping even large numbers of pages open should not have any negative impact on server RAM.
* When a user gets back to using the page, state information migrates back from disk to RAM, and the page will be live again.
* Sessions do eventually expire as nobody keeps a browser open forever.

Note that whenever an application keeps sessions alive for a long time, it is a good idea to keep as little data as possible in the session. The Orbeon Forms XForms engine itself uses a global state store and does not use session objects for storage, but do remember to keep your sessions small!

The session heartbeat should help prevent many occurrences of "session expired" error messages. As an Orbeon Forms application developer you don't have to worry about anything: the session heartbeat is enabled by default. You can however explicitly configure it globally in `properties-local.xml`:

```xml
<property
    as="xs:boolean"
    name="oxf.xforms.session-heartbeat"
    value="true"/>
```

## Session expiration dialog

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

When the session heartbeat feature is disabled with:

```xml
<property
    as="xs:boolean"
    name="oxf.xforms.session-heartbeat"
    value="false"/>
```

In that case, Orbeon Forms can display a dialog to the user when the session is about to expire. This dialog can be enabled with the following property:

```xml
<property
    as="xs:boolean"
    name="oxf.fr.detail.session-expiration-dialog.enabled.*.*"
    value="true"/>
```

The user can then manually renew the session if needed.

![Session about to expire](/files/ATN5LEHAickcvulDHB7o)

It is possible to configure when this dialog is shown using the following property, which is a percentage of the session expiration time, with a default value of 80%:

```xml
<property
    as="xs:integer"
    name="oxf.xforms.session-expiration.trigger"
    value="80"/>
```

For example, if the session expiration time is one hour, the dialog will be shown 48 minutes after the last interaction with the server.

If the user doesn't renew the session, the session will expire and the user will see the following dialog:

![Session expired](/files/jqZhOiaUFV0WxFmnPemb)

This dialog is also shown if the user logs out from another page/tab from the same session.

If the session heartbeat feature is enabled, the dialog won't be shown, independently of the value of `oxf.fr.detail.session-expiration-dialog.enabled.*.*`. In that case, the `oxf.xforms.session-expiration.trigger` property determines when the session heartbeat is sent to the server.

Behind the scene, Orbeon Forms synchronizes multiple Orbeon Forms tabs or windows within a same browser session. This means that activity in one tab will keep the session alive for all tabs.

## Browser navigation (back and forward) handling

\[DEPRECATED SINCE Orbeon Forms 2021.1]

### Deprecation

The way web browsers should handle history, in particular "back" and "forward", is subject to interpretation. However, one consistent such interpretation is that going "back" or "forward" in the browser history should take the user to something as close as possible as what the user saw last, whether for web "pages" or web "apps".

As of late 2021, the way web browsers handle history confirms this interpretation. In particular, the so-called ["bfcache"](https://web.dev/bfcache/) helps restore the state of pages and applications as they were last seen by the user.

One of the purpose of the `xxf:revisit-handling="reload"` setting described below was to prevent the possibility that a user could navigate back, see again data that was entered into a form, and resubmit it. Forcing a reload of the page upon browser back alleviated that issue, as the form would then be cleared. However, this also goes against the more accepted philosophy of navigation in history described above.

Since Orbeon Forms 2020.1, we recommend instead using workflow features to help with this. When saving or submitting form data:

1. Set the workflow stage to a value such as `saved` or `submitted`.
2. In the form definition, set the global "Read-only" formula to mark the form as read only when it is in `saved` or `submitted` stage.

This means that even if the user navigate to a confirmation page upon submission, and then navigates back to the form, the form will be shown (and if necessary restored) in a readonly mode. This achieves the intent without breaking the philosophy of navigation.

See [Workflow stage](/form-runner/features/workflow-stage) for more details.

### How it works

When visiting an XForms page by using your browser's Back and Forward buttons, or other browser-history mechanisms, Orbeon Forms by default restores the appearance of that page as it was when you left it. (Browsers don't automatically handle this behavior with Ajax applications!) This behavior best matches the usual user experience obtained when navigating regular web pages.

In certain situations, it can be useful instead to ask the XForms page to reload entirely. You control this by using the `xxf:revisit-handling` attribute on the first XForms model of the page you want to reload. This attribute supports two values: `restore` (the default) and `reload`. Example:

```xml
<xf:model xxf:revisit-handling="reload">
```

*NOTE: It is recommended to use the `reload` value carefully, as reloading pages upon browser navigation often does not match the expectation of the user.*

## See also

* [Orbeon Forms Caches](/installation/caches)


# Client-side error handling

## Disabling the standard error dialog

By default, when an Ajax error happens, Orbeon Forms shows users an error dialog.

![](/files/-LEkC3H3NSL2hoPXaakE)

You can disable this behavior by adding this property to your `properties-local.xml`:

```markup
<property
    as="xs:boolean"
    name="oxf.xforms.show-error-dialog"
    value="false"/>
```

## JavaScript event handler

Orbeon Forms exposes a custom JavaScript event: `ORBEON.xforms.Events.errorEvent`.

By default a dialog is shown to the user when an error is intercepted. If you prefer to show your own dialog or to implement some other behavior in case of error, most likely you will want to:

* Disable the default error dialog by setting the [`oxf.xforms.show-error-dialog`](/xforms/core/error-handling#error-dialog) property to `false`.
* Register your own listener on `ORBEON.xforms.Events.errorEvent`.

You can register your own listener on that event, and when fired, send users to a page you choose, as done in the following snippet, which sends users to the Orbeon home page:

```javascript
ORBEON.xforms.Events.errorEvent.subscribe(function(eventName, eventData) {
    // your code here
});
```

### Example

In case the user session expires, or some other error happens, you would like to redirect them a page you created that will, for instance, tell users to log in and try again, and if the problem persists to contact customer support.

```javascript
ORBEON.xforms.Events.errorEvent.subscribe(function(eventName, eventData) {
    window.location.href = "http://www.example.org/";
});
```

## Providing your own dialog

### File location

By default, the source markup for this dialog is available under:

```
oxf:/config/error-dialog.xml
```

This file is located inside the following jar file:

* \[SINCE Orbeon Forms 2016.3] `WEB-INF/lib/orbeon-core.jar`
* \[UP TO Orbeon Forms 2016.2] `WEB-INF/lib/orbeon-resources-private.jar`

You can override it in two ways:

* Globally, by placing your own `error-dialog.xml` file under `WEB-INF/resources/config`.
* Per application, by placing your own `error-dialog.xml` file under `WEB-INF/resources/apps/$app`, where `$app` stands for the name of your application.

### Localization

\[SINCE Orbeon Forms 2020.1] Should you need to provide multiple versions of the error dialog in different languages, you can do so by having multiple `<div class="xforms-error-panel">` each with its own `lang` attribute (see below for the full structure of the HTML you need to provide). Orbeon Forms will try to get the error panel with the `lang` attribute matching the current language, and if it can't find one it will pick the first error panel.

### Example: default dialog

The default dialog provides classes allowing for opening/closing a details section:

```markup
<div xmlns="http://www.w3.org/1999/xhtml" class="xforms-error-dialogs">
    <div class="xforms-error-panel xforms-initially-hidden" role="dialog" aria-labelledby="error-dialog-title">
        <div class="hd" id="error-dialog-title">An error has occurred</div>
        <div class="bd">
            <p>
                You may want to try one of the following:
            </p>
            <ul>
                <li><a class="xforms-error-panel-close">Close this dialog</a> and continue to use this page.</li>
                <li><a class="xforms-error-panel-reload">Reload this page</a>. Note that you will lose any unsaved changes.</li>
                <li>
                    <p>
                        If the above does not work, try reloading the page yourself. Note that you will lose any unsaved changes:
                    </p>
                    <ul>
                        <li>
                            With Firefox: hold down the <code>shift</code> key and click the Reload button in your browser toolbar.
                        </li>
                        <li>
                            With Safari and Chrome: click the Reload button in your browser toolbar.
                        </li>
                        <li>
                            With Internet Explorer: hold down the <code>control</code> key and click the Reload button in your browser toolbar.
                        </li>
                    </ul>
                </li>
                <li>Return <a href="/">home</a>.</li>
            </ul>
            <div class="xforms-error-panel-details-hidden">
                <p>
                    <a class="xforms-error-panel-show-details">
                        <img src="/ops/images/xforms/section-closed.png" alt="Show Details"/>
                        <span>Show details</span>
                    </a>
                </p>
            </div>
            <div class="xforms-error-panel-details-shown xforms-disabled">
                <p>
                    <a class="xforms-error-panel-hide-details">
                        <img src="/ops/images/xforms/section-opened.png" alt="Hide Details"/>
                        <span>Hide details</span>
                    </a>
                </p>
                <div class="xforms-error-panel-details"/>
            </div>
        </div>
    </div>
    <div class="xforms-login-detected-dialog modal hide fade" tabindex="-1" role="dialog" aria-hidden="true">
        <div class="modal-header">
            <h4>Reloading form</h4>
        </div>
        <div class="modal-body">
            <p>
                This form has to be reloaded. This most likely happened because your session has expired, which might
                take to the login page. (If you think that you shouldn't see this message and that the problem persists,
                please contact support.)
            </p>
        </div>
        <div class="modal-footer">
            <button class="btn btn-primary">OK</button>
        </div>
    </div>
</div>
```

### Example: minimal dialog

At a minimum, the file should contain the following structure:

```markup
<div xmlns="http://www.w3.org/1999/xhtml"
     class="xforms-error-panel xforms-initially-hidden">
    <div class="hd">An error has occurred</div>
    <div class="bd">
        <p>
            Sorry, a serious error has occurred!
        </p>
        <div class="xforms-error-panel-details-hidden xforms-disabled">
            <p>
                <a class="xforms-error-panel-show-details"/>
            </p>
        </div>
        <div class="xforms-error-panel-details-shown xforms-disabled">
            <p>
                <a class="xforms-error-panel-hide-details"/>
            </p>
            <div class="xforms-error-panel-details"/>
        </div>
    </div>
</div>
```

### CSS configuration

Advanced developers can configure the appearance of the error dialog. In most cases overriding CSS definitions should be enough.


# Clustering and High Availability

## Rationale

In certain environments calling for high availability, or when one server running Orbeon Forms isn't enough to handle the load, you will want to deploy Orbeon Forms on multiple servers. Those servers are often called *cluster*.

## Guidelines

When deploying Orbeon Forms in a cluster, you'll want to make sure that:

1. Only caches and session information is maintained at the level of each Orbeon Forms instance, and critical information is stored on a separate server, or servers, typically a database server. If using Form Runner, you'll want to set it up to [point to a separate relational database](/form-runner/persistence/relational-db) or separate eXist database, running on another server, or servers.
2. You setup your load balancer to use **sticky sessions**. Whether you're using a hardware or software load balancer, it will decide for each HTTP request coming in, which server in the cluster should handle it. Using sticky sessions ensures that all the requests for each specific session are sent to the same Orbeon Forms instance, which is essential since Orbeon Forms keeps track of session information on the server. For instance, see how to [setup sticky session with AWS Elastic Load Balancing](http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-sticky-sessions.html).

![Load balancer, Orbeon Forms servers, and database server](/files/-LEkC-lkjBg9e1YWtO2e)

## Replication

You can configure more advanced clusters using replication. See [Replication](/installation/replication).


# Configuring a Form Runner eXist database

## Deprecation

\[SINCE Orbeon Forms 2019.1]

Using the eXist database with Orbeon Forms is deprecated. We recommend using one of the supported [relational databases](/form-runner/persistence/relational-db) for production.

## Removal

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

The eXist database is no longer included in the standard Orbeon Forms WAR file. If you are using that version or newer, you don't need to explicitly remove eXist.

## Rationale

By default, Orbeon Forms ships with an embedded eXist-db database. For production, this is not an ideal setup and it is better to have a separate eXist-db database. After you download and install eXist-db, you should follow the instruction below to set it up, and let Orbeon Forms know how it should connect to eXist-db.

## Setup

### Configuring full-text indexing in eXist-db

Orbeon Forms uses the [Lucene-based full-text index](http://exist-db.org/exist/apps/doc/lucene.xml). Without this configuration, free-text search in the Form Runner Summary page won't work. After you install eXist-db, store a `collection.xconf` file as follows in the eXist-db, under `system/config/db/orbeon/fr/`. You can also find the [latest version of this file on GitHub](https://github.com/orbeon/orbeon-forms/blob/master/data/system/config/db/orbeon/fr/collection.xconf).

```xml
<collection xmlns="http://exist-db.org/collection-config/1.0">
    <index>
        <!-- Disable the standard full text index -->
        <fulltext default="none" attributes="no"/>
        <!-- Lucene index is configured below -->
        <lucene>
            <analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer"/>
            <!-- We want to index the content of all form elements -->
            <text match="//*"/>
        </lucene>
    </index>
</collection>
```

### Configuring Form Runner

Set the `oxf.fr.persistence.exist.exist-uri` in your Orbeon Forms `properties-local.xml` to tell Form Runner how to connect to your eXist-db REST API, using the appropriate URL.

```xml
<property
    as="xs:anyURI"
    name="oxf.fr.persistence.exist.exist-uri"
    value="http://orbeon:secret@localhost:8090/exist/rest/db/orbeon/fr"/>
```


# Creating a production WAR

## Rationale

The standard Orbeon Forms WAR comes with demo apps and forms. For production, you can safely remove some of that from the WAR file.

## What can be removed

For most deployments, the following can be removed:

* `xforms-jsp`: demo JSP files
* `WEB-INF/resources/apps`: demo apps
* `WEB-INF/resources/forms/orbeon/controls`: some demo forms resources
  * NOTE: These have been removed in recent versions of Orbeon Forms.
* `WEB-INF/resources/forms/orbeon/dmv-14`: some demo forms resources
  * NOTE: These have been removed in recent versions of Orbeon Forms.
* `orbeon-cli.jar` and `commons-cli-1_0.jar`: for command-line XPL
  * NOTE: These have been removed in recent versions of Orbeon Forms.

## Removing Form Builder

Form Builder is packaged as a separate JAR file:

`WEB-INF/lib/orbeon-form-builder.jar`

If you don't need Form Builder in an installation, you can simply remove that JAR file.

## Removing the built-in SQLite database

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

Out-of-the-box, Orbeon Forms includes an SQLite embedded database with multiple demo forms. This setup is designed for a quick start, but for development or production use, you should configure Orbeon Forms to utilize a separate relational database. For more information, see [Relational Database](/form-runner/persistence/relational-db).

In particular, make sure you also [disable the embedded SQLite database](/form-runner/persistence/relational-db#disabling-the-embedded-sqlite-provider).

You can leave the SQLite library and database in place if you are not using them, but you can also opt to remove them. If you do so, in addition to the above configuration property, you remove the following:

* `WEB-INF/lib/sqlite-jdbc-*.jar`: the SQLite JDBC driver and implementation
* `WEB-INF/orbeon-demo.sqlite`: the SQLite database with demo forms

## Removing the built-in eXist database

### Deprecation

\[SINCE Orbeon Forms 2019.1]

Using the eXist database with Orbeon Forms is deprecated. We recommend using one of the supported [relational databases](/form-runner/persistence/relational-db) for production.

### Removal

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

The eXist database is no longer included in the standard Orbeon Forms WAR file. If you are using that version or newer, you don't need to explicitly remove eXist.

### Steps

The version of eXist which ships with Orbeon Forms is intended for demo purposes only. If you plan to use eXist, we recommend setting up an external eXist database.

To remove the embedded eXist:

* remove
  * `WEB-INF/lib/exist-*.jar`: the embedded eXist implementation and its dependencies
  * `WEB-INF/exist-data`: data for the embedded eXist XML database
* in `WEB-INF/web.xml`:
  * remove all `<servlet>`, `<servlet-mapping>`, `<filter`, and `<filter-mapping>` which refer to eXist

If you want to point to an external eXist database, set the following property:

```xml
<property
    as="xs:anyURI"
    name="oxf.fr.persistence.exist.uri"
    value="$urlToExistRestService"/>
```

And replace `$urlToExistRestService` with the actual URL of the eXist REST service.

If you don't need an eXist database at all, in `properties-local.xml`, add the following to fully disable the eXist persistence implementation.:

```xml
<property
    as="xs:boolean"
    name="oxf.fr.persistence.exist.active"
    value="false"/>
```

Then set a property to select the persistence implementation you are using, for example, for SQL Server:

```xml
<property
    as="xs:string"
    name="oxf.fr.persistence.provider.*.*.*"
    value="sqlserver"/>
```

## See also

* [Database setup](/installation#database-setup)
* [Using Form Runner with a relational database](/form-runner/persistence/relational-db)


# Environments

## Types of environments

Orbeon Forms is often installed in different environments, with one or more servers dedicated to each environment. For instance:

1. A development environment — On which form authors create and test the forms they are working on.
2. A staging environment — On which testing is performed before deployment.
3. A production environment — Accessed by end users to fill out forms.

The above scenario is typical, but there is nothing in Orbeon Forms that dictates you have those 3 environments. Your setup could include more or less different environments depending on your needs.

## Database setup

So you get the full benefit of having different environment, you should setup the instances of Orbeon Forms in different environments to use different databases, or at least different database schemas, so you can see each environment as a silo, and never have, say, form authors accessing the development environment change any data related to the staging or production environment.

## Migration of form definitions

At some point, you might want to graduate data, typically forms, from one environment to the next. For instance, when form authors are done, moving forms from the development to staging. Or once testing is done, from staging to production. This can be achieved with the [Forms Admin page](/form-runner/form-runner-pages/forms-admin-page).


# JavaScript and CSS assets

## Minimal asset resources

Most JavaScript and CSS assets used by the XForms engine are available in two versions:

* A full version, which may contain comments, spaces, longer identifiers, etc.
* A minimal version, which is usually much smaller

Both versions work exactly the same. For development and debugging of the XForms engine itself, the full version is easier to work with. But if you never work directly with these JavaScript and CSS files, as well as for deployment, the minimal versions are recommended as they will load faster in the user's web browser.

You enable minimal resources in `properties-local.xml` as follows:

```xml
<property
  as="xs:boolean"
  name="oxf.xforms.minimal-resources"
  value="true"/>
```

Default:

* `prod` mode: `true`
* `dev` mode: `false`

## Combined asset resources

Starting with Orbeon Forms 2020.1, disabling combined resources is deprecated. Disabling combined resources already doesn't work when embedding forms, whether with the [Java embedding API](/form-runner/link-embed/java-api), the [JavaScript embedding API](/form-runner/link-embed/javascript-api), or the [Liferay proxy portlet](/form-runner/link-embed/liferay-proxy-portlet), and future versions of Orbeon Forms are likely to completely drop the support for non-combined resources.

### Rationale

Serving external CSS and JavaScript assets can have a high performance cost on page loads. This is particularly important with the intensive use of JavaScript in Orbeon Forms. In particular, it can be shown that serving many small files is slower than serving a single large file.

This is why Orbeon Forms supports the option of combining the multiple JavaScript and CSS files required for a given XForms page into one or two JavaScript files and one or two CSS file.

*NOTE: In theory, HTTP pipelining can improve very much on this, but this is (very unfortunately) useless in practice at the time of writing because browsers do not implement it or do not enable it by default. HTTP/2 might help solve this.*

### How it works

There are 3 categories of asset resources:

* built-in XForms engine resources, like `xforms.js`
* XBL components resources, like `grid.js`
* user resources (placed in the `<head>` element

Resources are split into 2 groups:

* baseline resources, which include
  * main built-in XForms engine resources
  * XBL resources for components specified with the `oxf.xforms.resources.baseline` property
* other resources, which include
  * other built-in XForms engine resources, such as for the Formatted Text / Rich Text Editor or tree selection
  * XBL resources not part of the baseline
  * user resources

The idea is that this way, in an application with multiple pages:

* a large baseline of resources is loaded and cached once and for all
* a smaller incremental set of extra resources is loaded for each individual page

If all the resources belong to the baseline, only one JavaScript and one CSS files are produced. Otherwise, two JavaScript and two CSS files are produced.

The URLs produced identify the resources needed by the page, for example:

```xml
<link rel="stylesheet"
  href="/orbeon/xforms-server/orbeon-8b3d174e93f2d74146c9b2a5356bd5b8b5e196f8.css"
  type="text/css" media="all">

<link rel="stylesheet"
  href="/orbeon/xforms-server/orbeon-e01218f47e6ecd43fb1c2295ccae2e688c443b62.css"
  type="text/css" media="all">

<script type="text/javascript"
  src="/orbeon/xforms-server/orbeon-19e8d10829ccdd0d9aec779c0c89e5d1f57764dd.js">

<script type="text/javascript"
  src="/orbeon/xforms-server/orbeon-3a3469eca94e6df9783e742067f464b57de4e2f3.js"/>
```

When the Orbeon Forms XForms server receives a request for a combined resource, it determine what files need to be combined and outputs them all together. Furthermore, for CSS files, all URLs referred to with `url()` are rewritten, so that links to images, in particular, remain correct.

Some CSS and JavaScript files are never included into aggregated resources:

* resources with `f:url-norewrite="true":` \<xh:link rel="stylesheet" href="/style.css" f:url-norewrite="true"/>\`
* resources with an absolute URL, such as:\
  `<xh:link rel="stylesheet" href="https://example.org/style.css"/>`
* CSS resources with a `media` attribute that is present but different from "all":\
  `<xh:link rel="stylesheet" href="/style.css" media="print"/>`

### Configuration

#### Basic configuration

You enable this feature in `properties-local.xml` as follows:

```xml
<property
  as="xs:boolean"
  name="oxf.xforms.combine-resources"
  value="true"/>
```

When this is enabled, Orbeon Forms combines JS and CSS resources.

Default:

* `prod` mode: `true`
* `dev` mode: `false`

Mappings between resources URLs and the resources are stored in the `xforms.resources` cache, configured in [`RESOURCES/config/ehcache.xml`](https://github.com/orbeon/orbeon-forms/blob/master/src/main/resources/config/ehcache.xml).

#### Baseline of XForms assets

\[SINCE Orbeon Forms 2017.1]

*NOTE: These properties are mainly intended for internal use. Proceed with caution.*

\[SINCE Orbeon Forms 2026.1] The Bootstrap 2 assets were replaced with Bootstrap 5 and removed. If you override this property, update the Bootstrap paths. They contain the Bootstrap version, so they change whenever Bootstrap is upgraded.

The default baseline of XForms assets is configured with the `oxf.xforms.assets.baseline` property. Here is an example:

```xml
<property as="xs:string"  name="oxf.xforms.assets.baseline">
    {
      "css": [
        { "full": "/ops/yui/container/assets/skins/sam/container.css",                   "min": false },
        { "full": "/ops/yui/calendar/assets/skins/sam/calendar.css",                     "min": false },
        { "full": "/apps/fr/style/bootstrap-5.3.8-orbeon/css/bootstrap.css",             "min": true  },
        { "full": "/apps/fr/style/form-runner-bootstrap5-override.css",                  "min": false },
        { "full": "/apps/fr/style/font-awesome/css/font-awesome.css",                    "min": true  },
        { "full": "/config/theme/xforms.css",                                            "min": false },
        { "full": "/config/theme/error.css",                                             "min": false },
        { "full": "/ops/nprogress-0.2.0/nprogress.css",                                  "min": false },
        { "full": "/ops/css-loader/css-loader.css",                                      "min": false }
      ],

      "js": [
        { "full": "/ops/jquery/jquery-3.6.0.js",                                         "min": true  },
        { "full": "/apps/fr/style/bootstrap-5.3.8-orbeon/js/bootstrap.bundle.js",        "min": true  },
        { "full": "/ops/javascript/orbeon/util/jquery-orbeon.js",                        "min": true  },
        { "full": "/ops/nprogress-0.2.0/nprogress.js",                                   "min": true  },
        { "full": "/ops/bowser/bowser.js",                                               "min": true  },
        { "full": "/ops/mousetrap/mousetrap.min.js",                                     "min": false },
        { "full": "/ops/fetch/promise-polyfill.min.js",                                  "min": false },
        { "full": "/ops/fetch/fetch.umd.js",                                             "min": false },
        { "full": "/ops/fetch/abortcontroller-umd-polyfill.js",                          "min": false },

        { "full": "/ops/yui/yahoo/yahoo.js",                                             "min": true  },
        { "full": "/ops/yui/event/event.js",                                             "min": true  },
        { "full": "/ops/yui/dom/dom.js",                                                 "min": true  },
        { "full": "/ops/yui/element/element.js",                                         "min": true  },
        { "full": "/ops/yui/animation/animation.js",                                     "min": true  },
        { "full": "/ops/yui/dragdrop/dragdrop.js",                                       "min": true  },
        { "full": "/ops/yui/container/container.js",                                     "min": true  },
        { "full": "/ops/yui/examples/container/assets/containerariaplugin.js",           "min": true  },
        { "full": "/ops/yui/calendar/calendar.js",                                       "min": true  },
        { "full": "/ops/yui/slider/slider.js",                                           "min": true  },

        { "full": "/ops/javascript/underscore/underscore.js",                            "min": true  },

        { "full": "/ops/javascript/xforms.js",                                           "min": true  },
        { "full": "/ops/javascript/orbeon/xforms/server/AjaxServer.js",                  "min": true  },
        { "full": "/ops/javascript/orbeon/xforms/control/CalendarResources.js",          "min": true  },
        { "full": "/ops/javascript/orbeon/xforms/control/Calendar.js",                   "min": true  },
        { "full": "/ops/javascript/orbeon/xforms/control/Placeholder.js",                "min": true  },
        { "full": "/ops/javascript/orbeon/xforms/controls/Placement.js",                 "min": true  },
        { "full": "/ops/javascript/orbeon/xforms/controls/Help.js",                      "min": true  },
        { "full": "/ops/javascript/orbeon/xforms/controls/Hint.js",                      "min": true  },

        { "full": "/ops/javascript/scalajs/orbeon-xforms-web.js",                        "min": false }
      ],

      "xbl": [
        "fr:number",
        "fr:date"
      ]
    }
</property>
```

Minimal versions:

* are enabled when `"min"` is set to `true` and
* assume that an asset named `file.min.css` is the minimal version for `file.css`.

\[SINCE Orbeon Forms 2021.1]

The `"xbl"` key allows specifying a JSON array of XBL components, indicated by direct name (QName), whose assets must also be included.

This capability renders the `oxf.xforms.resources.baseline` property deprecated.

#### Excluding assets

\[DEPRECATED SINCE Orbeon Forms 2019.2]

The `oxf.xforms.assets.baseline.excludes` property can be used to exclude assets.

It doesn't make much sense to use this property in a properties file. Instead, it should be used via an attribute on `xf:model`.

```xml
xxf:assets.baseline.excludes="/ops/javascript/scalajs/orbeon-xforms.js /ops/javascript/scalajs/orbeon-xforms-launcher.js"
```

This property is deprecated. Use `oxf.xforms.assets.baseline.updates` instead.

#### Updating assets

\[SINCE Orbeon Forms 2019.2]

The `oxf.xforms.assets.baseline.updates` property can be used to exclude and add assets.

It doesn't make much sense to use this property in a properties file. Instead, it should be used via an attribute on `xf:model`.

Each asset must be prefixed with a `+` or a `-` to indicate whether the asset as removed from or added to the baseline.

```xml
xxf:assets.baseline.updates="-/ops/javascript/scalajs/orbeon-xforms.js +/apps/fr/resources/scalajs/orbeon-form-runner.js"
```

#### Baseline of XBL components assets

\[DEPRECATED SINCE Orbeon Forms 2021.1]

The baseline of resources is configured as follows:

```xml
<property
  as="xs:string"
  name="oxf.xforms.resources.baseline"
  value="fr:tabview fr:databound-select1"/>
```

The value consists of a list of qualified names referring to XBL components. Resources for the components specified are always included in every page, whether the component is used by the page or not.

Starting Orbeon Forms 2021.1, use `oxf.xforms.assets.baseline` instead.

#### Disk caching of combined resources

\[UNTIL Orbeon Forms 2019.1]

In addition, you can enable caching on disk of combined resources with:

```xml
<property
  as="xs:boolean"
  name="oxf.xforms.cache-combined-resources"
  value="true"/>
```

This cache works differently from other Orbeon Forms caches, as the result is stored in the resources, typically under:

```xml
WEB-INF/resources/xforms-server/
```

One benefit of this mechanism is that it allows making such combined files to be served by an Apache front-end.

*NOTE: This property is removed with Orbeon Forms 2019.2. HTTP reverse proxies can cache resources without having access to a file on disk, which makes this feature obsolete.*

## Versioned asset resources

### Availability

This is an [Orbeon Forms PE](https://www.orbeon.com/download) feature.

This feature is enabled by default in Orbeon Forms PE.

With the introduction of [run modes](/configuration/advanced/run-modes), the feature is disabled by default in `dev` mode but enabled by default in `prod` mode.

### Rationale

To further improve caching efficiency, Orbeon Forms supports enabling *versioned resources*. Usually, a resource such as a CSS, JavaScript or image file, is served through URLs like this one:

```xml
http://localhost:8080/orbeon/xforms-server/orbeon-a8adf9b6d7d9e9ed23060a279fabed50bd829236.js
```

When configuring caching on the server, for example by using an Apache front-end, you may face a dilemma:

* Caching aggressively (with an expiration date far in the future and no revalidation) so that the client asks for the resource as rarely as possible. Doing so may cause resources on the client to be out of date.
* Caching for a shorter period of time or by forcing revalidation so that your client always has a fresh version of the resources. Doing so may cause longer page loads and more load on the server.

Orbeon Forms solves this by providing the option of using versioned resources, that is inserting automatically a version number within resource URLs.

### Configuration

#### oxf.resources.versioned

You enable versioned resources in `properties-local.xml`:

```xml
<property as="xs:boolean" name="oxf.resources.versioned" value="true"/>
```

With Orbeon Forms PE, versioned resources are enabled by default in `prod` mode.

#### oxf.resources.version-number

You can configure an optional application version number for your own resources:

```xml

<property as="xs:string" name="oxf.resources.version-number" value="1.6.3"/>
```

Note that if this property is commented out or missing, no versioning takes place for your application resources even if `oxf.resources.versioned` is set to `true`.

#### oxf.xforms.resources.encode-version

The Orbeon Forms version number is not exposed by default to users in the `prod` run mode. You can change this by setting the following property to `false`:

```xml
<property as="xs:boolean" name="oxf.xforms.resources.encode-version" value="false"/>
```

When this is set to `true`, the version number is HMAC-encoded with the configured password. This means the version number is stable, but cannot be guessed.

### Behavior

With versioned resources enabled, resources are served with URLs as follows:

```xml
http://localhost:8080/orbeon/xforms-server/58a8724010cd6fbe3ae7298de0a5c6e9dafc990b/orbeon-aa144f9fcd394054c18536a679b02ad4553e0048.js
```

The XForms Server component, which serves the resource in this case, sets an expiration date far in the future. For example, this is the description of the cache entry in the Firefox `about:cache` page:

```
Key: http://localhost:8080/orbeon/xforms-server/58a8724010cd6fbe3ae7298de0a5c6e9dafc990b/orbeon-aa144f9fcd394054c18536a679b02ad4553e0048.js  
Fetch count: 2
Last-Modified: Fri, 23 Jun 2017 06:23:34 GMT
Expires: Fri, 20 Jul 2018 09:35:03 GMT
```

This means that the resource can effectively be cached "for ever" by a client. In case the client visits Orbeon Forms pages often, the resources will be available from cache, therefore reducing page loading times and server load as well.

When Orbeon Forms is upgraded on the server, the version number changes as well. An XForms page will refer to resources with the new version number, so the cached resource is not used by the browser and a new resource is loaded from the server, before being cached. This "magic" is enabled simply with the inclusion of the Orbeon Forms version number in the URL.

Only CSS and JavaScript resources used by the XForms engine are loaded through the XForms Server component. Other resources like images referred by XForms stylesheets are served by the Page Flow Controller, through URLs like this one:

```xml
http://localhost:8080/orbeon/ops/images/xforms/error.png
```

With resource versioning enabled, the URL becomes:

```xml
http://localhost:8080/orbeon/58a8724010cd6fbe3ae7298de0a5c6e9dafc990b/ops/images/xforms/error.png
```

When resource versioning is enabled, the Page Flow Controller by default serves all the resources defined in `<files>` elements by first checking the `oxf.resources.versioned` property. If versioning is enabled, the PFC removes the version number from the URL before searching for the resource on disk. It is possible to turn this behavior on and off selectively with the `versioned` attribute. Here is how to turn off versioning for PDF files in `page-flow.xml`:

```xml
<config xmlns="http://www.orbeon.com/oxf/controller" xmlns:oxf="http://www.orbeon.com/oxf/processors">
    <!-- GIF images are loaded following oxf.resources.versioned -->
    <files path-info="*.gif"/>
    <!-- More file definitions here -->
    ...
    <!-- PDF files are not versioned -->
    <files path-info="*.pdf" versioned="false"/>
    <!-- More file definitions here -->
    ...
    <!-- More page definitions here -->
    ...
</config>
```

Conversely, resource URLs produced by an XForms page are automatically rewritten following the Page Flow's `<files>` definitions.

The versioning mechanism is made available to your own application resources as well. Any resource whose path doesn't start with `/ops/` or `/config/` is considered part of your application, not of Orbeon Forms. In that case, the Orbeon Forms version number is not used, but you specify instead an application version number in properties-local.xml:

```xml
<property as="xs:string" name="oxf.resources.version-number" value="1.6.3"/>
```

For deployed application, you should upgrade the application version number whenever you modify application resources so that clients retrieve the proper resources.

The following scenario shows the entire lifecycle for application resources:

* You create an image as `RESOURCES/apps/foo/bar.png`
* You refer to it as:

  ```xml
  <xhtml:body>
      <xhtml:img src="/apps/foo/bar.png" alt="My Image"/>
  </xhtml:body>
  ```
* With versioning enabled, the image path is rewritten automatically as follows:

  ```xml
  /1.6.3/apps/foo/bar.png
  ```

  Note that the application resource number is used because the resource is not part of Orbeon Forms.
* Your browser sees a URL like:

  ```xml
  http://localhost:8080/orbeon/1.6.3/apps/foo/bar.png
  ```
* When the browser loads the image, the PFC receives back:

  ```xml
  /1.6.3/apps/foo/bar.png
  ```
* The PFC knows that PNG files are versioned, so removes the version number and sends this resource to the browser:

  ```xml
  RESOURCES/apps/foo/bar.png
  ```

From client-side JavaScript, you can access the application version number as follow:

```xml
var version = ORBEON.util.Utils.getProperty(APPLICATION_RESOURCES_VERSION_PROPERTY);
```

Versioned resources served by the PFC (that is all the resources except the XForms engine's CSS and JavaScript resources) also get an aggressive expiration date.

In case you use Apache, you can in addition configure a rewriting rule with [mod\_rewrite](https://httpd.apache.org/docs/2.2/mod/mod_rewrite.html) to allow Apache to directly load resources containing a version number, as shown below.

*NOTE: We recommend restarting Orbeon Forms after changing the `oxf.resources.versioned` property, as data in Orbeon Forms caches may not be made aware of the change until the next restart.*

## JavaScript at the bottom of the page

### With Orbeon Forms 2019.1

With Orbeon Forms 2019.1, this feature is removed from Orbeon Forms and the `oxf.xforms.resources.javascript-at-bottom` property doesn't have any effect.

### With Orbeon Forms 2018.1 and 2018.2

With Orbeon Forms 2018.1 and 2018.2, this feature is deprecated and scripts are by default placed within the `<head>` section with the `defer` attribute.

### With Orbeon Forms 2017.2 and older

With Orbeon Forms 2017.2 and older, this feature is enabled by default.

The following property, if enabled, places external and inline JavaScript at the bottom of the page for performanc reasons:

```xml
<property
    as="xs:boolean"
    name="oxf.xforms.resources.javascript-at-bottom"
    value="true"/>
```

See Yahoo’s [Best Practices for Speeding Up Your Website](https://developer.yahoo.com/performance/rules.html#js_bottom)


# Limiter filter

## What this is about

\[SINCE Orbeon Forms 4.8]

The *limiter filter* is a servlet filter designed to limit the number of concurrent form processing requests, in order to reduce the likelihood of the server running out of resources.

Loading Form Runner pages and updating form state can be CPU and memory intensive, and a high level of concurrency for these operations is not desirable (even as we are working to improve the level of concurrency).

In the worst case, a large number of concurrent requests will compete for CPU and memory, causing slowdowns and possibly causing the server to run out of memory. In other cases, throughput can still be non-optimal.

It is therefore more efficient, past a certain level of concurrency, to *serialize* requests. Default Servlet containers settings are usually not adapted (Tomcat for example sets the default at 200 threads). They usually allow you to reduce the number of concurrent threads (Tomcat for example has settings on the `<Connector>` element), but these settings have drawbacks:

* Tomcat has a minimum of 10 concurrent threads.
* Other containers have settings which are very hard to figure out.
* Only *some* heavy requests must be serialized.

Therefore Orbeon Forms ships with a filter which implements the limiting mechanism internally.

## Configuration

The filter is enabled by default in Orbeon Forms 4.8. It is configured in `WEB-INF/web.xml`.

Here is a typical configuration:

```xml
<filter>
    <filter-name>orbeon-limiter-filter</filter-name>
    <filter-class>org.orbeon.oxf.servlet.LimiterFilter</filter-class>
    <!-- Include Form Runner pages and XForms Ajax requests -->
    <init-param>
        <param-name>include</param-name>
        <param-value>(/fr/.*)|(/xforms-server)</param-value>
    </init-param>
    <!-- Exclude resources not produced by services -->
    <init-param>
        <param-name>exclude</param-name>
        <param-value>(?!/([^/]+)/service/).+\.(gif|css|pdf|json|js|coffee|map|png|jpg|xsd|htc|ico|swf|html|htm|txt)</param-value>
    </init-param>
    <!-- Minimum, requested, and maximum number of concurrent threads allowed -->
    <!-- The `x` prefix specifies a multiple of the number of CPU cores reported by the JVM -->
    <init-param>
        <param-name>min-threads</param-name>
        <param-value>1</param-value>
    </init-param>
    <init-param>
        <param-name>num-threads</param-name>
        <param-value>x1</param-value>
    </init-param>
    <init-param>
        <param-name>max-threads</param-name>
        <param-value>x1</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>orbeon-limiter-filter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>
```

The filter applies to all incoming requests. However, internally, the filter only limits requests matching paths which:

* match the `include` parameter regular expression
* and do *not* match the `exclude` parameter regular expression

The default settings are meant to apply to:

* requests for forms
* XForms Ajax requests

but *not* to:

* assets such as JavaScript, CSS and images files
* file uploads

The number of threads which can run concurrently is based on the number of CPUs advertised by the JVM. This typically includes hyperthreading: for example, a laptop with 4 cores advertises 8 "CPUs".

In the configuration, `x1` means the advertised number of CPUs, `x2` means twice that, `.5x` means half that, etc. A fixed integer number can also be set.

The effective maximum number of concurrent threads allowed by the filter is:

* `min-threads` if `num-threads` is smaller than `min-threads`
* `max-threads` if `num-threads` is bigger than `max-threads`
* `num-threads` in all other cases

## Disabling the filter

Remove or comment-out the relevant `<filter-mapping>` in `WEB-INF/web.xml`.

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1) If you are using Orbeon Forms 2023.1 or newer, the configuration above will look slightly different. Parameters are stored in `<context-param>` instead of `<init-param>`, and the parameter names are prefixed with `oxf.orbeon-limiter-filter`. To disable the filter, add the following parameter:

```xml
<context-param>
    <param-name>oxf.orbeon-limiter-filter.enabled</param-name>
    <param-value>false</param-value>
</context-param>
```

## See also

* [Original issue](https://github.com/orbeon/orbeon-forms/issues/1971)


# Run modes

## Rationale

It is often necessary to have slightly different settings between development and production. Run modes provide a way to create two separate sets of settings targeting the two environments.

For a quick introduction, see also this [blog post](https://blog.orbeon.com/2012/05/run-modes.html).

## Configuration

The run mode is configured in the web application's web.xml file:

```markup
<context-param>
    <param-name>oxf.run-mode</param-name>
    <param-value>prod</param-value>
</context-param>
```

There are two possible modes:

* `prod`: production
* `dev`: development

By default, when you download Orbeon Forms, the mode is set to `prod`, the safest mode.

*NOTE: If you build Orbeon Forms from source and run it from the exploded `orbeon-war`, the default is set to `dev`.*

Orbeon Forms logs the mode used when starting the web app, for example:

```
Using run mode: prod
Using properties file: oxf:/config/properties-prod.xml
```

## Impact of the modes

Selecting a particular mode selects different properties files. By default, the following properties are different in prod vs. dev mode:

* `oxf.http.exceptions`
  * `prod`: `false`
  * `dev`: `true`
* `location-mode` for `oxf:builtin-saxon` and `oxf:unsafe-builtin-saxon`
  * `prod`: `none`
  * `dev`: `smart`
* `oxf.xforms.minimal-resources`
  * `prod`: `true`
  * `dev`: `false`
* `oxf.xforms.combine-resources`
  * `prod`: `true`
  * `dev`: `false`
* `oxf.xforms.resources.encode-version`
  * `prod`: `true`
  * `dev`: `false`
* `oxf.xforms.show-recoverable-errors`
  * `prod`: `0`
  * `dev`: `10`
* `oxf.show-version` \[SINCE Orbeon Forms 4.6.1]
  * `prod`: `false`
  * `dev`: `true`
* `oxf.fr.version.*.*` \[UNTIL Orbeon Forms 4.6]
  * `prod`: `false`
  * `dev`: `true`

In addition, depending on the mode, the following local file is used:

* `prod`: `properties-local-prod.xml`
* `dev`: `properties-local-dev.xml`

## See also

* [Configuration Properties](/configuration/properties)
* Blog post: [Run modes](https://blog.orbeon.com/2012/05/run-modes.html)


# Security

## Rationale

Orbeon Forms is used by health care, financial companies, government entities, and other organizations for which security is paramount. So we take security very seriously and believe Orbeon Forms provides a solid foundation in terms of security. What follows goes through common attacks and what Orbeon Forms is doing to counter those attacks.

## Type of security flaws

### Buffer overflows

Until recently, the most publicly reported security flaws were [buffer overflows](https://en.wikipedia.org/wiki/Buffer_overflow). The server-side code in Orbeon Forms is entirely written in Java and Scala, languages which performs bounds checking and other measures to shield programs from similar issues.

### Cross site scripting

Cross-site scripting ([XSS](https://en.wikipedia.org/wiki/Cross-site_scripting)) attacks come from the application taking some users' input, through form elements, request parameters, or otherwise, and displaying it on the page without proper escaping, thus allowing a malicious user to inject code into the page. To prevent this, Orbeon Forms:

* always encodes content provided by the user
* the content is stored in a safe container: XML
* uses standard XML parsers and serializers (which do proper escaping) to read/write the data
* when users can enter rich content (HTML), Orbeon Forms automatically performs [HTML cleanup](/xforms/controls/textarea#html-cleanup) on the data provided by users even before it reaches your application, so only HTML known to be safe is kept
* control values sent to the server are never inserted literally into queries

### HTML sanitization

[\[SINCE Orbeon Forms 2024.1.1\]](/release-notes/orbeon-forms-2024.1.1)

Form Builder automatically sanitizes HTML content entered by form authors, including HTML labels, hints, and help messages. This helps security by preventing a malicious or inattentive form author to inject script elements into a form, for example.

In addition, all Orbeon Forms HTML output goes through HTML sanitization as well. This also covers cases where HTML might be produced dynamically, for example using formulas.

### Authentication and cookie security

Orbeon Forms doesn't handle aspects of the infrastructure that can be handled in a better, more versatile, and more secure way by your application server. For instance, Orbeon Forms doesn't do user authentication itself, but relies on your application server. In a similar manner, it doesn't keep track of users' sessions, but leaves that to the application server, which, say, you could be setup to tie cookies to IP addresses to prevent cookie stealing.

### Modification of the internal state

#### Rationale

Your XForms manipulates on a number of XML documents called *instances* in XForms. You capture the data users enter in instances, but also use instances to store the internal state of your XForms pages. Some XForms implementations expose those instances to the client (the browser, a plug-in running in the browser, or JavaScript running in the browser). When this happens, malicious users can access and modify your application internal state, potentially leading to security flaws.

#### Direct access to XForms instance data

XForms state is kept on the server, so it is just not exposed to users. Only the values that are displayed to users are sent the browsers, and only values the user is authorized to change will be taken into account when received from the browser.

*NOTE: There is a deprecated option to keep state on the client. In this case, it is encrypted with a password you set. The password doesn't leave the server, and even if the encrypted value is visible to the client, it cannot be decrypted by users and can only be decrypted on the server with a valid key.*

#### Indirect access to XForms instance data via the Fetch API

The Orbeon Forms XForms engine typically interacts with the client using Fetch API requests (also known as XHR or Ajax requests) sent from the client browser. These requests are protected as follows:

* Fetch requests are only allowed to modify controls, not XML data directly.
* Fetch requests are only allowed to modify visible, read-write controls.
* Each page has a unique UUID generated each time the page is produced. The page has a finite lifetime, typically expiring with the user's session. Once the session has expired, any attempt to use that UUID fails.
* Each Fetch request has a unique sequence number. The server rejects incorrect sequence numbers. This prevents simply replaying incoming requests.
* Only requests via `POST` can have a side-effect on Orbeon Forms internal state. This excludes attacks via simply loading a URL via an image or a link.

### Cross site request forgery

Cross-site request forgery is often abbreviated CSRF or XSRF. We'll use CSRF here. There are two ways to think about CSRF:

1. The first is the risk of using Orbeon to launch CSRF attacks on another site. This would require users to be able to inject content into an Orbeon Forms page. Orbeon takes steps to prevent this, as covered in the documentation above.
2. The second is the risk of another site tricking Orbeon Forms into performing an action the user didn't want. In this scenario, the malicious site would make the browser do a form POST to the Orbeon Forms server. Two things prevent this:
   * When Orbeon Forms creates a page, it generates a random unique UUID for that page. Any request from that page needs this UUID. An attacker would need to know that UUID to pull off a CSRF attack.
   * For extra protection, you can configure your application server to create cookies with `SameSite=Strict` or `SameSite=Lax` ([MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#samesitesamesite-value)). With Tomcat, you can do this by adding `<CookieProcessor sameSiteCookies="strict"/>` inside the `<Context>` you use for Orbeon Forms.

### Communication with services

XForms pages communicate with the "outside world", say to load data initially shown in your page or to save data entered by users, by calling *services*. Services are usually HTTP services, such as REST or web services. These services typically implement your application backend logic: they provide data to your form and receive data from your form. With some XForms implementations, the calls to those services are made from the browser. This can potentially pose a significant security risk: it means you can't keep those services behind your firewall, and that the user can doctor the data sent to the services.

With Orbeon Forms, call to services are made from the Orbeon Forms server. You can keep the services running behind your firewall, and users won't be able to doctor the data sent to the services, or even see what that data is.

### Constraints on selection controls

Forms often constraint the values that can be entered by users with drop-downs, lists, radio buttons, or checkboxes. Say users need to rate a service and you provide a radio buttons they can select to choose a grade from 1 to 5. The values for the radio buttons will be 1, 2, 3, 4, and 5. In most web applications, users can easily doctor the value they send back to the server, and send, say 100. If you don't perform in your server-side code an additional check on the value received from the browser, you will take the 100 at face value, and users will be able to game an average of all the ratings you later compute based on that value.

With Orbeon Forms, the values you choose to use to represent the different items in drop-downs, lists, radio buttons, and checkboxes are encoded (either encrypted or represented as an opaque number) before they are sent to the browser. So not only they can't be doctored by users, but they won't even be sent to the browser, and you don't incur the risk of malicious users injecting an out-of-range value.

When the option of not encoding such values is chosen, the server still checks that the incoming value is allowed and rejects it if that is not the case.

## See also

* [Content-Security-Policy header](/configuration/advanced/security/content-security-policy)


# Content-Security-Policy header

## Availability

\[SINCE Orbeon Forms 2018.1]

## What is the Content-Security-Policy header?

The [`Content-Security-Policy` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), also known as CSP header, is a relatively recent HTTP header which "helps to detect and mitigate certain types of attacks, including [Cross Site Scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) and data injection attacks".

## Disabling inline scripts and CSS

### Introduction

Some organizations set the `Content-Security-Policy` header to strict values which disallow inline JavaScript and CSS within HTML pages, for example with `default-src 'self'`.

Up until Orbeon Forms 2017.2, Orbeon Forms included some inline scripts and CSS in the HTML served to the browser. Disabling inline scripts and CSS with `Content-Security-Policy` with those Orbeon Forms versions will prevent Orbeon Forms from working correctly.

With Orbeon Forms 2018.1 and newer, Orbeon Forms no longer produces inline scripts and CSS by default, which allows for these strict values of the `Content-Security-Policy` header. While Orbeon Forms already [takes measures](/configuration/advanced/security) against XSS and data injection, disabling the use of inline scripts can make Orbeon Forms even safer by default.

### Configuration property

The following XForms property allows you to re-enable inline scripts and CSS. The default is `false`:

```markup
<property 
    as="xs:boolean" 
    name="oxf.xforms.inline-resources"                            
    value="true"/>
```

We recommend leaving the value to the default of `false`.

## Generating the Content-Security-Policy header

In many cases, the `Content-Security-Policy` header is generated more globally by a reverse proxy or server.

But Orbeon Forms is able to produce that header as well, whether just for testing or for deployment. To enable this, simply uncomment the following entry in the Orbeon Forms WAR file's `web.xml`:

```markup
<init-param>
    <param-name>content-security-policy</param-name>
    <param-value>default-src 'self'; img-src 'self' data:</param-value>
</init-param>
```

You can set the `param-value` to any legal value supported by web browsers. In this example, `default-src 'self'` "Refers to the origin from which the protected document is being served, including the same URL scheme and port number. You must include the single quotes." ([Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src))

*NOTE: The `content-security-policy` name must remain in lowercase. It is a configuration parameter name, not the actual header name.*

## See also

* [Security](/configuration/advanced/security)
* Blog post: [Improving security with the Content-Security-Policy header](https://blog.orbeon.com/2018/08/improving-security-with-content.html)


# SAP Hybris Module

The Hybris yForms Module is required to integrate Orbeon Forms PE with SAP Hybris Commerce Suite. You can download it by following the relevant link below, depending on the version of SAP Hybris you are using:

* For SAP Hybris Commerce Suite 6.3 and earlier, download [Orbeon-Extension-6.3.0.zip](https://s3.amazonaws.com/orbeon-downloads/Orbeon-Extension-6.3.0.zip), built on Orbeon Forms PE 4.8.1.
* For SAP Hybris Commerce Suite 6.4, download [Orbeon-Extension-6.4.0.zip](https://s3.amazonaws.com/orbeon-downloads/Orbeon-Extension-6.4.0.zip), built on Orbeon Forms PE 2016.3.


# XForms logging

## Enabling XForms logging

Sometimes, an error message or stack trace in the Orbeon Forms log file provides enough information to a developer to figure out what went wrong, but not always. In such cases, you can turn to the XForms engine logging facility. To enable it, make sure you uncomment the following logging `<Logger>` (Log4j 2.x) or `<Category>` (Log4j 1.x):

Log4j 2.x (`WEB-INF/resources/config/log4j2.xml`):

```xml
<Logger name="org.orbeon.oxf.xforms.processor.XFormsServer" level="debug"/>
```

Log4j 1.x (`WEB-INF/resources/config/log4j.xml`):

```xml
<category name="org.orbeon.oxf.xforms.processor.XFormsServer">
    <priority value="debug"/>
</category>
```

*Note: You must restart your Servlet container for those changes to be taken into account.*

## Fine-grained configuration

Two properties in `WEB-INF/resources/config/properties-local.xml` control exactly what is logged by the XForms engine:

```xml
    <property as="xs:NMTOKENS" name="oxf.xforms.logging.debug">
        document
        model
        submission
        control
        event
        action
        analysis
        server
        html
        process
    </property>

    <property as="xs:NMTOKENS" name="oxf.xforms.logging.error">
        submission-error-body
    </property>
```

`oxf.xforms.logging.debug` controls what is logged at debug level:

* Related to a particular XForms document:
  * `model`
    * activity related to `xf:model`, including instance loads, validation, and binds
  * `submission`
    * timing activity related to `xf:submission`
  * `submission-details`
    * detail activity related to `xf:submission`
    * requires `submission` to be present as well
  * `control`
    * activity related to controls
  * `event`
    * activity related to events dispatching and propagation
  * `action`
    * activity related to XForms actions
  * `document`
    * other activity related to an XForms document
    * this includes the output of the `<xf:message>` action with level `xxf:log-debug`
  * `process`
    * activity related to [processes](/form-runner/advanced/buttons-and-processes)
* Not related to a particular XForms document:
  * `analysis`
    * activity related to the static analysis of an XForms document
  * `server`
    * activity related to handling Ajax requests
  * `html`
    * activity related to converting XForms to HTML
  * `resources`
    * activity related to handling XForms CSS and JavaScript resources
  * `state`
    * activity related to state handling
  * `resolver`
    * activity related to the URI resolver
  * `utils`
    * miscellaneous activity
  * `cache`
    * \[SINCE Orbeon Forms 4.6]
    * activity of the static state cache during XForms initialization
* Data:
  * `html-static-state`
    * requires `html`
    * outputs the static state input
  * `analysis-xbl-tree`
    * requires `analysis`
    * outputs the detail of the XBL shadow trees computed
  * `submission-body`
    * requires `submission` AND `submission-details`
    * outputs the detail of submission request/response bodies
  * `model-serialized-instance`
    * requires `model`
    * outputs the full instances serialized into the dynamic state after an Ajax request completes
  * `server-body`
    * requires `server`
    * outputs the full Ajax request and response bodies

`oxf.xforms.logging.error` controls what is logged at error level:

* `submission-error-body`
  * Whether to attempt to output a submission response body when a submission error occurs
  * This is enabled by default, but you can turn it off, e.g. for data sensitivity reasons
  * Binary bodies are not logged, but the logger mentions that the type is a binary type
  * When the response body is NOT logged and is used for `replace="instance|all"`, streaming is taking place. However when the response body IS logged, streaming does not take place:
    * The body is read in memory
    * The body is logged
    * Then the rest of the submission proceeds
* `server-body`
  * output the Ajax request in case of error occurring while processing the request

## Development configuration

During XForms development, you might want to enable a more aggressive debug configuration.

1. Change your Log4j2 or Log4j configuration. In your `WEB-INF/resources/config`, check if you have a file named `log4j2.xml` or `log4j.xml`:
   * If you have a file named `log4j2.xml` (with `2` in the file name), inside the element `<Loggers>`, add:

     ```xml
     <Logger name="org.orbeon.oxf.xforms.processor.XFormsServer"          level="debug"/>
     <Logger name="org.orbeon.oxf.fr.FormRunnerPersistence"               level="debug"/>
     <Logger name="org.orbeon.oxf.processor.DebugProcessor"               level="debug"/>
     <Logger name="org.orbeon.oxf.processor.pdf.XHTMLToPDFProcessor"      level="debug"/>
     <Logger name="org.orbeon.relational"                                 level="debug"/>
     <Logger name="org.orbeon.auth"                                       level="debug"/>
     <Logger name="org.orbeon.lifecycle"                                  level="debug"/>
     <Logger name="org.orbeon.xforms.submission.two-pass"                 level="debug"/>
     <Logger name="org.orbeon.properties"                                 level="warn"/>
     ```
   * If you have a file named `log4j.xml` (without `2` in the file name), uncomment the following:

     ```xml
     <category name="org.orbeon.oxf.xforms.processor.XFormsServer">
         <priority value="debug"/>
     </category>
     ```
2. Configure the `oxf.xforms.logging.debug` property in your `config/properties-local.xml`. You can choose precisely what the XForms engine logs. The following is the most comprehensive configuration. It will log almost everything. In most cases, this is a good configuration during development, and while troubleshooting issues in staging:

   ```xml
   <property as="xs:NMTOKENS" name="oxf.xforms.logging.debug">
       document
       model
       submission
       control
       event
       action
       analysis
       server
       server-body
       html
       process
       submission-details
       submission-body
       state
   </property>
   ```

## Production configuration

### No debug output

In production, you probably don't want any debug information coming out to your logs. So set this in `log4j.xml`:

```xml
<category name="org.orbeon.oxf.xforms.processor.XFormsServer">
    <priority value="info"/>
</category>
```

Alternatively, remove or comment-out the lines above. When this is done, the `oxf.xforms.logging.debug` property is no longer used, so it does not matter what it contains. However, the `oxf.xforms.logging.error` is still relevant. Configure it appropriately, depending on whether you want to see submission responses bodies logged or not.

### Just submission timings

If you only want to see submission timings, in your `log4j.xml` use:

```xml
<category name="org.orbeon.oxf.xforms.processor.XFormsServer">
    <priority value="debug"/>
</category>
```

And in your `properties-local.xml`:

```xml
<property as="xs:NMTOKENS" name="oxf.xforms.logging.debug">
    submission
</property>
```

## Example output

The following shows a sample XForms logging session:

```
XForms server - start handling external events
  XForms server - start handling external event {target id: "age-input-control", event name: "xxforms-value-change-with-focus-change"}
    setvalue - setting instance value {value: "36", changed: "true", instance: "instance"}
    event - start dispatching {name: "xxforms-value-changed", id: "instance"}
    event - end dispatching {time (ms): "0"}
    event - start dispatching {name: "xforms-recalculate", id: "main-model"}
        model - start performing recalculate {model id: "main-model"}
          setvalue - setting instance value {value: "A", changed: "false", instance: "instance"}
          setvalue - setting instance value {value: "A", changed: "false", instance: "countries-instance"}
          setvalue - setting instance value {value: "", changed: "false", instance: "country-details-instance"}
        model - end performing recalculate {time (ms): "9"}
    event - end dispatching {time (ms): "9"}
    event - start dispatching {name: "xforms-revalidate", id: "main-model"}
        model - start performing revalidate {model id: "main-model"}
          event - start dispatching {name: "xxforms-invalid", id: "instance"}
          event - end dispatching {time (ms): "0"}
          event - start dispatching {name: "xxforms-valid", id: "resources-instance"}
          event - end dispatching {time (ms): "0"}
          event - start dispatching {name: "xxforms-valid", id: "flavors-instance"}
          event - end dispatching {time (ms): "0"}
          event - start dispatching {name: "xxforms-valid", id: "carriers-instance"}
          event - end dispatching {time (ms): "0"}
          event - start dispatching {name: "xxforms-valid", id: "countries-names-instance"}
          event - end dispatching {time (ms): "0"}
          event - start dispatching {name: "xxforms-valid", id: "countries-instance"}
          event - end dispatching {time (ms): "0"}
          event - start dispatching {name: "xxforms-valid", id: "country-details-instance"}
          event - end dispatching {time (ms): "0"}
        model - end performing revalidate {time (ms): "4"}
    event - end dispatching {time (ms): "4"}
    event - start dispatching {name: "xforms-refresh", id: "main-model"}
        model - start performing refresh {model id: "main-model"}
          controls - start cloning
          controls - end cloning {time (ms): "0"}
          controls - start updating bindings
          controls - end updating bindings {time (ms): "11", controls updated: "91", repeat iterations: "0"}
          event - start dispatching {name: "xforms-value-changed", id: "age-input-control"}
          event - end dispatching {time (ms): "0"}
          event - start dispatching {name: "xforms-enabled", id: "age-input-control"}
          event - end dispatching {time (ms): "0"}
        model - end performing refresh {time (ms): "17"}
    event - end dispatching {time (ms): "17"}
  XForms server - end handling external event {time (ms): "33"}
XForms server - end handling external events {time (ms): "33"}
```

## See also

* [Logging](/installation/logging)
* [Relational database logging](/configuration/troubleshooting/database-logging)


# Troubleshooting


# Troubleshooting with the orbeon.log

When you get an unexpected behavior, like an error message for Form Builder or when running a form, you can often find more information about what went wrong in an Orbeon Forms log file generally referred to as `orbeon.log`. To check this log:

1. Find where your `orbeon.log` is located. Out-of-the-box, it is written in `../logs/orbeon.log`, relative to the current directory at the time you're starting your servlet container or application server. For instance, if you're starting Tomcat from it `bin` directory, the log file will be in Tomcat's `logs` directory. If you can't find the `orbeon.log`, or would like it to be created in a different location, in Orbeon Forms edit the `WEB-INF/resouces/config/log4j.xml`, locate the `SingleFileAppender`, and in `<param name="File" value="../logs/orbeon.log"/>` replace `../logs/orbeon.log` with the location where you'd like the `orbeon.log` to be stored. Using an absolute path, like `/opt/tomcat/logs/orbeon.log` is often a good idea.
2. Update the Orbeon Forms `WEB-INF/resouces/config/log4j2.xml` and `WEB-INF/resouces/config/properties-local.xml` per the [development configuration](/configuration/advanced/xforms-logging#development-configuration). This will increase the amount of information logged by Orbeon Forms, and it is generally a good idea to keep that configuration in place for as long as you are developing forms or troubleshooting a problem.
3. On Linux or macOS, run `tail -f orbeon.log` so you can watch information appended to the `orbeon.log`. On Windows, you can use a tool like [LogExpert](https://github.com/zarunbal/LogExpert) ([download](https://github.com/zarunbal/LogExpert/releases)). Reproduce the problem, at the same time watching your `orbeon.log`, and see if any error is being reported. If so analyze the error message, and see if this helps you find the source of the problem.
4. If you can't seem to find the source of the problem based on what you are seeing in your `orbeon.log`, when reporting a problem, it is a good idea to submit your `orbeon.log` with your report. To minimize the size of the file, stop your servlet container or application server (e.g. Tomcat), delete your `orbeon.log`, restart your servlet container or application server, reproduce the problem, and send the `orbeon.log` you have right after reproducing the issue.


# Memory and threads

## Memory

If you suspect that Orbeon Forms is running out of memory, add the following to your JVM options:

```
-verbosegc -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintGCDetails
```

This will output GC information to the Tomcat logs. The next time Orbeon Forms locks or gets slow, check the GC information in the servlet container logs. This might indicate whether Orbeon Forms blocks because of an actual memory issue or not.

## Deadlocks

If you suspect that Orbeon Forms is encountering a deadlock, obtain a JVM thread dump. The easiest is to use the `kill` command on Linux/Unix. See for example [How do I generate a Java thread dump on Linux/Unix?](https://access.redhat.com/solutions/18178).


# Relational database logging

## Introduction

In case of issues when using Orbeon Forms with a relational database, you might want to see what's happening between Orbeon Forms and said database. We have found that [P6Spy](https://github.com/p6spy/p6spy) is a useful tool for troubleshooting this scenario.

## Configuration

Below, you'll find a summary of the steps you can follow to install and configure P6Spy. For more options, or if you're using an application server other than Tomcat, you'll most likely want to refer to the P6Spy documentation, and specifically their [installation](http://p6spy.readthedocs.io/en/latest/install.html) and [configuration](http://p6spy.readthedocs.io/en/latest/configandusage.html) instructions.

1. [Download](https://search.maven.org/search?q=g:p6spy) the P6Spy jar file ([files](https://central.sonatype.com/artifact/p6spy/p6spy/versions)).
2. Move the P6Spy jar file to Tomcat's `lib` directory, or the equivalent directory on your application server. This should be the same directory where you installed your database driver jar file.
3. In the same directory, create a `spy.properties` as follows. On the first line replace `/var/log/tomcat` by the directory where your log files are stored. If you're using a database other than MySQL, on the second line replace `com.mysql.cj.jdbc.Driver` by the corresponding JDBC driver class name for your database.

   ```
   logfile=/var/log/tomcat/spy.log
   driverlist=com.mysql.cj.jdbc.Driver
   dateformat=MM-dd-yy HH:mm:ss:SS
   logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
   customLogMessageFormat=%(currentTime)|%(executionTime)|%(category)|connection%(connectionId)\n%(sql)
   ```
4. Where you define the datasource for Orbeon Forms, replace the driver class name by `com.p6spy.engine.spy.P6SpyDriver` and prefix the URL by `jdbc:p6spy:`. On Tomcat, this is done by editing Tomcat's `server.xml`, and in that file, inside the `Context` you have defined for Orbeon Forms, change the `Resource` setting the value of the `driverClassName` attribute to `com.p6spy.engine.spy.P6SpyDriver`, and prefixing the value of the `url` attribute by `jdbc:p6spy:`, as in:

   ```xml
   <Resource
       name="jdbc/mysql"
       driverClassName="com.p6spy.engine.spy.P6SpyDriver"

       auth="Container"
       type="javax.sql.DataSource"

       initialSize="3"
       maxActive="10"
       maxIdle="10"
       maxWait="30000"

       poolPreparedStatements="true"

       testOnBorrow="true"
       validationQuery="select 1"

       username="orbeon"
       password=""
       url="jdbc:p6spy:mysql://localhost:3306/orbeon?useUnicode=true&amp;characterEncoding=UTF8"/>
   ```
5. Restart Tomcat or the application server you're using. Check that no error messages show on the console, `catalina.out`, or equivalent with your setup. If none show and Orbeon Forms starts properly, run `tail -f spy.log` in a terminal window, or equivalent on your operating system, and check that as you access, say, the Form Builder Summary page, SQL statement are properly being logged.

## See also

* [Using Form Runner with a Relational Database](/form-runner/persistence/relational-db)
* [Troubleshooting with the orbeon.log](https://github.com/orbeon/orbeon-forms-doc/blob/master/configuration/troubleshooting/orbeon-log/README.md)
* [Logging](/installation/logging)
* [XForms logging](/configuration/advanced/xforms-logging)


# Misc

## Session not found when running both Tomcat and WebLogic

This issue can also manifest itself with a dialog titled *Session has expired. Unable to process incoming request.* showing up as you try to interact with a form. This comes from the fact that Tomcat and WebLogic handle the `JSESSIONID` cookie used to track sessions differently:

* Tomcat creates one `JSESSIONID` per web application, with the cookie path set to the context of the application. When an application invalidates the session, Tomcat sends a new `JSESSIONID` to the browser.
* WebLogic stores one cookie `JSESSIONID` with cookie path `/` for all the applications. This cookie doesn't change when a session is invalidated, and hence there is no one-to-one mapping between a `JSESSIONID` cookie and a session in WebLogic.\
  The error can happen when:
  1. You first access your application deployed on `/myapp` with Tomcat. Tomcat sets a `JSESSIONID` cookie for `/myapp`.
  2. You then access your application on the same server deployed on `/myapp` with WebLogic. Tomcat sets a `JSESSIONID` cookie for `/`.
  3. In subsequent requests, the browser sends the Tomcat `JSESSIONID` as it is more specific (for `/myapp` instead of just `/`), but WebLogic doesn't recognize it, hence the error you're getting.

The solution is simply to clear in your browser all the `JSESSIONID` cookies for the host you are trying to access.

## Data looks garbled on the Summary page with MySQL

If when accessing the Form Runner Summary page, the data you're seeing looks garbled, then run the following in on your MySQL database:

```sql
alter table orbeon_form_definition change xml xml mediumtext collate utf8_unicode_ci;
alter table orbeon_form_data       change xml xml mediumtext collate utf8_unicode_ci;
```

This instructs MySQL to use the `utf8_unicode_ci` collation instead of the default `utf8_bin`, and fixes this issue ([#1607](https://github.com/orbeon/orbeon-forms/issues/1607)). Note that no data was lost; data was always safe in the database, and this only impacted how it was shown on the Summary page. The [MySQL DDL for Orbeon Forms 4.5](https://github.com/orbeon/orbeon-forms/blob/master/form-runner/jvm/src/main/resources/apps/fr/persistence/relational/ddl/mysql-4_5.sql) has been updated after the 4.5 release, so if you're today installing Orbeon Forms 4.5, you can safely use that DDL and don't need to run the above commands.


# Form Builder

## Introduction

Orbeon Form Builder is a visual form designer which allows you to build and deploy forms in minutes right from your web browser.

A few key Form Builder features:

* **100% web-based.** Form Builder does not require installing any software on the user's computer: all that is needed is a recent web browser such as Firefox, Safari, Google Chrome or Microsoft Edge.
* **Easy grid-based layout.** Form Builder uses an easy-to-understand layout based on sections and grids, onto which you place your form elements. You concentrate on the data you want to capture, not the pesky details.
* **Rich data validation and controls.** Form Builder supports common datatypes for validation and user interface controls, as well as attachments and pictures. It also lets you import your own XML Schema and use the imported types.
* **Easy HTML and PDF output.** The forms you build with Form Builder automatically produce nice-looking HTML and PDF output.
* **Full internationalization.** With Form Builder, any form is easily designed in multiple languages, including labels, help, and error messages.
* **Expert mode with XForms.** For experts, Form Builder lets your form talk to the outside world with its built-in Web Services editor, and advanced form authors can provide their own XForms markup.
* **Accessible forms.** Forms produce use either Ajax or a more accessible mode without script.
* **Built-in runtime environment.** With a single click, your form is deployed into the Form Runner runtime environment and users can start filling-out data.
* **Services and actions.** Build simple services and actions right from the editor without writing code.

## Prerequisite knowledge

Form Builder can be used without much technical knowledge to build a vast range of forms. Some of the advanced features do require technical knowledge, but you don't necessarily have to use them, and if you do you can delegate the task as Form Builder lends itself to team work.

## Lifecycle of a form

Deployment use cases for Form Builder and Form Runner can vary depending on configuration, but here is a typical life for a form:

* **Design time** — The form author
  * Initiates the creation of a new form definition from the Form Builder Summary page
  * Edits the form definition from the Form Builder editor
  * Saves the form definition
  * Tests the form definition
  * Multiple edit/save/test cycles can take place
  * Publishes the form definition
* **Runtime** — The form user
  * Initiates the creation of new form data from the form's Summary page
  * Enters data into the form
  * Reviews, saves, submits, or downloads form data

Form definitions, as well as form data, can also be searched and deleted.

## Terminology

A few useful terms used in this document:

* **Form control.** A form control is a form user interface element such as a text line, text field, group of radio buttons, email or currency field, etc.
* **Form definition.** A form definition (often simply called a *form*) includes a set of form controls, a layout of these controls on the screen, a representation of the data to capture (e.g. an XML document format), and optionally events and actions defining behavior when the user interacts with the controls, as well as rules that can apply to the data.
* **Form data.** Form data (often also called a *document*) is data that is captured or edited by a form definition.
* **Form author.** The form author is the person creating a form definition (either using a visual tool, or by writing code).
* **Data entry.** Act of entering data into a form.
* **Form user.** As opposed to the form *author*, the form *user* is the person entering data into a form.
* **Form Builder.** The software, described in this document, used by the form author to create a form definition.
* **Form Runner.** The Orbeon Forms runtime environment, which takes care of presenting to an end user the form definitions created by the form author with Form Builder.
* **Design time.** The time during which the form is edited in Form Builder.
* **Runtime.** The time during which the form is executed by Form Runner, and where end users can enter data.

## Software requirements

On the client, Form Builder requires a modern web browser. For specifics, see the release notes of the Orbeon Forms release you are using.

The faster the browser and the computer, the better your authoring experience will be.

## See also

* [Form Runner](https://github.com/orbeon/orbeon-forms-doc/tree/05dfe635d04d26fa28f0c788c3cc816a72c4c8ae/form-runner/overview/README.md)


# Form settings

## Introduction

Some settings apply to the entire form definition. You access these settings with the "Form Settings" wrench icon on the top right of Form Builder. The General Settings also show initially when creating a new form definition.

## General Settings

General settings allow you to set the following form definition metadata:

* Application name
* Form name
* Form title in the current language
* Form description in the current language

![General Settings](/files/jWojdAmXhK9mQD6LeLrM)

\[SINCE Orbeon Forms 2020.1]

The description can optionally use rich text.

![Form description with rich text](/files/RZGg1G9cJ5MB74jsHx2b)

## Form Options

\[SINCE Orbeon Forms 2016.2]

Form options include options which apply to the entire form definition.

![Form Options](/files/JMHtJ6OUNM4PnqInjJqR)

* Singleton Form
  * Enable or disable singleton form behavior.
  * See [Singleton Form](/form-runner/advanced/singleton-form)
* Allow use as form template
  * [\[SINCE Orbeon Forms 2024.1\]](/release-notes/orbeon-forms-2024.1)
  * When this is checked, this form can be used as a template for new forms.
  * The default is unchecked.
  * For details, see [Form templates](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/form-templates.md).
* Simple Data Migration
  * \[SINCE Orbeon Forms 2018.2]
  * For details, see [Simple data migration](/form-runner/features/simple-data-migration).
* Grid Tab Order
  * [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)
  * Use property: use the [`oxf.xforms.xbl.fr.grid.tab-order` property](/configuration/properties/form-runner/form-runner-detail-page#grid-tab-order)
  * For details, see [Grid Tab Order](/form-builder/form-editor/grid-settings#grid-tab-order) in the Grid Settings dialog.
* Maximum Attachment Size per File
  * \[SINCE Orbeon Forms 2017.1]
  * Use property: use the [`oxf.fr.detail.attachment.max-size-per-file` property](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments#maximum-attachment-size)
  * Other: the maximum size allowed in bytes
* Maximum Aggregate Attachment Size
  * \[SINCE Orbeon Forms 2017.1]
  * Use property: use the [`oxf.fr.detail.attachment.max-size-aggregate-per-form` property](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments#maximum-aggregate-attachment-size-forms)
  * Other: the maximum size allowed in bytes
* Maximum Number of Files
  * [\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)
  * Use property: use the [`oxf.fr.detail.attachment.max-files-per-control` property](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments#maximum-number-of-files)
  * Other: the maximum number of files allowed per multiple file attachment control
* Allowed File Types
  * \[SINCE Orbeon Forms 2017.1]
  * Use property: use the [`oxf.fr.detail.attachment.mediatypes` property](/configuration/properties/form-runner/form-runner-detail-page/form-runner-attachments#allowed-file-types)
  * Other: a space-separated list of mediatypes or wildcard mediatypes
* Use Automatic Hints
  * [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)
  * Use property: use the `oxf.fr.detail.hint.automatic.*.*` property
  * Yes/No: show or don't show the automatic hints for all controls on this form
  * For details, see [Automatic hints](/form-builder/form-editor/control-settings#automatic-hints)

Here is how hints appear depending on the type of control they are associated with:

![Hints](/files/-LEkBtUVPhNrIEbjNXU9)

## Time Window

![Time Window](/files/6aQ7fsogR5ddqnCmrdL4)

See [Time Window](/form-builder/form-settings/time-window).

## Control Settings

\[SINCE Orbeon Forms 2018.2]

This tab shows settings for specific controls. Those settings apply to all control on the form except when they are overridden by individual settings in the "Control Settings" dialog.

For example, you can set a "Digits After Decimal" setting global to the form, and then override it on specific number controls as needed.

![Number Control Settings](/files/5yiEud7LWU0VsQ03xBFb)

![Date Control Settings](/files/8SfVoAZj0eXHKt9sYjSZ)

See also the [blog post](https://blog.orbeon.com/2019/03/form-level-and-control-level-settings.html).

## Appearance

### Settings

\[SINCE Orbeon Forms 2018.1] These options are now in a separate tab.

![Appearance](/files/n3x23SRXbcKvtpZW8Rj5)

* Browser page layout
  * \[SINCE Orbeon Forms 2019.2]
  * Use property: use the [`oxf.fr.detail.html-page-layout` property](/configuration/properties/form-runner/form-runner-detail-page#html-page-layout)
  * Fixed width: the form sections and grids take a fixed and predefined width of approximately 940px for large displays (the layout becomes responsive for smaller displays sizes).
  * Fluid width: the form sections and grids take the entire web browser's viewport size.
* Density
  * [\[SINCE Orbeon Forms 2024.1\]](/release-notes/orbeon-forms-2024.1)
  * Use property: use the [`oxf.fr.detail.density` property](/configuration/properties/form-runner/form-runner-detail-page#density)
  * Sets the spacing between elements in the interface, `Compact` being the most compact (default) and `Roomy` being the most spacious.
* Appearance of Control Labels
  * Use property: use the [`oxf.xforms.label.appearance` property](/xforms/controls/input#per-form-properties)
  * Inline: labels show inline above the control
* Use Placeholder for Text Fields and Text Areas
  * Labels show inline above the control for most fields.
  * For text, date, and time input fields, labels show as an HTML *placeholder* within the field when the field is empty.
  * For text areas, labels show as an HTML *placeholder* within the field when the field is empty. \[SINCE Orbeon Forms 2017.1]
* Appearance of Control Hints
  * Use property: use the `oxf.xforms.hint.appearance` property
  * Inline: hints show inline below the control
  * Tooltips: hints show as tooltips upon mouseover
* Use Placeholder for Text Fields and Text Areas
  * Hints show inline or as tooltips upon mouseover.
  * For text, date, and time input fields, hints show as an HTML *placeholder* within the field when the field is empty.
  * For text areas, hints show as an HTML *placeholder* within the field when the field is empty. \[SINCE Orbeon Forms 2017.1]

See also:

* Blog post: [New layout choices for PDF and browser views](https://blog.orbeon.com/2019/11/new-layout-choices-for-pdf-and-browser.html)

### Form Density

[\[SINCE Orbeon Forms 2024.1\]](/release-notes/orbeon-forms-2024.1)

Here is how the same form layout looks with the three different form density settings. First, the default "Compact" setting:

![Compact](/files/FPOpIofXKi1tj8RDSWhQ)

Second, the "Comfortable" setting. Notice how the controls are spaced out more:

![Comfortable](/files/V0RFNPkNr4EdJpvXfFe7)

Finally, the "Roomy" setting:

![Roomy](/files/4tR8mZlMBuT9NfEkC5yA)

See also:

* Blog post: [Form Density Setting](https://www.orbeon.com/2024/11/form-density)

## Wizard

For details, see [Wizard View](/form-runner/features/wizard-view).

* Wizard View:
  * Use property: use the [`oxf.fr.detail.view.appearance` property](/form-runner/features/wizard-view#using-a-property)
  * Always or Never: enable or disable the wizard view for this form definition, no matter how the property is configured.
* Wizard Navigation Validation Mode
  * Form Builder setting \[SINCE Orbeon Forms 2018.1]
  * Use property: use the [`oxf.xforms.xbl.fr.wizard.validate` property](/form-runner/features/wizard-view#lax-validated-mode)
  * Free, Lax or Strict: use the given validation mode for this form definition, no matter how the property is configured.
* Wizard Subsections Navigation
  * Form Builder setting \[SINCE Orbeon Forms 2018.1]
  * Use property: use the [`oxf.xforms.xbl.fr.wizard.subsections-nav` property](/form-runner/features/wizard-view#subsections-navigation)
  * Always or Never: enable or disable subsection navigation for this form definition, no matter how the property is configured.
* Wizard Subsections Table of Contents
  * Form Builder setting \[SINCE Orbeon Forms 2018.1]
  * Use property: use the [`oxf.xforms.xbl.fr.wizard.subsections-toc` property](/form-runner/features/wizard-view#visibility-in-the-table-of-contents)
  * "Show subsections for the active section only", "Show subsections for all sections", "Don't show subsections": use the given setting for this form definition, no matter how the property is configured.
* Wizard Separate Table of Contents
* Always Show Wizard Section Status

See also the [blog post](https://blog.orbeon.com/2019/11/new-layout-choices-for-pdf-and-browser.html).

## PDF

\[SINCE Orbeon Forms 2019.2]

![PDF](/files/I35iaOuleyhZIBAKiIGa)

* PDF page orientation
  * Use property: use the [`oxf.fr.detail.rendered-page-orientation` property](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf#automatic-pdf-page-size-and-orientation)
  * Portrait: use the portrait (vertical) orientation.
  * Landscape: use the landscape (horizontal) orientation.
* PDF page size
  * Use property: use the [`oxf.fr.detail.rendered-page-size` property](/configuration/properties/form-runner/form-runner-detail-page/form-runner-pdf#automatic-pdf-page-size-and-orientation)
  * Letter: US letter size.
  * A4: standard A4 size.
  * Legal: US legal size.

See also the [blog post](https://blog.orbeon.com/2019/11/new-layout-choices-for-pdf-and-browser.html).

## Formulas

![Formulas](/files/PKxmCZlWiXsmHvRXMUVQ)

* Calculations in Read-Only Modes (Review, PDF)
  * \[SINCE Orbeon Forms 2021.1]
  * Use property: use the [`oxf.fr.detail.readonly.disable-calculate` property](/configuration/properties/form-runner/form-runner-detail-page#calculations-in-readonly-modes)
    * the property defaults to `false`, which means that Calculated Value formulas run in readonly modes
  * Enable: Calculated Value formulas run in readonly modes.
  * Disable: Calculated Value formulas do not run in readonly modes.
  * Sometimes calculations should not run in readonly modes, such as the View page. This can now be configured at the form level.
* Read-Only
  * \[SINCE Orbeon Forms 2020.1]
  * Boolean expression specifying whether the entire form is read-only (not editable).
  * If this field is left blank, then the form is editable.
  * Otherwise, it is editable only if the result of the Boolean expression is `false()`.
* Automatic Calculations Dependencies
  * \[SINCE Orbeon Forms 2018.1]
  * For details, see [Automatic calculations dependencies](/form-runner/features/automatic-calculations-dependencies).

## Summary Page

\[SINCE Orbeon Forms 2026.1]

This tab allows you to configure the visibility of form metadata columns (Created, Last Modified, Workflow Stage, Created By, Last Modified By) on the [Summary page](/form-runner/form-runner-pages/summary-page). For each column, you can choose to use the global property, show, or hide the column, and optionally restrict visibility by role.

See [Summary Page](/form-builder/form-settings/summary-page).

## About this Form

![About this Form](/files/zwziG83cfgzvVAQmh1zz)

### Versions

\[SINCE Orbeon Forms 2018.2]

This area shows relevant Orbeon Forms versions:

* "Created with Version": version with which this form definition was created.
  * *NOTE: This is blank for forms created prior to Orbeon Forms 2018.1.*
* "Updated with Versions": versions with which this form definition was updated This is updated:
  * when saving the form definition
  * when upgrading the form definition from the Form Runner Home page.
* "Current Version": the current Orbeon Forms version.

### Form Statistics

Form statistics show counts of various form elements.

## See also

* [Wizard View](/form-runner/features/wizard-view)
* [Simple data migration](/form-runner/features/simple-data-migration)
* [Automatic calculations dependencies](/form-runner/features/automatic-calculations-dependencies)
* [Summary page configuration](/form-builder/form-settings/summary-page)
* Blog posts
  * [New layout choices for PDF and browser views](https://blog.orbeon.com/2019/11/new-layout-choices-for-pdf-and-browser.html)
  * [Improved constraints on attachments uploads](https://blog.orbeon.com/2017/04/improved-constraints-on-attachments.html)
  * [Form-level and control-level settings](https://blog.orbeon.com/2019/03/form-level-and-control-level-settings.html)


# Time window

\[SINCE Orbeon Forms 2023.1] \[Orbeon Forms PE only]

<figure><img src="/files/6aQ7fsogR5ddqnCmrdL4" alt="" width="1022"><figcaption><p>Time Window tab of the Form Settings dialog</p></figcaption></figure>

For some forms, you might only want to accept new entries after a given start date or before a given end date. You can define such constraints by opening your form in Form Builder, opening the Form Settings dialog, and switching to the Time Window tab, which is also shown in the above screenshot. This allows you to define an optional start date and end date. If users try to fill out a new form before or after the specified dates, they will be shown a default message, which you can also override in the same tab.

## Properties

If you prefer to define a time window for your forms using properties, you can do so using the property names shown below. The properties values, if provided, must be in the `xs:dateTime` format, as in `2024-01-01T00:00:00` for the beginning of January 1, 2024. By default, your form will be using the properties, which you can override through the UI discussed above.

```xml
<property as="xs:string"  name="oxf.fr.detail.available-from.dateTime.*.*" value=""/>
<property as="xs:string"  name="oxf.fr.detail.available-to.dateTime.*.*"   value=""/>
```

You can change the default messages by overriding the built-in resources with the following properties. If you want to change the default message for languages other than English, replace `en` in the property names as appropriate.

```xml
<property 
    as="xs:string"  
    name="oxf.fr.resource.*.*.en.detail.available-from.message"            
    value="Form not available yet. Patience is bitter, but its fruit is sweet."/>
<property 
    as="xs:string"
    name="oxf.fr.resource.*.*.en.detail.available-to.message"            
    value="Form not available anymore. On the bright side, punctuality is the virtue of the bored."/>
```

## Applies to new forms

The time window restriction you set applies only to new forms. For example, if a user has access to specific form data within the time window, they will retain access even after the time window has closed. To prevent users from modifying the data after the time window ends, use the read-only feature under the Formulas tab. Implement a formula similar to `current-date() >= xs:date('2024-01-01')`.


# Summary page

\[SINCE Orbeon Forms 2026.1]

<figure><img src="/files/3hngDgr5eaK4z9ru4yz9" alt="Summary Page tab of the Form Settings dialog"><figcaption><p>Summary Page tab of the Form Settings dialog</p></figcaption></figure>

## Introduction

The Summary Page tab of the Form Settings dialog allows you to configure the visibility of form metadata columns on the [Summary page](/form-runner/form-runner-pages/summary-page). This provides a per-form alternative to using global [properties](/configuration/properties/form-runner/form-runner-summary-page), and adds the ability to restrict column visibility by user role.

The following form metadata columns can be configured:

| Metadata column  | Related property                                                                                                                                    |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Created          | [`oxf.fr.summary.show-created`](/configuration/properties/form-runner/form-runner-summary-page#created-and-last-modified-columns)                   |
| Last Modified    | [`oxf.fr.summary.show-last-modified`](/configuration/properties/form-runner/form-runner-summary-page#created-and-last-modified-columns)             |
| Created By       | [`oxf.fr.summary.show-created-by`](/configuration/properties/form-runner/form-runner-summary-page#show-created-by-and-last-modified-by-users)       |
| Last Modified By | [`oxf.fr.summary.show-last-modified-by`](/configuration/properties/form-runner/form-runner-summary-page#show-created-by-and-last-modified-by-users) |
| Workflow Stage   | [`oxf.fr.summary.show-workflow-stage`](/configuration/properties/form-runner/form-runner-summary-page#show-the-workflow-stage)                      |

## Display options

For each metadata column, you can choose one of the following options:

* **Use property** (default): the column's visibility is determined by the corresponding global property.
* **Show**: the column is always shown on the Summary page (subject to an optional role restriction, see below).
* **Hide**: the column is never shown on the Summary page.

## Role restriction

When "Show" is selected for a metadata column, a "Restrict to role" checkbox appears. When checked, an input field allows you to enter a role name. You can add multiple roles by using the "+" button.

When two or more roles are specified, a dropdown allows you to choose between:

* **All**: all specified roles must be present for the column to be shown.
* **Any**: at least one of the specified roles must be present for the column to be shown.

This is the same mechanism as the [role restriction in the Control Settings dialog](/form-builder/form-editor/control-settings#basic-options).

## Interaction with properties

The form-level settings in this tab take precedence over the corresponding global properties. If a metadata column is set to "Use property" (the default), the global property is used as a fallback.

## See also

* [Summary page](/form-runner/form-runner-pages/summary-page)
* [Summary page configuration properties](/configuration/properties/form-runner/form-runner-summary-page)
* [Role restriction in Control Settings](/form-builder/form-editor/control-settings#basic-options)
* [Workflow stage](/form-runner/features/workflow-stage)


# Form editor

## Creating your first form definition

After pressing the "New" button on the [Summary page](/form-builder/summary-page), you reach the form editor (also known as form designer) proper. Form Builder requires that you provide two small pieces of information before starting:

* **Application Name.** Each form definition has an *application name* which identifies a group of forms that belong together. In practice, an application name might correspond to a project, or a department, or a company. For example, all forms built by Orbeon have the "orbeon" application name. *NOTE: Depending on your permissions, the application name might have been picked for you already, or you might have the choice of a restricted set of application names.*
* **Form Name.** Each form definition also has a *form name*, which identifies a unique form name within a particular application.

![Form Settings for a New Form Definition](/files/sjJQd6qb2W1MLGTTCmql)

Both application name and form name must respect a certain format:

* Use letters and optionally numbers and separators like "-" and "\_"
* Start with a letter
* Do not use spaces

*NOTE: One reason for these restrictions is that the names will eventually appear in your browser's URL bar when the form is deployed and it is better to have clean-looking URLs.*

For your first form, don't worry too much about those: just enter any name! You can always change these settings later!

You also may enter at this time a form title and description.

## General Form Builder layout

Form Builder's form editor is organized in 4 areas:

* **Top:** title area with status information and selection of the Form Builder user interface language.
* **Left:** the [toolbox](/form-builder/form-editor/toolbox), which might scroll vertically depending on your browser or monitor size.
* **Bottom:** status icons and buttons.
* **Center:** WYSIWYG [form area](/form-builder/form-editor/form-area) where you actually edit your form definition. This area might scroll vertically depending on your browser or monitor size or the size of the form you are editing.

![General Layout of Form Builder](/files/-LEkBtUK8s-7bd04Hq2f)

## The top bar

On the top right corner of the top bar, you can change the Form Builder user interface language by clicking on one of the languages available. Currently, English and French are available. More languages may be added in the future.

Changing the language immediately updates the Form Builder user interface: you do not even need to save your form prior to using this feature.

*NOTE: This feature controls the language of the Form Builder user interface only. For localization of the form definition you are currently editing, see documentation below.*

![Form Builder's Top Bar](/files/-LEkBtUMUx124SONFYs_)

## The buttons bar

See [Buttons bar](/form-builder/form-editor/buttons-bar).

## Keyboard shortcuts

See [Keyboard shortcuts](/form-builder/form-editor/keyboard-shortcuts).

### Other functions

In addition, the bottom bar displays status information, such as whether the current form definition was successfully saved.

## See also

* [Form area](/form-builder/form-editor/form-area)
* [Toolbox](/form-builder/form-editor/toolbox)
* [Buttons bar](/form-builder/form-editor/buttons-bar)


# Form area

## Introduction

The Form Builder form area is meant to look as much as possible like the published form.

Form Builder is built around as simple layout concept: *sections* and *grids*. This is a good alternative to:

* absolute positioning, which is rarely appropriate for web forms
* complex dynamic layouts, which often confuse form authors

Starting with Orbeon Forms 2017.2, Form Builder grids are more flexible then with earlier versions and follow a 12-column layout.

![Form Builder and the main form area](/files/-LEkC4vixlh5Ja_bjUho)

## Sections

### Introduction

Form Builder represents every form as a series of *sections*. A section is a logical grouping of form controls which has a *title* and an optional *help*. For example, your form may have an "Address" section, and a "Personal Details" section.

Sections are by default presented in order, on top of each other in the page. But when using the [wizard view](/form-runner/features/wizard-view), each top-level section, and optionally the next level of subsections, becomes a wizard page at runtime.

In addition:

* Every form has at least one section.
* There is no maximum number of sections within a form.
* Sections can have *subsections*, which are sections nested within other sections.

### Operations on sections

You can perform the following operations on sections.

* Set the section's localizable title by clicking on it. To select a different language, use the language selector at the top right of the form area.
* Delete the section by clicking on the trashcan icon. If the section contains controls, a confirmation dialog appears.
* Set an optional help message for the section by clicking on the help icon. The help is localizable.
* Set other [section settings](/form-builder/form-editor/section-settings).
* Open or collapse the section by clicking on the arrow to the left of the section title.
* Move the section up or down by clicking on the up/down arrows. These appear as needed if there is more than one section.
* Move the section left or right by clicking on the left/right arrows. These appear as needed if there is more than one section and if it is possible to move a section *into* another section ("right"), or out of its container section ("left"). This allows making sections *subsections* of other sections.

## Grids

Each section contains one or more *grids*. A grid is a logical grouping of form controls organized in rows and columns of cells. Unlike sections, a grid doesn't have a title or help.

A grid may be a regular grid or a [repeated grid](/form-builder/form-editor/repeated-grids), which repeats a group of one or more rows. A repeated grid may have from no iteration (or repetition) to any number of repetitions. A repeated grid also has settings, but regular grids do not have settings as of Orbeon Forms 2018.2.

In addition:

* A grid may have any number of rows (but at least one).
* Each grid cell may contain a single form control, or remain empty.
* A grid cell might span multiple rows.
* Unlike a section, a grid does not have a title or properties, except for *repeated grids* which do have properties.

When your mouse pointer hovers over a grid, the grid boundaries, cells and icons are highlighted.

### The 12-column layout

#### Introduction

\[SINCE Orbeon Forms 2017.2]

A grid always has 12 columns when shown on a wide display. Note that on narrower displays, grids still collapse to a vertical layout (see [Responsive design](/form-runner/features/responsive-design)).

See also [the blog post](https://blog.orbeon.com/2018/05/the-12-column-layout.html).

#### Operations

You can perform the following operations on grids:

* Add grid rows by clicking on the up and down arrows on the left of each row.
* Delete a row by clicking on the "minus" icon on the left of a row.
* Expand/shrink grid cells horizontally or vertically.
* Using icons to the left of the grid
  * Open the Grid Settings dialog (cogwheel icon)
    * *NOTE: Until Orbeon Forms 2018.2, this was only available for repeated grids.*
  * Delete the entire grid (minus icon)
  * Moving a grid relative to its sibling grids (arrow icons)
    * *NOTE: Until Orbeon Forms 2018.2, this was only available for repeated grids.*
  * Cut or copy the entire grid to the clipboard (scissors or copy icons)
    * *NOTE: Until Orbeon Forms 2018.2, this was only available for repeated grids.*

![Grid icons](/files/-Lq2RyYc3QyNB6Hh-7tf)

Expanding/shrinking cells horizontally makes grid cells span *columns*. This allows grid cells to have a width of 1, 2, and up to 12 columns.

![Grid cell widths at runtime](/files/-LEkC4wEEAqlZHqs5Wsq)

With Orbeon Forms 2017.2, you use the arrow icons within the grid to expand into empty adjacent cells.

![Expanding cell boundaries](/files/-LKcq6n4BbtzaQ_3ieq-)

With Orbeon Forms 2018.1 and newer, you drag the cell boundary to expand into empty adjacent cells. See also the [blog post](https://blog.orbeon.com/2018/10/resizing-cells-with-drag-drop-in-form.html).

![Dragging cell boundaries horizontally](/files/-LKcq6n7tP15sSlbQa49)

Expanding/shrinking cells vertically makes grid cells span *rows*. This allows making some cells taller when next to other cells, for example to allow for taller radio buttons lists.

![Dragging cell boundaries vertically](/files/-LKcq6n9byLLvbf9D10q)

#### Undoing operations

Destructive operations do not ask for confirmation but add the operation to the Undo stack, allowing you to undo the operation from the toolbox. See [Undo and redo](/form-builder/form-editor/undo-redo).

### Until Orbeon Forms 2017.1

A grid has between 1 and 4 columns, but it is possible to make Form Builder support more than 4 via a configuration property.

You can perform the following operations on grids:

* Add grid columns by clicking on the left and right arrows at the top of each column.
* Add grid rows by clicking on the up and down arrows on the left of each row.
* Delete a column by clicking on the trashcan icon at the top of a column.
* Delete a row by clicking on the trashcan icon on the left of a row.
* Delete the entire grid by clicking on the trashcan icon on the top left corner of the grid.
* Expand/shrink grid cells vertically only.

For delete operations, a warning dialog shows if controls will be deleted as a result.

![Column confirmation dialog](/files/-LEkC0X7izqx_znmcp7J)

## Grid cells and controls

Each grid cell can contain a single form control, or no control at all. Some controls, by default, take the entire width of the cell. This includes text fields and text areas. In such cases, to size the control, you can make the cell wider or narrower.

If a control is present, the following actions related to the control are possible:

* **Set control label:**
  * Click on the label.
  * A control's label appears on top of the control. It is intended to provide a descriptive label for the form control.
  * Examples: "First Name", "Street", "Phone Number".
* **Set control hint:**
  * Click on the hint.
  * A control's hint usually appears under the control. It is intended to provide a short indication to the form user of how to fill-out the form control.
  * Examples: "Your first name", "Date in mm/dd/yyyy format".
* **Set control default value:**
  * Simply enter text or select a value.
  * You can also set a calculated initial value from the ["Control Settings" dialog](/form-builder/form-editor/control-settings).
* **Edit the control's items:**
  * Click on the "Edit Items" icon or test to open the [Choices editor](/form-builder/form-editor/itemset-editor) (for selection controls only).

When your mouse pointer hovers over a grid cell containing a control, some icons allowing for further actions appear:

* **Move or Copy Control icon:**
  * \[SINCE Orbeon Forms 2017.2]
  * This handle lets you drag and drop the control to another grid cell. Press the *shift* key before you start the drag & drop motion to create a copy of the current control in the target cell, instead of moving it there.
* **Delete Control icon:**
  * This deletes the control, removing all the information associated with the control including validation properties.
* **Control Settings icon:**
  * This opens the ["Control Settings" dialog](/form-builder/form-editor/control-settings).
* **Expand/shrink icons:**
  * These expand and shrink the cell horizontally or vertically if possible.

## In-place editing

Form Builder allows you to edit certain text information in-place. This just means that text appears as it would in the published form, and is editable when you click on it.

To edit such text:

* Click on the text (or placeholder text).
* A text field appears.
* Enter the text.
* Use the "Enter" key or click outside the text field to commit the change.

To indicate that the text is editable, it highlights when the mouse hovers over it.

This mechanism is how you edit:

* section titles
* control labels
* control hints
* explanation texts

## See also

* Blog post: [The 12-column layout](https://blog.orbeon.com/2018/05/the-12-column-layout.html)
* Blog post: [Resizing cells with drag & drop in Form Builder](https://blog.orbeon.com/2018/10/resizing-cells-with-drag-drop-in-form.html)
* [Form editor](/form-builder/form-editor)
* [Toolbox](/form-builder/form-editor/toolbox)
* [Buttons bar](/form-builder/form-editor/buttons-bar)
* [Section settings](/form-builder/form-editor/section-settings)
* [Repeated grids](/form-builder/form-editor/repeated-grids)
* [Responsive design](/form-runner/features/responsive-design)


# Toolbox

## Introduction

The Form Builder toolbox provides the following:

* **Global icons:**
  * Form Settings \[SINCE Orbeon Forms 2017.2]
  * Cut/copy/paste
  * Undo/redo \[SINCE Orbeon Forms 2017.2]
  * "Reload toolbox" \[UNTIL Orbeon Forms 2017.1]
* **Form structure:**
  * New Section
  * New Grid
  * New Repeated Grid
* **Form controls:**
  * Form control (also known as form fields) you can add to your form.
* **Metadata:**
  * Allows you to modify the application name and form name.
* **Advanced:**
  * Includes advanced features like XML Schema, PDF and source code view.
* **Services and Actions:**
  * Editors for simple services and actions.

Depending on your monitor or browser size, you can use the scrollbar to the right of the toolbox to see more toolbox content.

## Configuration of the toolbox

The toolbox can be configured via:

* [Properties](/configuration/properties/form-builder#toolbox): for selecting and ordering the components that show in the toolbox.
* [Component metadata](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/form-builder/metadata.md): for the component names.

## Cut, copy and paste

See [Cut, copy and paste](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/form-builder/cut-copy-paste.md).

## Undo and redo

See [Undo and redo](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/form-builder/undo-redo.md).

## Reloading the toolbox

### Function

The "Reload Toolbox" icon or button reloads all the components in the toolbox. After reloading:

* The list of controls in the toolbox updates to reflect changes to the [`oxf.fb.toolbox.group` properties](/configuration/properties/form-builder#groups-of-controls).
* The [section templates](/form-builder/advanced/section-templates) libraries update.
* [Section templates](/form-builder/advanced/section-templates) used in your form also update to their latest published version.

\[SINCE Orbeon Forms 2019.1]

* The list of all available (published) *versions* of section templates is updated.
* The currently-selected versions of section templates, if still available, are updated to reflect their latest published.

For more, see [Versioning of section templates](/form-builder/advanced/section-templates#versioning-of-section-templates).

### Orbeon Forms 2017.2 and newer

The "Reload Toolbox" button is located at the top of the "Advanced" tab.

![](/files/-LEkBvjyJnZXF5GahqjH)

### Orbeon Forms 2017.1 and earlier

The "Reload Toolbox" icon is located at the top of the toolbox:

![](/files/-LEkBvk-GBZtVovi9uko)

## Form structure

![](/files/-LEkBvk1mVkefSY6aBPp)

### New Section button

Pressing this button inserts a new section into the form. The section is inserted after the currently selected section, that is the section containing the currently selected control.

After insertion, the new section has an empty title. You can change the section title by clicking on it.

### New Grid button

Pressing this button inserts a new grid into the form. The grid is inserted after the currently selected grid within the currently selected section, that is the section and grid containing the currently selected control.

### New Repeated Grid button

Pressing this button inserts a new repeated grid into the form. The grid is inserted after the currently selected grid within the currently selected section (the section and grid containing the currently selected control).

See also [Repeated grids](/form-builder/form-editor/repeated-grids).

\[SINCE Orbeon Forms 2019.1]

You can also switch a regular grid to be repeated. See [Repeat settings](/form-builder/form-editor/repeat-settings).

## Form controls

The toolbox contains the user interface controls you can insert into your form, grouped by category:

| Category               | Description                                                                |
| ---------------------- | -------------------------------------------------------------------------- |
| **Text Controls**      | for capturing text                                                         |
| **Utility Controls**   | for descriptions and calculations                                          |
| **Typed Controls**     | for capturing other data like numbers, emails, phone numbers, etc.         |
| **Date and Time**      | for capturing a date, a time or both                                       |
| **Selection Controls** | for selecting one or more values, like dropdown menus, radio buttons, etc. |
| **Attachments**        | for attaching images or files                                              |
| **Buttons**            | for action buttons                                                         |

To add a control to your form, simply click on the control. The following insertion logic is implemented:

* If the currently selected grid cell is empty, the control is inserted there.
* Otherwise, if the cell to the right of the currently selected grid cell is empty, the control is inserted there.
* Otherwise, if the control is the last control of the grid, a new grid row is inserted and the control is inserted in the first cell of the new row.
* Otherwise, the controls in the toolbox are disabled and you cannot insert a new control.

## Drag and drop of controls

Once a form control is inserted into the form, you can drag it to a different grid cell in the form: simply click on the arrow icon that shows when you hover over the grid cell, and drag it to the new location.

\[SINCE Orbeon Forms 2022.1]

You can also drag a control directly from the toolbox to a grid cell in the form.

### Text Controls

![](/files/-LEkBvk5GE78cH7igp_0)

| Name                    | Description                                    |
| ----------------------- | ---------------------------------------------- |
| **Input Field**         | single-line input field                        |
| **Plain Text Area**     | multi-line plain text area                     |
| **Formatted Text Area** | formatted text area (AKA Rich Text Editor)     |
| **Password Field**      | input field that hides the characters you type |

The controls appear like this at design time:

![](/files/-LEkBvk8Cu2HdobFExE6)

### Utility Controls

![](/files/-M8v0Hhcbp3EBeIP3--Q)

| Name                 | Description                                                                                                                                                                                         |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Explanatory Text** | Explanatory text which does not capture a value ([blog post](https://blog.orbeon.com/2015/04/adding-explanatory-text-to-your-forms.html))                                                           |
| **Calculated Value** | Like most other controls, this control has a label and hint, but unlike most controls it is not used to enter a value. Instead, it just *shows* a value, either fixed or calculated with a formula. |
| **Hidden Field**     | This control supports most features of regular controls, but doesn't show in the form at runtime. See [Hidden field component](/form-runner/component/hidden).                                      |

### Typed Controls

![](/files/-LEkBvkC5njXROstAixY)

| Name                | Description                                                     |
| ------------------- | --------------------------------------------------------------- |
| **Number**          | number field with formatting, validation and rounding           |
| **Email Address**   | text field which validates that the content is an email address |
| **Currency**        | US dollar currency                                              |
| **US Phone Number** | US phone number with automatic formatting                       |
| **US State**        | US state selector                                               |

The controls appear like this at design time:

![](/files/-LEkBvkEgBt4nS5YnP4s)

### Date and time Controls

![](/files/-LEkBvkGioMU8HulalY8)

| Name              | Description                                  |
| ----------------- | -------------------------------------------- |
| **Date**          | date field with date picker                  |
| **Time**          | time field                                   |
| **Date and Time** | combined date and time field                 |
| **Dropdown Date** | date chooser which uses dropdown menus       |
| **Fields Date**   | date chooser which uses separate text fields |

The controls appear like this at design time:

![](/files/-LEkBvkIIKvXUBGtVEB8)

### Selection Controls

![](/files/-LEkBvkKLvyzI0iHw-Kh)

| Name                           | Selection | Description                                                  |
| ------------------------------ | --------- | ------------------------------------------------------------ |
| **Dropdown Menu**              | single    | dropdown menu                                                |
| **Radio Buttons**              | single    | radio buttons                                                |
| **Radio Buttons with "Other"** | single    | radio buttons with "Other" option to a enter a custom option |
| **Checkboxes**                 | multiple  | checkboxes                                                   |
| **Scrollable Checkboxes**      | multiple  | scrollable checkboxes                                        |
| **Yes/No Answer**              | single    | a simple "Yes" or "No" choice                                |
| **Dynamic Data Dropdown**      | single    | dynamic dropdown bound to a REST service                     |
| **Autocomplete**               | single    | autocompletion                                               |

You edit the choices of most selection controls with the [Choices editor](/form-builder/form-editor/itemset-editor).

The controls appear like this at design time:

![](/files/-LEkBvkOAcC-k1LtHnJU)

*NOTE: The "Scrollable List" control is deprecated since Orbeon Forms 2016.1 and removed from the toolbox.*

### Attachment Controls

![](/files/-LEkBvkQhniNNOUe20Lk)

| Name                          | Description                                                                                                                                                                                                                                                                        |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Single File Attachment**    | <p>- file which can be attached to the form at design time or at runtime<br>- the file can also be replaced or downloaded once attached</p>                                                                                                                                        |
| **Multiple File Attachments** | <p>- \[SINCE Orbeon Forms 2020.1]<br>- files which can be attached to the form at design time or at runtime<br>- the files can also be replaced or downloaded once attached</p>                                                                                                    |
| **Image Attachment**          | <p>- image which can be attached to the form at design time or at runtime<br>- the image can also be changed at runtime once attached</p>                                                                                                                                          |
| **Image**                     | <p>- image attached to the form at design time<br>- it is not possible to change the image at runtime</p>                                                                                                                                                                          |
| **Image Annotation**          | <p>- image which can be attached to the form at design time or at runtime<br>- the image can then be annotated with graphic objects<br>- see also the <a href="https://blog.orbeon.com/2013/08/new-image-annotation-control.html">blog post</a><br>- SINCE Orbeon Forms 2016.3</p> |
| **Handwritten Signature**     | signature drawn with a mouse or touch screen                                                                                                                                                                                                                                       |

The controls appear like this at design time:

![](/files/-LEkBvkSBF9_YCwXMFTe)

Here is an example of attachment control with multiple selection enabled:

/form-runner/component/images/xbl-attachment-multiple.png

This is the Handwritten Signature control (which of course you usually leave blank at design time):

![](/files/-LEkBvkUvLL8FdXEaMT5)

### Button Controls

![](/files/-LEkBvkWvhrwp4HTjjg1)

| Name            | Description                |
| --------------- | -------------------------- |
| **Button**      | standard button            |
| **Link Button** | button appearing as a link |

Buttons do not allow entering data, and by default do nothing significant, but they can be used to trigger actions with the Actions Editor.

*NOTE: Buttons do not appear at all in preview mode.*

The controls appear like this at design time:

![](/files/-LEkBvkY0kfVHvXAtbos)

### Choices

Choices (sometimes called options) refer to the items which are part of a selection control such as a dropdown menu or checkboxes.

For more, see the [Itemset Editor](/form-builder/form-editor/itemset-editor).

### Data dropdown

From the perspective of people who will be filling out your form, the data dropdown works just like a regular dropdown. However, the data in the dropdown comes from a service. For instance, imagine you have a list to select a state and that you don't want to hard code the list of states in the form, either for convenience, or because the subset of selectable states is dynamic:

1. Insert a data dropdown field.
2. Click on cogwheel to bring up a *Control Settings* dialog, similar to the one shown to the right.
3. In the *Resource URI*, enter the address of an HTTP service that returns the data you want to use to populate the dropdown. In most cases, the URL will look like `http://your-host/your-service`. If the address you specify start with a `/`, it is relative to the Orbeon Forms web app, which allows you to access a service you might have implemented in Orbeon Forms with XPL. For this example, let's assume your service returns a list of states that looks like:

```xml
<states>
    <state abbreviation="AK" name="Alaska"/>
    <state abbreviation="AL" name="Alabama"/>
    <state abbreviation="AR" name="Arkansas"/>
    ...
</states>
```

4. In the *Items* field, enter an XPath expression that returns one node per state. In this case, it will be: `/states/state`.
5. For each state (*item*), specify an expression relative to the node returning the label (shown to users in the dropdown) and the value (stored in the data). In this case, those expressions will be, respectively: `@name` and `@abbreviation`.

If the data in the dropdown depends on a value entered by users in another form field, you can pass that value to the service as a request parameter. For instance, let's say that in addition to the *State* dropdown, you have a *City* dropdown where you want to list all the cities in the currently selected state. If the service is at `/xforms-sandbox/service/zip-cities` and takes a request parameter `state-abbreviation`, assuming you named your *State* field `state`, in the *Resource URI* enter:

```
/xforms-sandbox/service/zip-cities?state-abbreviation={$state}
```

*Limitation: you can't yet use a variable as shown in the above example, to refer to another fields value. Instead, if the control is in the same section use `{../state}`. If in a different section with name other-section, use `{../../other-section/state}`. See* [*#431*](https://github.com/orbeon/orbeon-forms/issues/431)*.*

### Autocomplete

The autocomplete control is a single item selection control that loads a list of suggestions from a service. It takes the same *Resource URI*, *Items*, *Label*, and *Value* configuration parameters as the \[Data dropdown control]\[46]. You may want to pass the value of other controls to the service, but you'll always want to pass the currently typed value, as the suggestions should depend on what users typed so far. You access to the currently typed by value with `$fr-search-value`, as in the following example:

```
/xforms-controls/services/countries?country-name={`encode-for-uri($fr-search-value)}
```

## Keyboard shortcuts

See [Keyboard shortcuts](/form-builder/form-editor/keyboard-shortcuts).

## See also

* [Form editor](/form-builder/form-editor)
* [Form area](/form-builder/form-editor/form-area)
* [Buttons bar](/form-builder/form-editor/buttons-bar)
* [Section templates](/form-builder/advanced/section-templates)
* [Documented XBL components](/form-runner/component)
* [XBL components](/xforms/xbl)
* [Form Builder toolbox properties](/configuration/properties/form-builder#toolbox)
* [Component metadata](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/form-builder/metadata.md): for the component names.
* Blog post: [The new Multiple File Attachments control](https://blog.orbeon.com/2020/05/the-new-multiple-file-attachments.html)
* Blog post: [Copying and pasting across forms](https://blog.orbeon.com/2020/06/copying-and-pasting-across-forms.html)


# Buttons bar

## Introduction

The buttons bar sits at the bottom of the Form Builder screen.

![The buttons bar](/files/d1WocsRqwAQlh0DoEore)

By default, the bar includes the following buttons:

* **"Home"**:
  * Go back to the Form Runner Landing page.
  * If the current form is not saved, you will be prompted to save it.
* **Summary"**:
  * Go back to the [Form Builder Summary page](/form-builder/summary-page).
  * If the current form is not saved, you will be prompted to save it.
* **New"**:
  * Close the current form and create a new one.
  * If the current form is not saved, you will be prompted to save it.
* **Export buttons"**:
  * Excel Export: export the form in Excel format
    * This is an Orbeon Forms PE feature.
  * XML Export: export the form in "XML with metadata" format
    * This is an Orbeon Forms PE feature.
  * XHTML Export
    * [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)
    * Export the form definition in XHTML+XForms format (the Orbeon Forms form definition format).
    * This produces a file with the same content shown by the ["Edit Source" dialog](/form-builder/advanced/edit-source).
* **Test functions"**:
  * [Test](/form-builder/advanced/web-test): open the form in test mode
  * [Test PDF](/form-builder/advanced/pdf-test): open the "Test PDF" dialog for testing PDF output
    * This is an Orbeon Forms PE feature.
  * [Test Offline](/form-builder/advanced/offline-test): open the form in test mode, but using the "offline" (JavaScript) runtime (experimental)
    * This is an Orbeon Forms PE feature.
  * [Formula Inspector](/form-builder/formulas/formulas-inspector): observe formulas and their dependencies (experimental)
* **Publish"**:
  * Open the ["Publish" dialog](/form-builder/form-editor/publishing) to publish the form definition
  * This will automatically save the form definition if you complete publishing in the dialog.
* **Save"**:
  * Save, but don't publish, the in-progress form definition

The buttons are configurable with the `oxf.fr.detail.buttons.orbeon.builder` property. The default value is, for Orbeon Forms 2023.1 PE:

```xml
<property as="xs:string" name="oxf.fr.detail.buttons.orbeon.builder">
    home
    summary
    new
    form-definition-xhtml-export|excel-export|xml-export|form-definition-xhtml-export
    test|test-pdf|test-offline|test-formulas
    publish
    save
</property>
```

Buttons combined with the `|` character show as a combined "drop-up" button.

## Messages area

The buttons bar also includes transient messages, which appear in the messages area. Here is an example:

![The buttons bar with a status message](/files/0pD0KdsMEsl1ter5R9TY)

## See also

* [Form editor](/form-builder/form-editor)
* [Form area](/form-builder/form-editor/form-area)
* [Toolbox](/form-builder/form-editor/toolbox)


# Control settings

## Introduction

The Control Settings dialog allows controlling all the aspects the a control besides its label and hint. The dialog has several tabs, detailed below.

### Basic Settings

![Basic Settings tab](/files/-LEkBwwjEc0xzGzrbW8c)

#### Basic options

The control *name* specifies a identifier for the control, unique in the entire form (except [Section Templates](/form-builder/advanced/section-templates)). The identifier is used for the following:

* to refer to the control value from formulas, using the variable notation `$foo` where `foo` is the control name
* to determine an XML element name when the form data is represented as XML

If a control name is not explicitly specified, Form Builder assigns a default name, such as `control-42`.

A control name can be changed, provided it doesn't collide with another control name (an error will show otherwise).

\[SINCE Orbeon Forms 2019.1]

See [Renaming of controls and formulas](/form-builder/formulas#renaming-of-controls-and-formulas).

**Main options**

The following options are available:

* **Persist control value:**
  * [\[SINCE Orbeon Forms 2025.1.1\]](/release-notes/orbeon-forms-2025.1.1)
  * Persist the value of this control. This is selected by default for most form controls, except for the Explanatory Text and Button controls. When not selected, the control value is not persisted in the database.
  * When this option is not selected, it is not possible to index or encrypt control data (see below).
  * See also: [Non-persisted control values](https://www.orbeon.com/2026/03/non-persisted-control-values)
* **Index control:**
  * SINCE Orbeon Forms 2021.1
  * Index the value of this control even if it's not shown or searchable from the Summary page, or bulk editable. This makes the control accessible from the [search API](/form-runner/api/persistence/search).
* **Show on Summary page:**
  * When selected, the control value is visible as a Summary page column and searchable.
* **Allow bulk edit:**
  * When selected, the control value can be bulk edited from the Summary page.
* **By default, sort this by column:**
  * [\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)
  * Change the default sort column on the Summary page.
* **Encrypt data at rest:**
  * SINCE Orbeon Forms 2018.1
  * This is an Orbeon Forms PE feature.
  * See [Field-level encryption](/form-builder/advanced/field-level-encryption) for details.

**Role restriction**

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

It is possible to restrict the "Show on Summary page" and "Allow bulk edit" options to one or more specific roles (i.e. group of users). When multiple roles are specified, the logic that applies can be chosen as well. If "All" is selected, then all specified roles must be present. If "Any" is selected, just one of the specified roles must be present.

<figure><img src="/files/FYvq0gYfCEyJuPIK8mWz" alt="Role restriction" width="492"><figcaption><p>Role restriction</p></figcaption></figure>

**Bulk action**

[\[SINCE Orbeon Forms 2024.1\]](/release-notes/orbeon-forms-2024.1)

By default, bulk edition of form data on the Summary page will simply save the updated form data. You can also specify a custom process to run by selecting "Run process" instead of "Save data".

<figure><img src="/files/XCKrmymfyffdaRQefP3u" alt="Bulk process" width="485"><figcaption><p>Bulk process</p></figcaption></figure>

The selected process is responsible for saving the data.

It's possible to specify a list of processes available for selection in the Control Settings dialog. This is done by specifying the following property:

```xml
<property
    as="xs:string"
    name="oxf.fr.summary.bulk-processes.*.*"
    value="process1 process2"/>
```

The value is a space-separated list of process names. By default, this property is empty.

The processes must not only be listed in the `oxf.fr.summary.bulk-processes` property, but also be defined as individual `oxf.fr.detail.process` properties. See [Simple process syntax](/form-runner/advanced/buttons-and-processes/syntax) for more information.

#### Default sort column

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1) By default, data entries on the Summary page are sorted by last modification date/time, from most recent to least recent. You can change that behavior by checking the "By default, sort by this column" checkbox. When this setting is enabled, the Summary page will sort the data entries by the selected control when first loaded. It will still be possible to change the sort column and direction by clicking on the column headers.

<figure><img src="/files/bH7N5xcp9NyjpIJiYj9T" alt="Default sort column" width="264"><figcaption><p>Default sort column</p></figcaption></figure>

When enabled on a control, this setting will automatically be disabled on all other controls.

**Email options**

The following email options are available:

*NOTE: SINCE Orbeon Forms 2018.2, these options are in a separate list.*

* **Email recipient:**
  * When selected, the control is used to determine an email recipient ("To:") when the form data is sent by email.
  * If more than one non-blank email addresses is found, they are all included as email recipients. In addition, the `oxf.fr.email.to` property is used.
  * A single control value can contain more than one email address, separated by commas (`,`) or spaces.
* **Email carbon copy recipient:**
  * SINCE Orbeon Forms 2017.1
  * When selected, the control is used to determine a carbon copy email recipient ("Cc:") when the form data is sent by email.
  * If more than one non-blank email addresses is found, they are all included as email recipients. In addition, the `oxf.fr.email.cc` property is used.
  * A single control value can contain more than one email address, separated by commas (`,`) or spaces.
* **Email blind carbon copy recipient:**
  * SINCE Orbeon Forms 2017.1
  * When selected, the control is used to determine a blind carbon copy email recipient ("Bcc:") when the form data is sent by email.
  * If more than one non-blank email addresses is found, they are all included as email recipients. In addition, the `oxf.fr.email.bcc` property is used.
  * A single control value can contain more than one email address, separated by commas (`,`) or spaces.
* **Email sender:**
  * SINCE Orbeon Forms 2017.1
  * When selected, the control is used to determine an email sender ("From:") when the form data is sent by email.
  * Only *one* "From:" email address is used, specifically the first non-blank address selected in the form. If no such address is found the `oxf.fr.email.from` property is used.
* **Email reply-to:**
  * SINCE Orbeon Forms 2020.1
  * When selected, the control is used to determine a reply-to address ("Reply-To:") when the form data is sent by email.
  * Only *one* "Reply-To:" email address is used, specifically the first non-blank address selected in the form. If no such address is found the `oxf.fr.email.reply-to` property is used.
* **Exclude from email body:**
  * SINCE Orbeon Forms 2018.1
  * When using "All Control Values" in an [email body template](/form-builder/advanced/email-settings), controls selected with this checkbox will be *excluded* and omitted from the email body.
* **Include as email attachment**:
  * SINCE Orbeon Forms 2016.1
  * this option only shows for file and image attachments
  * when the property `oxf.fr.email.attach-files` is set to `selected`, only file and image attachments with this option checked are attached to the email
* **Show in email subject:**
  * DEPRECATED SINCE Orbeon Forms 2018.1: Use a [template for the subject](/form-builder/advanced/email-settings) instead.
  * When selected, the control value is used as part of the subject of the email when the form data is sent by email.
  * If more than one non-blank values are found, they are all included in the email subject, comma-separated.

\[SINCE Orbeon Forms 2021.1.4]

Email addresses can now contain a name, following the standard syntax:

```
John Smith <john@example.org>
```

Or:

```
"John Smith" <john@example.org>
```

When more than one email is present, if you specify names, you must use commas exclusively as a separator (with whitespace allowed around commas):

```
John Smith <john@example.org>, Alice <alice@acme.org>
```

Or:

```
"John Smith" <john@example.org>, "Alice" <alice@acme.org>
```

#### Name of the PDF field

\[SINCE Orbeon Forms 2018.2]

This informational field shows the name of the field to use when using a PDF template.

See also [PDF templates](/form-runner/features/pdf-templates).

#### Custom CSS classes

The "Custom CSS Classes" field allows you to add CSS classes that will be applied to the control in the resulting HTML. This is useful for applying custom styling. Note that CSS classes themselves do not directly define styles; instead, they serve as space-separated tokens that you can reference in [your own CSS files](/form-runner/styling/css).

For example, you might specify:

```
my-name-field my-highlight
```

In this example, the `my-` prefix is used as a best practice to prevent conflicts with other CSS classes on the page. In your custom CSS file, you can target these classes using a selector, such as:

```css
.orbeon .my-name-field.my-highlight input {
  border-color: blue;
}
```

#### Autocomplete attribute

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

The "Autocomplete attribute" dropdown allows specifying an [HTML autocomplete value](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) for controls for which it makes sense to do so. Such a value tells the browser that it can suggest values for the field or, in the case of the `off` value, that it shouldn't suggest any value.

#### Control appearance

\[SINCE Orbeon Forms 4.10]

Some controls support more than one appearance. For example, a single selection control can appear as a dropdown menu, or as radio buttons. When available, the "Control Appearance" selector allows selecting and changing the appearance of the control.

See also [How the new Form Builder Appearance Selector Works](https://blog.orbeon.com/2015/06/how-new-form-builder-appearance.html).

#### Custom control settings

Some controls have custom settings. For example:

![Custom Control Settings](/files/-LQ5xhPF0FMelVWFBjkb)

The following controls have custom settings:

* [Number](/form-runner/component/number#form-builder-customer-settings)
* [Currency](/form-runner/component/currency#form-builder-customer-settings)
* [US Phone Number](/form-runner/component/us-phone#form-builder-customer-settings)
* Date
* Time
* Date and Time
* [Dropdown Date](/form-runner/component/dropdown-date#form-builder-customer-settings)
* Dropdown
* Dropdown with Search
* [File Attachment](/form-runner/component/attachment#form-builder-customer-settings)
* [Image Attachment](/form-runner/component/image-attachment#form-builder-customer-settings) \[SINCE Orbeon Forms 2022.1]

See also [Control metadata for the Control Settings dialog](/form-builder/advanced/extensibility/metadata#control-metadata-for-the-control-settings-dialog)

#### Automatic PDF options

\[SINCE Orbeon Forms 2021.1]

When producing a PDF file, single-selection controls like the "Static Dropdown" normally just output the selected value. However, in some cases, it is useful for the user to see all the available options.

You can achieve this with the "Automatic PDF" options in the "Control Settings" dialog for single-selection controls. This option is hierarchical: you can set it at the field level, form level, or globally. They are:

* **Use default**: use the form settings, and if they also have "Use default" use the property (see below)
* **Show the selected value only**: the label for the selected value shows
* **Show all values as radio buttons**: all possible values show as radio buttons and the selected value, if any, shows selected

<figure><img src="/files/1a0bY8FgPWFdugaMMLFQ" alt="Automatic PDF options" width="230"><figcaption><p>Automatic PDF options</p></figcaption></figure>

The supported properties are:

* `oxf.xforms.xbl.fr.dropdown-select1.pdf-appearance.*.*`
* `oxf.xforms.xbl.fr.dropdown-select1-search.pdf-appearance.*.*`

The possible values for each property are:

* empty: show the selected value only (default)
* `full`: show all values as radio buttons

Example to change the default the "Static Dropdown" control to showing all values in the automatic PDF:

```xml
<property 
    as="xs:string" 
    name="oxf.xforms.xbl.fr.dropdown-select1.pdf-appearance.*.*"
    value="full"/>
```

The global default is "Show the selected value only".

### Validations and alerts

![Validations and alerts tab](/files/-LEkBwwzQBuFcIscjgrv)

See [Form Builder Validation](/form-builder/form-editor/validation) for details.

### Formulas

![Formulas tab](/files/-LEkBwx0uppKKLVgHDn6)

See [Formulas](/form-builder/formulas) for details.

\[SINCE Orbeon Forms 2018.2]

The "Yes" and "No" options have their own radio button. Select "Formula" to enter a dynamic "Visibility" or "Read-Only" formula.

### Explanatory Text

\[SINCE Orbeon Forms 2019.1]

For the Explanatory Text control only, you can modify the text in this location, including making the text dynamic using templates as is the case for the Label, Hint and Help Message.

![Explanatory Text tab](/files/-LVyWLvUjl8PnTrNFelc)

### Label and Hint

\[SINCE Orbeon Forms 2017.2]

In addition to setting a control's label and hint in place in the form area, you can also set and update them in this tab. You can switch between plain text and HTML text as well. The "Previous" and "Next" buttons allow quick navigation between controls.

![Label tab](/files/-LEkBwx2g0igpDH-gE9R)

#### Automatic hints

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

For attachment controls, in addition to the hint you provide for the control, Orbeon Forms can add an automatic hint informing the user about the maximum allowed file size and the accepted file types, if you have specified such validations in the Validations and Alerts tab.

Automatic hints are disabled by default, and you can enable them for a specific control in the Control Settings dialog, for all controls in a form in the Form Settings dialog, or globally using the property below. A setting other than "Default" in the Control Settings dialog box overrides a setting other than "Use property" in the Form Settings dialog box, which overrides the value of the property below.

```xml
<property 
    as="xs:boolean"
    name="oxf.fr.detail.hint.automatic.*.*"                           
    value="true"/>
```

### Help Message

![Help tab](/files/-LEkBwx4SaqLJdT4KLKd)

This allows specifying some help text, which can be plain text or rich text when the "Use HTML" checkbox is selected.

The help message is available at runtime through a help icon positioned next to the control. By default, the icon opens a pop-up containing the help text. In *noscript* mode (removed since Orbeon Forms 2018.1), the icon links to a help section at the bottom of the form.

The help text is localizable.

See also [Improving how we show help messages](https://blog.orbeon.com/2014/01/improving-how-we-show-help-messages.html).

### Dynamic labels, hints and help messages

\[SINCE Orbeon Forms 2018.1]

Controls support *dynamic* labels, hints, and help messages. This means that, instead of being specified once and for all at form design time, labels, hints and help messages can incorporate dynamic parts such as control values and other custom expressions.

For more, see [Template syntax](/form-builder/advanced/template-syntax).

### Language selector

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

When multiple languages are available for the form, a language selector allows selecting the language for which localizable texts are being edited (e.g. Validations and Alerts, Label, Hint, and Help Message) directly from the Control Settings dialog.

![Language selector](/files/UhAKQQL0f2DR1gnARuSE)

## Keyboard shortcuts

See [Keyboard shortcuts](/form-builder/form-editor/keyboard-shortcuts).

## See also

* [Control metadata for the Control Settings dialog](/form-builder/advanced/extensibility/metadata#control-metadata-for-the-control-settings-dialog)
* [Form Builder Validation](/form-builder/form-editor/validation)
* [Formulas](/form-builder/formulas)
* [Template syntax](/form-builder/advanced/template-syntax)
* Blog posts
  * [Enhanced validation in Form Builder and Form Runner](https://blog.orbeon.com/2013/07/enhanced-validation-in-form-builder-and.html)
  * [Improving how we show help messages](https://blog.orbeon.com/2014/01/improving-how-we-show-help-messages.html)
  * [How the new Form Builder Appearance Selector Works](https://blog.orbeon.com/2015/06/how-new-form-builder-appearance.html)
  * [Improved constraints on attachments uploads](https://blog.orbeon.com/2017/04/improved-constraints-on-attachments.html)
  * [More flexible email senders and recipients](https://blog.orbeon.com/2017/05/more-flexible-email-senders-and.html)


# Dependent fields and sections

## Rationale

* Both fields and sections can:
  * Be shown or hidden depending on the value of other fields, by setting their **visibility** property.
  * Become read-only, as opposed to read-write, depending on the value of other fields, by setting their **read-only** property.
* In addition, fields can also:
  * Have a dynamic value set when the form is first loaded by users, by setting their **initial value** property.
  * Have a value that is updated "live" as users set or change the value of other fields in the form, by setting their **calculated value** property.

By using a combination of the aforementioned visibility, read-only, initial value, and calculated value properties for sections and fields, you can create forms that are very dynamic, which can behave like a spreadsheet, or like a small application.

The visibility, read-only, initial value, and calculated value are set using [formulas](/form-builder/formulas) in the [Control Settings](/form-builder/form-editor/control-settings) or [Section Settings](/form-builder/form-editor/section-settings) dialogs.

## Examples

### Hide a field based on the user's answer to an earlier questions

Say you want to show a textarea when users check a checkbox to indicate they want to provide more details:

![Textarea shown if checkbox is checked](/files/-MLBhL8GCjy58UxLbg08)

1. Add a Single Checkbox control, open its Control Settings, name it `provide-additional-details`.
2. Add a Plain Text Area control, open its Control Settings, open the Formulas tab, set the Visibility to Formula, an enter `$provide-additional-details/string() = 'true'`.


# Validation

## Introduction

An important part of designing a form is to prevent incorrect data from being captured. For example:

* an applicant's first and last names is required
* an applicant's age must be a positive number and has to be greater than a minimum
* an id number has to follow a specific syntax

If such conditions are not met, the user must see an *error* and cannot submit the form until they are corrected.

In addition, some values might be correct, but the user should be encouraged to pay special attention to them. In such cases, the user should see a *warning* or *informational message* before submitting the form.

Form Builder supports this kind of validations via the "Validations and Alerts" tab of the "Control Settings" dialog. You open the dialog with the "Control Settings" icon to the right of each control.

*NOTE: Prior to Orbeon Forms 4.3, the dialog was called "Validation Properties". Orbeon Forms 4.3 combines dialogs and adds functionality.*

![Control validation settings](/files/-LEkC5V_Hi-90aIve8oZ)

## Validation types

### Introduction

The value associated with a control can be validated with 3 different validation types:

1. *Required*. This indicates whether the value can be empty or not.
2. *Data Type*. For example `string`, `decimal`, `date`, or `time`.
3. *Formula*. A custom formula, expressed in XPath, which determines whether the value is valid or not for a certain validation level.
4. *Common Constraints*. This includes constraints such as "Maximum Length", "Minimum Length", and more. \[SINCE Orbeon Forms 4.10]

### Required validation

This simple validation has 3 possibilities:

* *Yes*: a value is required and cannot be empty. (*NOTE: Blank spaces count as "not empty".*)
* *No*: a value is not required and can be empty.
* *Formula*: an XPath expression to dynamically control whether the control is valid or not. \[SINCE Orbeon Forms 4.7]

![Options for required validation](/files/-LEkC5VwndfWtxEALqSA)

When the value is required, an asterisk appears next to the control to signify to the user that the value is required.

At runtime, if the value is required but not empty, the value is marked as invalid.

\[SINCE Orbeon Forms 4.9]

The Required validation can have a custom alert message.

### Trimming leading and trailing spaces

\[SINCE Orbeon Forms 2016.1]

When the "Trim leading and trailing spaces" option is enabled, leading and trailing spaces are removed from the value of the control before validation. This option is enabled by default for text and email fields.

When used in conjunction with the Required validation, this allows making sure that a required field doesn't contain just spaces and in fact contains some value.

This option is also useful for non-required fields which must not contain leading or trailing spaces. For example if the user enters " Sam", the value is automatically converted into "Sam" as the user focuses out of the field.

See also [Required fields: more subtle than you might think](https://blog.orbeon.com/2016/02/required-fields-more-subtle-than-you.html).

![Whitespace trimming](/files/-LEkC5W-Ff_PhkPn1g2X)

### Data type validation

The list of data types includes:

* Built-in types
* XML Schema types (only if an XML Schema with simple types was attache to the form).

Built-in types include:

* Common Types
  * String: any string of characters
  * Boolean: `true` or `false`
  * Email address: an email address
* Number Types
  * Decimal: positive or negative decimal number, which can be fractional with an optional decimal point `.`
  * Integer: positive or negative integer decimal number
  * Double-precision floating-point
    * \[UNTIL Orbeon Forms 4.10]
    * \[SINCE Orbeon Forms 2021.1] (again, see below)
* Date and Time Types
  * Date: date stored in ISO format
  * Time: time stored in ISO format
  * Date and Time: date and time stored in ISO format

![Built-in Types](/files/-LEkC5W2OEOzYTbyUC_v)

*NOTE: Prior to Orbeon Forms 4.3, the list of available types was longer, as explained in* [*this blog post*](https://blog.orbeon.com/2013/07/simplifying-list-of-data-types-in-form.html)*.*

*NOTE: Until Orbeon Forms 4.10, the "Double-precision floating point" data type was available. It was removed with Orbeon Forms 2016.1, as explained in this* [*blog post*](https://blog.orbeon.com/2016/01/removing-double-datatype.html)*, and re-added with Orbeon Forms 2021.1.*

*The reason for the removal was that in the vast majority of cases, this is not the appropriate type ("Integer" or "Decimal" are), and users would often select the incorrect type and then get rounding errors in fields such as the Currency field.*

*The reason for re-adding the double-precisiong floating-point data type is that it is appropriate for certain scientific calculations. This said, we recommend its use only in very specific circumstances.*

At runtime, if the value is required and does not match the specified datatype, the value is marked as invalid.

*NOTE: When selecting certain controls from the toolbox, such as "Email", "Date", "Time", and "Date and Time", the appropriate data type is already selected by Form Builder. Changing the type to a different type might change the appearance of the control to match the type selected.*

If an XML Schema containing simple types has been attached to the form \[Orbeon Forms PE only], the simples types are listed in the Schema Type menu.

![Built-in Types](/files/-LEkC5WECdrg8lFENX1x)

Either a built-in data type or an XML Schema data type can be selected. If you select an XML Schema data type, the built-in data type is automatically reset. Similarly, if you select a built-in data type, the XML Schema data type is reset.

At runtime, if the value is required and does not match the specified datatype, the value is marked as invalid. For example, if the value must be an `integer`, the value "John" is invalid.

*NOTE: If the control is of type `string`, doesn't have a constraint and is not required, then any value entered is valid. This is the default for input fields and text areas.*

When an XML Schema data type is selected:

* If *Required* is set to *Yes*, the control is still made required, and an asterisk appears.
* If *Required* is set to *No*, the value must still match the definition of the XML Schema type to be valid. If the XML Schema type requires a non-empty value, setting *Required* to *No* does not make the value optional.

\[SINCE Orbeon Forms 4.9]

The Required validation can have a custom alert message.

### Formula validation

A formula validation is a boolean XPath expression running with the XML element containing the value as context item. The validation *fails* if the expression doesn't return `true()`. This also means that it fails if there is an error while running the validation.

See also [Form Builder Formulas](/form-builder/formulas).

For example the following expression, which would make sense for a birthday date field, checks that the user is 18 year old or older:

```xpath
. <= (current-date() - xs:yearMonthDuration("P18Y"))
```

\[SINCE Orbeon Forms 4.3]

There can be more than one formulas applied to a given control. You add formulas with the `+` icon and remove them with the `-` icon.

\[SINCE Orbeon Forms 4.3]

Each formula can have a *level* associated with it and a custom alert message.

### Dates to Exclude constraint

\[SINCE Orbeon Forms 2018.2]

The Dates to Exclude constraint takes a list (sequence) of dates to exclude, provided by a formula. The constraint fails if users type a date that is in the supplied list of dates to exclude. Also, dates in this list are disabled in the non-native date picker, so users cannot select them, as in the case of the 18th and 20th in the screenshot below. When the date picker is native, the dates are not disabled because the native date picker doesn't support disabling specific dates, but the validation still applies.

<figure><img src="/files/-LU9G_eqXh56gujs50oC" alt="Dates disabled in date picker" width="239"><figcaption></figcaption></figure>

You might have a service storing a the list of dates to exclude in a [dataset](/form-runner/features/datasets), for example. From that dataset, you can extract and convert the dates to XPath dates.

For example, assuming the following `my-excluded-dates` dataset, with dates in the ISO format:

```xml
<result>
  <date>2018-12-31</date>
  <date>2019-01-15</date>
  <date>2019-01-31</date>
</result>
```

You can pass the following expression as parameter to the "Dates to Exclude" common constraint:

```xpath
for $d in fr:dataset('my-excluded-dates')/date return xs:date($d)
```

### Other common constraints

* "Maximum Length"
  * SINCE Orbeon Forms 4.10
  * applies to "String" and "Email" types only
  * the constraint fails if the length of the value converted to a string is larger than the specified integer value
* "Minimum Length"
  * SINCE Orbeon Forms 4.10
  * applies to "String" and "Email" types only
  * the constraint fails if the length of the value converted to a string is smaller than the specified integer value
* "Positive"
  * SINCE Orbeon Forms 2016.1
  * applies to "Decimal" and "Integer" types only
  * the constraint fails if the number is not positive
* "Positive or Zero"
  * SINCE Orbeon Forms 2016.1
  * applies to "Decimal" and "Integer" types only
  * the constraint fails if the number is not positive or zero
* "Negative or Zero"
  * SINCE Orbeon Forms 2016.1
  * applies to "Decimal" and "Integer" types only
  * the constraint fails if the number is not negative or zero
* "Negative"
  * SINCE Orbeon Forms 2016.1
  * applies to "Decimal" and "Integer" types only
  * the constraint fails if the number is not negative
* "Maximum Fractional Digits"
  * SINCE Orbeon Forms 2016.1
  * applies to "Decimal" type only
  * the constraint fails if the decimal number has more than the specified number of significant digits (trailing zeros are ignored)
* "Maximum Attachment Size" ("Maximum Attachment Size per File" in Multiple File Attachments controls)
  * SINCE Orbeon Forms 2017.1
  * applies to Single and Multiple File Attachments controls only
  * the constraint fails if the attachment size is larger than the specified size
* "Maximum Aggregate Attachment Size"
  * SINCE Orbeon Forms 2024.1
  * applies to Multiple File Attachments controls only
  * the constraint fails if the attachment size is larger than the remaining allowed size for the control
* "Maximum Number of Files per Control"
  * [\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)
  * applies to Multiple File Attachments controls only
  * the constraint fails if users attempt to attach more than number of specified files
* "Supported File Types"
  * SINCE Orbeon Forms 2017.1
  * applies to Single and Multiple File Attachments controls only
  * the constraint fails if the attachment mediatype doest not match one of the included mediatypes or mediatype wildcards
  * when listing multiple media types, separate them by a space

In the future, it is expected that more common constraints will be added (see [#2281](https://github.com/orbeon/orbeon-forms/issues/2281)).

![Min and max length constraints](/files/-LEkC5WNsb5f8E5orVr-)

## Control validity

A control value (entered by the user, constant, or calculated) is either *valid* or *invalid*. It is invalid if any of the following conditions is met:

* It is required but remains empty.
* It does not match the selected data type.
* There is at least one failed error formula or common constraint validation.

## Validation levels

\[SINCE Orbeon Forms 4.3]

If a control is valid, it can have a *warning* level. This is the case if there is at least one failed warning validation.

If a control doesn't have a warning level, it can have an *info* level. This is the case if there is at least one failed info validation.

A warning or info level does not make the control value invalid and it is still possible to submit form data.

*NOTE: It is not possible to associate a validation level to the required or data type validations: they always use the error level.*

## Localization of messages

\[Orbeon Forms PE only]

All alert messages can be localized.

When opening the dialog, the current language of the form determines the language used. To switch languages:

* close the dialog
* select another form language
* reopen the dialog

## Alert messages

When the user enters data, if the value is invalid or if the control has a warning or info level, the control is highlighted and one or more alert messages are shown. The message is selected as follows, generally following the philosophy of "more specific messages win over less specific messages":

*NOTE: Since 4.6.2, required validations take precedence over other validations, see* [*#1830*](https://github.com/orbeon/orbeon-forms/issues/1830)*.*

* If a required validation has failed:
  * The default alert message for the control is used if available, or a global default Form Runner message is used otherwise.
  * Other messages are not used, even if there are data type or error formula or common constraint validations.
* If data type validation has failed:
  * The default alert message for the control is used if available, or a global default Form Runner message is used otherwise.
  * Other messages are not used, even if there are error formula or common constraint validations.
* If at least one error validation has failed:
  * If no specific alert message is specified for the validation, the default alert message for the control is used if available, or a global default Form Runner message is used otherwise.
  * If a specific alert message is specified, then it is used.
  * More than one message can show is several error validations have failed.
* Only if the control is valid, if at least one warning formula or common constraint has failed:
  * The specific alert message is used.
  * More than one message can show is several warning validations have failed.
* Only if the control is valid and doesn't have any failed warning s, if at least one info formula or common constraint has failed:
  * The specific alert message is used.
  * More than one message can show is several info validations have failed.

*NOTE: It is not possible to associate specific alert message to the required or data type validations: they always use the default or global alert message.*

Alert messages appear:

* under the control value
* in the Error Summary section of the form
* as badge counts in the navigation bar
* as general count in the browser's title bar

![Alert Messages](/files/-LEkC5Wegthix-1JvV-f)

## Validation errors and review messages dialog

By default, when saving or sending form data, the following happens:

* If any control value is invalid, a dialog shows and the operation is stopped.
* If all controls are valid and there are no warning or info messages, the operation continues.
* If all controls are valid and there is at least one warning or info message, the "Review Messages" dialog shows. \[SINCE Orbeon Forms 4.3]

![Review Messages](/files/-LEkC5KBEjDkuvu6WRcF)

The user has the following choices:

* Stop the operation, close the dialog and review the warnings and/or informational messages.
* Continue the operation, ignoring the warnings and/or informational messages.

These processes are entirely configurable. See [Buttons and Processes](/form-runner/advanced/buttons-and-processes) for more information.

Optionally, it is possible to annotate the XML data submitted with error, warning or informational messages. See [Buttons and Processes](/form-runner/advanced/buttons-and-processes) for more information.

## Using an external validation service

You can validate a field using an external validation service as follows:

1. In your form, you'll want to have:
   * (a) the field you want to validate,
   * (b) a [hidden field](/form-runner/component/hidden) used to store the result from the validation.
2. You create (c) an HTTP Service for your validation service
3. You create an action, which, when the value of the field you want to validate (a) changes, calls the service (c), passing the value of the field (a), and stores the result from the validation in the hidden field (b). That result is typically be a boolean, `true` if valid, and `false` if invalid.
4. In the Control Settings for the field you want to validate (a), in the Validations and Alerts tab, you use a formula to declare that the field is valid only of the value of the hidden field (b) is `true`.

## See also

* Documentation
  * [XForms Validation](/xforms/core/validation)
  * [Date component](/form-runner/component/date)
* Blog posts
  * [Better formulas with XPath type annotations](https://blog.orbeon.com/2013/01/better-formulas-with-xpath-type.html)
  * [Formulas for summing values, done right](https://blog.orbeon.com/2013/08/formulas-for-summing-values-done-right.html)
  * [Control required values with formulas in Orbeon Forms 4.7](https://blog.orbeon.com/2014/09/control-required-values-with-formulas.html)
  * [How Common Constraints Work](https://blog.orbeon.com/2015/07/how-common-constraints-work.html)
  * [Required fields: more subtle than you might think](https://blog.orbeon.com/2016/02/required-fields-more-subtle-than-you.html)


# Choices editor

## Introduction

You can edit the possible choices for selection controls by clicking on the icon that shows to the right of a control. When doing so, a dialog, like the one shown in the following screenshot, will appear.

![](/files/-LEkBvnaLnTOjJ7Y4-1d)

For each choice, you can enter:

* **Label**: This is what users see when they fill out the form.
* **Value**: This is what is stored as part of the data when users select this choice.
* **Hint**: The third column only shows for radio buttons and checkboxes.
* **Selection**: For radio buttons and checkboxes, whether the item is selected by default.

If you provide a hint for a choice, that choice will be highlighted and the hint you provided will show when users move the mouse pointer over the label, as shown in the following screenshot:

![](/files/-LEkBvnd998utaw7mdoP)

For radio buttons and checkboxes, the "Mark this checkbox if you'd like to type HTML tags" option shows. If you check the HTML checkbox, all the hints and labels you type in dialog are interpreted as HTML, allowing you to use HTML tags in label and hints, say to make text bold or italic:

![](/files/-LEkBvnfC_HboZQaRHng)

This results in the following hint:

![](/files/-LEkBvnh2c3Qket73H-e)

You add items using the "+" button.

You move, insert and remove items using the menu to the left of each row. For example to remove an item entirely, use the "Remove Line" menu item:

![](/files/-LEkBvnj5JjO7p6TQTZc)

## Selected items

You can select a default value for a radio button directly in the Form Builder editor simply by clicking the desired radio button or checkbox. You can also do this in the choices editor by clicking the radio button or checkbox associated with an item.

For radio buttons, the choices editor allows you to clear the selected value with the "Clear Value" button.

## Internationalization

When the form has more than one language, the choices editor opens in the language selected in Form Builder. You can switch between languages directly in the choices editor. Switching the language allow you to localize labels and hints. However, values are shared between all languages.

![](/files/-LEkBvnlGLw6vPM5mAD9)

Item values are not localizable: they remain the same for each language. On the other hand, item labels can be localized. For example:

* English
  * Name: "Strawberry"
  * Value: "strawberry"
* French
  * Name: "Fraise"
  * Value: "strawberry"

This ensures that the data captured is machine-readable even if the user interface language changes.

![](/files/87YinkntXdG4H7baE8Tz)

## Removing all items

\[SINCE Orbeon Forms 2016.3]

The "Remove All" button removes all the items in the itemset. This is particularly useful when opening the choices editor for the first time for a given control, since by default itemsets contain default values.

## Filtering items

[\[SINCE Orbeon Forms 2024.1.1\]](/release-notes/orbeon-forms-2024.1.1)

The "Filter" field allows filtering items by label or value by entering a formula:

* the formula's context is an individual `<item>` element, which contains
  * a `<label>` child element
  * a `<value>` child element
  * a `<hint>` child element
* the formula must return a boolean value

In the following example, the following formula will filter out the item with `strawberry` as a value if the form control named `allow-fruits` is set to `false`:

```xpath
$allow-fruits/string() = 'true' or value != 'strawberry'
```

![](/files/pjbjbUpwiOz6xkKqmJrg)

## Copying and Pasting Items

\[SINCE Orbeon Forms 2026.1.1]

The "Copy to Clipboard" and "Paste from Clipboard" buttons allow you to easily copy/paste items from/to the itemset editor. The former takes all items (label/value/hint) and stores them into the clipboard, and the latter does the opposite (Hint being optional). You can, for example, paste/copy to/from Google Sheets and Excel, in order to manage or track items in a spreadsheet.

When pasting items, they are matched by value, for the current language:

* existing values are updated
* missing values are added

If you want to replace all items, use "Remove All" first.

## Usability notes

* When in a label field, pressing the "tab" key into an empty value field automatically creates a default value. For example:
  * "Apple" becomes "apple"
  * "Wax Apple" becomes "wax-apple"
* When in a value field, pressing the "enter" key automatically adds a new item after the current item.

## Constraints on multiple selection controls values

While for *single selection controls* (e.g. radio buttons), the value can be any string of characters, for *multiple selection controls* (e.g. checkboxes), the value must **not contain spaces**.

The reason for this restriction is that, in the data, the values of checkboxes are stored as *space-separated tokens*. So if you have values `chocolate` and `strawberry` selected, you'll have in the data:

```
chocolate strawberry
```

If the value has a space in it, say `chocolate chip`, then the result would be:

```
chocolate chip strawberry
```

and it would be unclear which values are selected.

## See also

* Blog post: [Hints for checkboxes and radio buttons](https://blog.orbeon.com/2014/02/hints-for-checkboxes-and-radio-buttons.html)


# Publishing

## Introduction

The notion of *publishing* is central to Form Builder/Form Runner.

* As a form author, you first work on a form definition in a special space where the form can be modified, saved, and tested.
* Once the form definition is ready, you *publish it* to Form Runner.
* After that moment:
  * the form becomes available by form users for data entry
  * Form Builder is no longer part of the equation

## First publish

When you start publishing a form with the Publish button at the bottom of Form Builder, a dialog opens to confirm the application name and form name:

![Creating a new version](/files/FAplwTP5CeUqnOrwGWjM)

If you decide to go ahead with publishing, simply use the Publish button.

\[SINCE Orbeon Forms 2017.2]

You can decide whether the published form is *available* to end users with the "Make published form available" option. You can later change the availability of the published form either in Form Builder by re-publishing it, or from the [Forms Admin page](/form-runner/form-runner-pages/forms-admin-page#controlling-form-definitions-availability). By default, the form is made available.

## Versioning

### New version or overwrite

When versioning is enabled, you have a choice, when publishing, of whether to *create a new form version* or to *overwrite an existing one*.

![Creating a new version](/files/-LEkBwwLIDKHC9nZMEPp)

When creating a new form version, publishing the form definition creates a new version of the form definition in the database. Data which already exists in the database will still be viewed and edited with the previous versions of this form definition which are associated with that data. The new form definition version will be used to create, edit and view new data.

![Overwriting an existing version](/files/-LEkBwwNtxUT8onzoITk)

When overwriting an existing form definition, if the changes you have made to the form definition are small and non-structural, such as changing control labels only, the form definition will be compatible with existing form data. But if you have made structural changes, such as adding, removing, renaming, or moving controls, the form definition might be incompatible with existing data. We recommend being careful when overwriting an existing form definition version.

See also [versioning](/form-runner/features/versioning) for details about the implications of these options.

\[SINCE Orbeon Forms 2016.1]

You can overwrite not only the latest published version, but any previous version.

### Versioning comments

\[SINCE Orbeon Forms 2016.2]

You can add or update a textual comment associated with the given published version. Adding a comment is not required, but setting a comment can help the form author understand what changes a given form version includes.

When creating a new version, the field is initially empty:

![Empty comment](/files/FAplwTP5CeUqnOrwGWjM)

You can set an explanatory comment:

![Original comment](/files/jpISoF2iAgowolRjZcXm)

When overwriting an existing form version, the existing comment, if any, is read back from the published form definition and you can update it before publishing:

![Updated comment](/files/iFuVc83a5K96aDOYLiaj)

When you publish a new version, a completely separate comment is created for that version:

![Original comment for the new version](/files/ZFli2zxbfPcjhLLOvuiD)

## See also

* [Application name and form name](/form-runner/overview/terminology#application-name-and-form-name)
* [Versioning](/form-runner/features/versioning)
* Blog posts:
  * [Form versioning](https://blog.orbeon.com/2014/02/form-versioning.html)
  * [Choosing the best versioning option when publishing a form](https://blog.orbeon.com/2015/01/choosing-best-versioning-option-when.html)
  * [Versioning comments](https://blog.orbeon.com/2016/09/versioning-comments.html)


# Cut, copy and paste

## Introduction

## Cut, copy and paste from the toolbox

The top of the toolbox has the usual cut, copy, and paste icons:

![Cut, copy and paste icons](/files/-LEkBzFc-dec6GwBva7_)

They allow performing the usual cut/copy/paste operations on form controls.

* **Cut:**
  * Copies the control in the currently selected grid cell to the Form Builder clipboard.
  * Then removes the control from the current grid cell.
  * If the cell is empty, nothing happens.
* **Copy:**
  * Copies the control in the currently selected grid cell to the Form Builder clipboard.
  * The copied control is left as is in its cell.
  * If the cell is empty, nothing happens.
* **Paste:**
  * Pastes the contents of the Form Builder clipboard.
  * If the clipboard contains a control, it is inserted in the next available grid cell.
  * If the clipboard contains a grid or section (see below), it is inserted in the next available position.
  * If the clipboard is empty, nothing happens.

The following control information is copied and pasted:

* type and appearance
* name
* label, hint, and help
* default value
* itemset
* validations, including datatype, constraints, and alerts
* formulas
* any associated localized resources

When a control is pasted, if the control name of the clipboard control is currently not in use in the form, it is used. Otherwise, a new automatically-generated name is chosen by Form Builder.

## Keyboard shortcuts

\[SINCE Orbeon Forms 2020.1]

You can use keyboard shortcuts to cut, copy and paste.

See [Keyboard shortcuts](/form-builder/form-editor/keyboard-shortcuts#cut-copy-and-paste) for details.

## Copying and pasting between forms

\[SINCE Orbeon Forms 2020.1]

Before Orbeon Forms 2020.1, the cut, copy, and paste operations were restricted to the currently running instance of Form Builder and did not apply between different Form Builder windows or tabs, or between edition sessions of a same form.

With Orbeon Forms 2020.1, these operations are shared between forms belonging to the same user session. For example, the following scenarios work:

1. You can copy an item, go back to the Form Builder Summary page, reopen the same form or open a different form, and paste the item just copied.
2. You can open several forms with Form Builder in different tabs or windows of the same browser (and the same browser user), and copy/paste items between these forms.

*NOTE: It is for now not possible to copy and paste items between different browsers, different users, or different user sessions (for example after logging out and logging back in).*

## Cut, copy and paste of grids and sections

\[SINCE Orbeon Forms 2017.2]

When hovering over a grid or section, "Cut" and "Copy" icons are available:

!["Cut" and "Copy" icons for grids and sections](/files/-LEkC7J9AvQ_1aTR7ZEL)

*NOTE: The "Cut" icon is not available if the section or grid is the last one in its container.*

* **Cut:**
  * Copies the given grid or section to the Form Builder clipboard.
  * Then removes the grid or section from the form.
* **Copy:**
  * Copies the grid or section to the Form Builder clipboard.
  * The copied grid or section is left as is in the form.
  * If the cell is empty, nothing happens.

You paste a cut or copied grid or section using the toolbox's "Paste" icon (see above).

If pasting the grid or section can cause name conflicts, a dialog shows:

![Control Names dialog](/files/-LEkC77TZZ0VoJXA9NK1)

The dialog shows a list of all control names within the grid or section and how they will be changed after pasting. Since control names are unique within a form definition, two controls cannot have the same name. Names that are available show in green. Names that conflict show in yellow and an automatic name is generated.

You can optionally set a prefix and/or a suffix for all names. This can help prevent automatic generation of names. For example, you could paste a US Address twice:

* first, with all control names prefixed by `shipping-`
* second, with all control names prefixed by `billing-`

![Control Names dialog with prefix](/files/-LEkC7JH1q3eWq5l0ZFd)

## Undoing and redoing cut and paste operations

\[SINCE Orbeon Forms 2017.2]

The "Cut" and "Paste" operations are undoable in the following way:

* Undoing a "cut" of a control, grid or section puts the item back to its original location. However, the clipboard retains the item.
* Undoing a "paste" of a control, grid or section removes the newly-inserted item from the form. The clipboard remains untouched.

## See also

* [Undo and redo](/form-builder/form-editor/undo-redo)
* [Section templates](/form-builder/advanced/section-templates)
* [Keyboard shortcuts](/form-builder/form-editor/keyboard-shortcuts)
* Blog post: [Copying and pasting across forms](https://blog.orbeon.com/2020/06/copying-and-pasting-across-forms.html)


# Section and grid settings

## Availability

* Sections and repeated grids: \[SINCE Orbeon Forms 3.8 or earlier].
* Non-repeated grids: \[SINCE Orbeon Forms 2019.1].

## Basic settings and formulas

### Overview

![](/files/-LEkC53L4t74PaFSKYeH)

### Section or grid name

Each section or grid has a *name* or identifier, which determines how data is represented in XML. The name name specifies an identifier for the section or grid which is unique in the entire form. If no name is explicitly specified, Form Builder assigns a default name, such as "section-1" or "grid-1".

A section or grid name can be changed, provided it doesn't collide with another control name (an error will show otherwise).

\[SINCE Orbeon Forms 2019.1]

See [Renaming of controls and formulas](/form-builder/formulas#renaming-of-controls-and-formulas).

### Page breaks

When producing PDF automatically, the "Page break before" checkbox, when enabled, ensures that the given section starts at the top of a new page.

### Custom CSS classes

The "Custom CSS classes" field allows adding CSS classes which will be placed on the control in the resulting HTML. This can be used for custom styling.

See [Control Settings](/form-builder/form-editor/control-settings#custom-css-classes) for more about custom CSS classes.

### Formulas

* **Visibility:** Specifies whether the section or grid is visible. This can be either "Yes" (default), "No", or an XPath formula, in which case the section or grid is visible only if the formula evaluates to `true()`.
* **Read-Only:** Specifies whether the section or grid is read-only (not editable). This can be either "Yes", "No" (default), or an XPath formula, in which case the section or grid is editable only if the formula evaluates to `false()`.

For a section or grid to be visible or editable, all enclosing sections/grids must be visible or editable as well.

XPath expressions are described in more details in [Formulas](/form-builder/formulas).

## Repeat settings

See [Repeat settings](/form-builder/form-editor/repeat-settings).

## Settings specific to sections

See [Section settings](/form-builder/form-editor/section-settings).

## See also

* [Repeat settings](/form-builder/form-editor/repeat-settings)
* [Section settings](/form-builder/form-editor/section-settings)
* [Grid settings](/form-builder/form-editor/grid-settings)
* [Repeated grids](/form-builder/form-editor/repeated-grids)
* [Formulas](/form-builder/formulas)
* [Grid component](/form-runner/component/grid)
* [Section component](/form-runner/component/section)
* [Wizard view](/form-runner/features/wizard-view)
* Blog post: [Dynamic loading of closed sections](https://blog.orbeon.com/2020/04/dynamic-loading-of-closed-sections.html)


# Section settings

## Settings shared with grids

See [Section and grid settings](/form-builder/form-editor/container-settings).

## Basic settings

### Overview

![](/files/-LEkC53L4t74PaFSKYeH)

### Section collapsing

\[SINCE Orbeon Forms 2016.1]

The following only applies when the form doesn't use the [Wizard view](/form-runner/features/wizard-view).

A section can be open/expanded or closed/collapsed. By default, sections are open when the form loads, unless the "Initially open" checkbox is *deselected*.

The "Collapsible" radio buttons control whether the user can collapse and expand sections:

* **Use property:** use the configuration specified with the `oxf.xforms.xbl.fr.section.collapsible` property
* **Always:** the section is collapsible no matter what the `oxf.xforms.xbl.fr.section.collapsible` property specifies
* **Never:** the section is not collapsible no matter no matter what the `oxf.xforms.xbl.fr.section.collapsible` property specifies

## Repeat settings

See [Repeat settings](/form-builder/form-editor/repeat-settings).

## Label, help message, and short labels

The label can be configured directly in the form area, by clicking on the section title, or in the "Label" tab.

The help message is configured in the "Help Message" tab. Similarly, the help message can be in plain text or use rich text (HTML).

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

A short label can be configured in the "Short Label" tab. This short label is used instead of the regular label in the table of contents when using the [wizard view](/form-runner/features/wizard-view).

![](/files/LO3q2DBDqQdO46X3BI8v)

![](/files/E19Ke5gUkCmbEaktc6vU)

![](/files/Ji0hjP5OyG9ENt5AgLjK)

[\[SINCE Orbeon Forms 2023.1.3\]](/release-notes/orbeon-forms-2023.1.3)

Section labels are now optional. If you don't provide a label in any language, by leaving it blank, the section title will not be displayed.

For more, see [Optional Section Labels](https://www.orbeon.com/2024/05/optional-section-labels).

### Dynamic label and help message

\[SINCE Orbeon Forms 2018.1]

In most cases, labels and help messages are simply localized messages without dynamic parts.

However, sections also support *dynamic* labels and help messages. This means that, instead of being specified once and for all at form design time, labels and help messages can incorporate dynamic parts such as control values and other custom expressions.

For more, see [Template syntax](/form-builder/advanced/template-syntax).

![](/files/-LJVmxfmjGLM-76fl8EZ)

![](/files/-LJVmxfoaxdQPK7Tju6b)

### Dynamic iteration label

\[SINCE Orbeon Forms 2019.1]

For sections with repeated content, the "Repetition Label" tab allows you to set a label that applies to individual repetitions. Typically, this label will use a template so that values from the repeated content can be used.

When using the [Wizard view](/form-runner/features/wizard-view), repetition labels will show in the Wizard's table of contents.

![](/files/-LmpMg3OBnIuE8BS52wf)

### Custom "Add Repetition" label

\[SINCE Orbeon Forms 2022.1]

For sections with repeated content, the "Add Repetition Label" tab allows you to set a label that applies to the button used to add repetitions.

![](/files/ThQRXIMB6DI1pT9B5OP6)

## See also

* [Section and grid settings](/form-builder/form-editor/container-settings)
* [Grid settings](/form-builder/form-editor/grid-settings)
* [Repeat settings](/form-builder/form-editor/repeat-settings)
* [Repeated grids](/form-builder/form-editor/repeated-grids)
* [Formulas](/form-builder/formulas)
* [Section component](/form-runner/component/section)
* [Template syntax](/form-builder/advanced/template-syntax)
* [Wizard view](/form-runner/features/wizard-view)
* Blog post: [Optional Section Labels](https://www.orbeon.com/2024/05/optional-section-labels)


# Grid settings

## Settings shared with sections

See [Section and grid settings](/form-builder/form-editor/container-settings).

## Basic settings

### Overview

![](/files/-M-GkArfAYWs6Y5rWxSx)

### Number of Grid Columns

\[SINCE Orbeon Forms 2020.1]

Orbeon Forms introduced a [12-column layout](/form-builder/form-editor/form-area#the-12-column-layout) with Orbeon Forms 2017.2.

It is now possible to control whether an individual grid has 24 columns instead of 12. This is useful in particular when using a fluid form layout, which can be wider and accommodate more form controls on a given line.

The default remains 12 columns. To change to 24 columns, choose the "24" option. The grid then allows moving cell boundaries on a 24-column resolution.

![Moving cell boundaries with a 12-column grid](/files/-M-GkArkwzRx__RWabFT)

![Moving cell boundaries with a 24-column grid](/files/-M-GkArnVKoTt-mtxabr)

When going from 12 to 24 columns, the grid cells positions and widths are adjusted so that the grid looks the same. Similarly, when going from 24 to 12 columns, the grid cells positions and widths are adjusted so that the grid looks the same. However, if the grid contains controls whose position or width does not allow migrating the grid back to 12 columns, the "12" option is disabled.

### Number of Grid Rows

\[SINCE Orbeon Forms 2021.1]

The dialog shows the current number of grid rows. This is the "static" number of grid rows, independently of the number of grid repetitions in the case of repeated grids.

See also [Enhancements to grids](https://blog.orbeon.com/2021/09/enhancements-to-repeated-grids.html).

### Grid Tab Order

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

Cells inside a grid can be ordered in two ways:

* By row first, then by column
* By column first, then by row

Ordering the cells by row is the default behaviour.

Ordering the cells by column impacts the tab order (i.e. the order in which cells are navigated using the tab key) and the order in which the cells are placed on devices with a narrow display (e.g. phones) when all cells are stacked up (responsive layout).

A default value can be set for all grids in the [Form Settings dialog](/form-builder/form-settings#form-options).

See also the [blog post](https://blog.orbeon.com/2024/05/grid-tab-order).

## See also

* [Section and grid settings](/form-builder/form-editor/container-settings)
* [Section settings](/form-builder/form-editor/section-settings)
* [Repeat settings](/form-builder/form-editor/repeat-settings)
* [Repeated grids](/form-builder/form-editor/repeated-grids)
* [Formulas](/form-builder/formulas)
* [Section component](/form-runner/component/section)
* [Template syntax](/form-builder/advanced/template-syntax)
* [Wizard view](/form-runner/features/wizard-view)
* Blog post: [Enhancements to grids](https://blog.orbeon.com/2021/09/enhancements-to-repeated-grids.html)
* Blog post: [Grid Tab Order](https://blog.orbeon.com/2024/05/grid-tab-order)


# Quick control search

## Availability

\[SINCE Orbeon Forms 2021.1]

## Rationale

In large forms, it is often difficult to navigate to a specific form control, especially when sections are closed.

The Quick control search provides a quick way to find a control.

## Usage

You use one of the following keyboard shortcuts:

* `⌘J` (macOS)
* `⌃J` (other operating systems)

The shortcut opens a small dialog. Clicking or pressing the Enter key and then typing a search term allows you to search from the list of available form controls. Upon selection, Form Builder scrolls to the control selected and highlights it.

If the "Open Settings Dialog" option is selected, the "Control Settings" dialog for the control also opens right away.

![The quick search dialog](/files/6c07o3Jiag3DfCVwSDfU)

## See also

* Blog post: [Quickly navigate to fields in large forms](https://blog.orbeon.com/2022/06/navigate-to-fields-in-large-forms.html)
* [Keyboard shortcuts](/form-builder/form-editor/keyboard-shortcuts)


# Repeat settings

## Overview

Form Builder supports grids and sections with repeated content.

* With grids, you can repeat a single row (which is the most common case), or multiple heterogeneous rows (with different controls).
* With sections, you can repeat the entire content of the section a number of times.

Both repeated grids and repeated sections have *repeat settings* in the "Section/Grid Settings" dialog's "Repeated Content" tab.

![Repeated Content](/files/-MkEnf0TKF8U-ED4hP6V)

## Repeat Content

Select this checkbox to enable repeated content. This enables a series of detailed options below in the same dialog.

## Allow the user to add, remove, or move repetitions

\[SINCE Orbeon Forms 2019.1]

By default, the form user is allowed to add, remove, or move repetitions. To disable this behavior, deselect the "Allow the user to add, remove, or move repetitions" option. When the checkbox is deselected, the user does not have direct access to buttons, icons or menus to change repetitions.

![Allow the user to add, remove, or move repetitions](/files/-MkEnf0VnzQHetlMu-4k)

When this option is deselected, the minimum, maximum and frozen repetitions settings do not apply.

Deselecting this option makes sense in particular when the number of repetitions:

* is defined at design-time,
* is controlled via [actions](/form-builder/advanced/services-and-actions/actions-syntax),
* or is controlled via [synchronization](/form-builder/advanced/services-and-actions/synchronize-repeated-content).

## Show repetition number

\[SINCE Orbeon Forms 2021.1]

For grids only, selecting this option automatically shows a row number at the beginning of each grid repetition.

![Show repetition number](/files/-MkEnf0Xw86Eirxvm5xG)

This is how the repetition number appears at runtime.

![Repetition number at runtime](/files/-MkEq6OTPluvWBroPUZI)

## Minimum and maximum number of repetitions

These settings can be predefined numbers or formulas when selecting "Other". Here is an example of using a firmula for the maximum number of repetitions.

![Formula for the maximum number of repetitions](/files/-MkEnf0YPulQI83p-Jvi)

## Freeze repetitions

\[SINCE Orbeon Forms 2018.2]

This setting can be a predefined number or a formula.

This allows *freezing* the first *N* iterations of a repeated grid or repeated section. Frozen iterations cannot be removed or moved by the user. The grid menus and buttons reflect that those operations are not possible.

The number of frozen iterations must be at most the minimal number of repetitions. If that's not the case, the number of frozen iterations will be reset to the minimal number of repetitions.

See also [Freezing rows in repeated grids or sections](https://blog.orbeon.com/2019/06/freezing-rows-in-repeated-grids-or.html).

## Simplified appearance

\[SINCE Orbeon Forms 2019.1]

When selected, this enables a simplified appearance for a repeated grid or section.

The default is the "full" appearance, which features:

* visible borders
* menus for inserting, removing, and moving rows

When enabled, the simplified (or "minimal") appearance features:

* no visible borders
* no ability to insert a new row at a specific position
* no ability to reorder rows
* simple buttons/icons to add/remove rows

Before Orbeon Forms 2019.1, the simplified appearance was available but only controllable via a property. See [Appearance of repeated grids](/configuration/properties/form-runner#appearance-of-repeated-grids) and [Appearance of repeated sections](/configuration/properties/form-runner#appearance-of-repeated-sections).

## Initial value formulas

\[SINCE Orbeon Forms 2016.1]

The "Apply initial value formulas when adding iterations" option specifies whether the "Initial Value" formulas for controls within the grid are evaluated for new iterations.

With the option enabled, new iterations can have dynamic initial values:

![Initial Values](/files/-LEkC44pn_QEqocCsQAR)

With Orbeon Forms 2016.1, the option is enabled by default for new forms and new repeated grids. The option is disabled by default for grids created with previous versions of Orbeon Forms.

## Initial number of iterations uses template

\[SINCE Orbeon Forms 2016.1]

The "Initial Number of Iterations Uses Template" option specifies, when an *enclosing repeated section* creates a new iteration, how many iterations this repeated grid will contain:

* when enabled: the number of iterations shown in Form Builder (which can be no iterations at all, one iteration, two iterations, etc.)
* when disabled: exactly one iteration

The following screenshot shows a case with a repeated grid within nested repeated sections. At first, when the form shows, there are two iterations of the repeated grid.

![](/files/-LEkC44rfyj3jsSLyNVT)

With the option enabled on the grid, adding a new iteration of *Repeated section 2* causes the new iteration to contain a new repeated grid with two iterations:

![](/files/-LEkC44tJLL2Lndoq6Es)

While, with the option disabled on the grid, adding a new iteration of *Repeated section 2* causes the new iteration to contain a new repeated grid a single iterations:

![](/files/-LEkC44v63zQxNjAJWeQ)

## Show one repetition at a time in the wizard

\[SINCE Orbeon Forms 2019.1]

This option can be selected only when the "Allow the user to add, remove, or move repetitions" option is deselected.

For details, see [Paging large repeated sections](/form-runner/features/wizard-view#paging-large-repeated-sections).

## Visibility and Read-Only formulas

[\[SINCE Orbeon Forms 2024.1\]](/release-notes/orbeon-forms-2024.1)

* **Visibility:** Specifies whether the repetition is visible. This can be either "Yes" (default), "No", or an XPath formula, in which case the repetition is visible only if the formula evaluates to `true()`.
* **Read-Only:** Specifies whether the repetition is read-only (not editable). This can be either "Yes", "No" (default), or an XPath formula, in which case the repetition is editable only if the formula evaluates to `false()`.

For a repetition to be visible or editable, all enclosing sections/grids must be visible or editable as well.

XPath expressions are described in more details in [Formulas](/form-builder/formulas).

## See also

* [Repeated grids](/form-builder/form-editor/repeated-grids)
* [Section settings](/form-builder/form-editor/section-settings)
* [Grid component](/form-runner/component/grid)
* [Section component](/form-runner/component/section)
* [Paging large repeated sections](/form-runner/features/wizard-view#paging-large-repeated-sections)
* Support for repeats lands in Form Builder: [older blog post](https://blog.orbeon.com/2012/04/support-for-repeats-lands-in-form.html)
* Inserting and reordering grid rows: [blog post](https://blog.orbeon.com/2013/11/inserting-and-reordering-grid-rows.html)
* Repeated sections: [blog post](https://blog.orbeon.com/2014/01/repeated-sections.html)
* Repeated grids and sections just got more subtle: [blog post](https://blog.orbeon.com/2015/10/repeated-grids-and-sections-just-got.html)
* Freezing rows in repeated grids or sections: [blog post](https://blog.orbeon.com/2019/06/freezing-rows-in-repeated-grids-or.html)


# Repeated grids

## Creating a repeated grid

You insert a new repeated grid with the "New Repeated Grid" toolbox button.

Once the grid is inserted, you can add and remove (using the grid arrow and trash icons which appear on mouseover) columns and rows, and add controls to grid cells as you would in a regular non-repeated grid.

With only one row, the control labels are used as column headers and are not repeated within the grid. If present, control hints are also added to the column headers. That single row is repeatable with the "plus" icon.

![Repeating a single row](/files/-LEkC44IFCSIsQiBbifV)

You can add multiple heterogeneous rows with the arrow icons. In this case the entire group of rows is repeatable. Control labels and hints do not appear in column headers, but appear alongside the controls in the grid.

![Repeating multiple rows](/files/-LEkC44NNsucjF2bogFT)

Whether there is a single or multiple repeatable rows, you can add and remove repetitions (iterations) of those rows with the "plus" icon. You typically let the user add iterations at runtime, but it is possible to create iterations in advance at design time as well.

## How things look at runtime

At runtime, notice how in the first grid a single row is repeated, and in the second grid the two rows are repeated.

![Repeated grids](/files/-LEkC44R0_pMGvDuWGuf)

In review mode and PDF mode, icons and menus disappear and the grid appears entirely readonly.

![Repeated grids in review mode](/files/-LEkC44TljULuRoMgXgm)

![Repeated grids in PDF mode](/files/-LEkC44VTchgFp9lz443)

## Grid settings

Once a grid is inserted, you can edit its properties with the "Grid Settings" icon.

![Grid Settings](/files/-LEkC44Xz_uPl5TSk68d)

*NOTE: Since Orbeon Forms 4.8, these settings (correctly) apply to the entire grid. Previously, the grid's repeat headers did not hide properly for example when the grid was hidden. See issue* [*#635*](https://github.com/orbeon/orbeon-forms/issues/635)*.*

![Basic Settings and Formulas](/files/-M-GkArfAYWs6Y5rWxSx)

\[SINCE Orbeon Forms 2019.1]

These settings also apply to non-repeated grids.

### Repeat settings

See [Repeat settings](/form-builder/form-editor/repeat-settings).

## See also

* [Repeat settings](/form-builder/form-editor/repeat-settings)
* [Section settings](/form-builder/form-editor/section-settings)
* [Formulas](/form-builder/formulas)
* [Grid component](/form-runner/component/grid)


# Undo and redo

## Availability

\[SINCE Orbeon Forms 2017.2]

## Undo and redo icons

The toolbox shows familiar undo and redo icons.

![Undo and redo icons](/files/-LEkBzFc-dec6GwBva7_)

## Using undo and redo

When hovering over the icons, a tooltip tells you which operation is undone or redone.

You can undo most operations which change important aspects of the form:

## Keyboard shortcuts

\[SINCE Orbeon Forms 2020.1]

You can use keyboard shortcuts to undo and redo operations.

See [Keyboard shortcuts](/form-builder/form-editor/keyboard-shortcuts#undo-and-redo) for details.

## Supported undo/redo actions

* insertion and deletion
  * control, grid, grid row, section and section template
* moving
  * control (via drag and drop)
  * section (up/down/right/left arrows)
* settings
  * rename control, grid or section
  * other control, grid or section settings, including itemsets, labels, hint, and validations
* section template merging (new in 2017.2)

## Limitations

In this initial version, it is not yet possible to undo changes to the form definition's source code with Edit Source, or changes to actions, services, and other global form settings. We hope to support undoing these operations in the future.

## See also

* Blog post: [New Orbeon Forms 2017.2 feature: undo and redo](https://blog.orbeon.com/2017/12/new-orbeon-forms-20172-feature-undo-and.html)


# Form Builder keyboard shortcuts

## Keyboard Shortcuts dialog

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1)

Form Builder includes a dedicated Keyboard Shortcuts dialog that lists all available shortcuts, allowing you to discover them without leaving the app. You can display this dialog in two ways:

* By pressing <kbd>⇧?</kbd> (`shift` + `?`).
* In the Form Builder toolbox's Advanced tab, click the "Keyboard Shortcuts" button.

![Form Builder Keyboard Shortcuts](/files/YT6bj8OHlGlghBPQnAnG)

## List of shortcuts

| Area                      | Key         | Description                                                                     | Since                                            |
| ------------------------- | ----------- | ------------------------------------------------------------------------------- | ------------------------------------------------ |
| Button Shortcuts          | `⌘S`/`⌃S`   | Save the form definition                                                        | [2020.1](/release-notes/orbeon-forms-2020.1)     |
|                           | `⌘P`/`⌃P`   | Open the [Publish dialog](/form-builder/form-editor/publishing)                 | [2020.1](/release-notes/orbeon-forms-2020.1)     |
| Cut, Copy and Paste       | `⌘X`/`⌃X`   | Cut the current control                                                         | [2020.1](/release-notes/orbeon-forms-2020.1)     |
|                           | `⌘C`/`⌃C`   | Copy the current control                                                        | [2020.1](/release-notes/orbeon-forms-2020.1)     |
|                           | `⌘V`/`⌃V`   | Paste from the toolbox                                                          | [2020.1](/release-notes/orbeon-forms-2020.1)     |
| Grid Navigation Shortcuts | `←`/`→`     | Move to the previous or next grid cell, including empty cells.                  | [2020.1](/release-notes/orbeon-forms-2020.1)     |
|                           | `⇧←`/`⇧→`   | Move to the previous or next grid cell, skipping empty cells.                   | [2020.1](/release-notes/orbeon-forms-2020.1)     |
|                           | `↑`/`↓`     | Move to the previous or next grid cell vertically, including empty cells.       | [2024.1.1](/release-notes/orbeon-forms-2024.1.1) |
|                           | `⌃⇧↑`       | Move the current grid line up.                                                  | [2024.1.3](/release-notes/orbeon-forms-2024.1.3) |
|                           | `⌃⇧↓`       | Move the current grid line down.                                                | [2024.1.3](/release-notes/orbeon-forms-2024.1.3) |
| Undo and Redo             | `⌘Z`/`⌃Z`   | Undo the last operation                                                         | [2020.1](/release-notes/orbeon-forms-2020.1)     |
|                           | `⌘⇧Z`/`⌃Y`  | Redo the last operation                                                         | [2020.1](/release-notes/orbeon-forms-2020.1)     |
| All dialogs               | `⇧↵`        | Open the [Control Settings dialog](/form-builder/form-editor/control-settings)  | [2020.1](/release-notes/orbeon-forms-2020.1)     |
|                           | `⌘↵`/`⌃↵`   | Apply/OK dialog (clicks primary button)                                         | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `⌘J`/`⌃J`   | Open the [Quick control search](/form-builder/form-editor/quick-control-search) | [2021.1](/release-notes/orbeon-forms-2021.1)     |
|                           | `O F`       | Open [Form Settings](/form-builder/form-settings)                               | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
|                           | `O P`       | Open [Permissions](/form-runner/access-control/deployed-forms)                  | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
|                           | `O E`       | Open [Email Settings](/form-builder/advanced/email-settings)                    | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
|                           | `O M`       | Open [Messages](/form-builder/advanced/messages)                                | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
|                           | `O S`       | Open [Edit Source](/form-builder/advanced/edit-source)                          | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
|                           | `T W`       | Test the web form                                                               | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
|                           | `T P`       | [Test PDF production](/form-builder/advanced/pdf-test)                          | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
|                           | `T O`       | [Test the offline form](/form-builder/advanced/offline-test)                    | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
|                           | `T F`       | [Inspect formulas](/form-builder/formulas/formulas-inspector)                   | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
| Control Settings dialog   | `⌃[`        | Go to previous control                                                          | [2024.1.3](/release-notes/orbeon-forms-2024.1.3) |
|                           | `⌃]`        | Go to next control                                                              | [2024.1.3](/release-notes/orbeon-forms-2024.1.3) |
| Tab Navigation            | `⌃}`        | Move to next tab                                                                | [2024.1.2](/release-notes/orbeon-forms-2024.1.2) |
|                           | `⌃{`        | Move to previous tab                                                            | [2024.1.2](/release-notes/orbeon-forms-2024.1.2) |
| Inserting Form Structure  | `I F S`     | [Insert a new section](/form-builder/form-editor/toolbox)                       | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
|                           | `I F G`     | [Insert a new grid](/form-builder/form-editor/toolbox)                          | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
|                           | `I F R`     | [Insert a new repeated grid](/form-builder/form-editor/toolbox)                 | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |
| Inserting Form Controls   | `I C I`     | Insert Text Field                                                               | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I C T`     | Insert Text Area                                                                | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I C F`     | Insert Formatted Text Area                                                      | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I U E`     | Insert Explanatory Text                                                         | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I U C`     | Insert Calculated Value                                                         | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I U H`     | Insert Hidden Field                                                             | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I T N`     | Insert Number                                                                   | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I T E`     | Insert Email Field                                                              | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I T D`     | Insert Date Field                                                               | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I T T`     | Insert Time Field                                                               | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I S D`     | Insert Dropdown                                                                 | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I S R`     | Insert Radio Buttons                                                            | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I S C`     | Insert Checkboxes                                                               | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I A F`     | Insert Attachment                                                               | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I A I`     | Insert Image Attachment                                                         | [2024.1](/release-notes/orbeon-forms-2024.1)     |
|                           | `I A V`     | Insert Video Attachment                                                         | [2024.1](/release-notes/orbeon-forms-2024.1)     |
| Reloading the Toolbox     | `⌘⇧R`/`⌃⇧R` | [Reload the toolbox](/form-builder/form-editor/toolbox#reloading-the-toolbox)   | [2023.1.3](/release-notes/orbeon-forms-2023.1.3) |

## Discoverability

[\[SINCE Orbeon Forms 2023.1.3\]](/release-notes/orbeon-forms-2023.1.3) Most keyboard shortcut hints now show when you hover over the Form Builder toolbox, the buttons bar, or icons. [\[SINCE Orbeon Forms 2023.1.6\]](/release-notes/orbeon-forms-2023.1.6) If you want to disable hover hints because you find them distracting or for any other reason, set the following property to `false`. Its default value is `true`, meaning hints are shown by default for all forms.

```xml
<property
    as="xs:boolean"
    name="oxf.fr.keyboard-shortcuts.show-hints.*.*"
    value="false"/>
```

## See also

* Blog post: [Keyboard Shortcut Improvements in Form Builder](https://www.orbeon.com/2025/10/keyboard-shortcut-improvements)
* Blog post: [Improved Keyboard Shortcuts](https://www.orbeon.com/2024/07/keyboard-shortcuts)
* Blog post: [Adding keyboard shortcuts to Form Builder](https://www.orbeon.com/2021/01/adding-keyboard-shortcuts-to-form.html)


# Formulas

## Formulas and XPath

In Orbeon Forms, formulas are expressed using *XPath*, a standard *expression language* for XML. It does not allow you to *modify* XML data, but it allows you to *query* XML data and compute values.

For those familiar with Microsoft Excel or other spreadsheet software, you can think of formulas with Orbeon Forms as what follows the "=" sign in a spreadsheet. It is similar to that, but with a slightly different syntax and set of rules.

In general, you don't need to know about XPath in Form Builder, with the exception of some properties in the Control Settings and Section Settings dialogs. Formulas are considered an advanced feature of Form Builder, which might require some programming knowledge.

*NOTE: Incorrect XPath expressions may cause the form to behave improperly, so caution must be applied.*

## Examples of formulas

See [Examples of formulas](/form-builder/formulas/formulas-examples).

## Referring to control values from formulas

### Basic usage

To refer to a form control value from a formula, use the variable notation `$foo` where `foo` is the control name. For example:

```xpath
$price * $quantity
```

This assumes a form control named "price" and another form control named "quantity".

### Variables in depth

In a formula, when referring to `$price`, the result is actually an XML node, as this is how Orbeon Forms stores data internally. Think of a node as a container for a value:

```xml
<price>4.99</price>
```

There are two ways to obtain a value from this node:

* using the `string()` function
  * this returns, of course, a value of type `string`
  * example: `$price/string()` or `string($price)`
* using the `data()` function
  * this returns a value of the type associated with the associated form control
  * example: `$price/data(.)` or `data($price)`

Now in many cases, XPath gets the value for you without calling `string()` or `data()`. For example when you perform a multiplication:

```xpath
$price * $quantity
```

Here, XPath "sees" that the multiplication cannot apply to nodes, and automatically fetches ("atomizes") the value from the nodes. In this case, if the associated form control has type "Decimal", then the values will be retrieved from the nodes and converted to decimal numbers as if you had used the `data()` function explicitly:

```xpath
data($price) * data($quantity)
```

Note that the multiplication will fail if the form controls do not contain valid decimal numbers.

The `data()` function can be a little tricky because of this. Now say you'd like instead to *concatenate* the text of those nodes containing decimal values even if the don't contain valid decimal values. Then you have to explicitly use the `string()` function:

```xpath
concat('Price: ', string($price), 'Quantity: ', string($quantity))
```

### Where you can use variables

You can use the variable notation in the following formulas in the Control Settings dialog:

* Required
* Validation
* Calculated Value
* Initial Value
* Visibility
* Read-Only

\[SINCE Orbeon Forms 2022.1]

You can also use the variable notation in the following formulas:

* Repeated grids and sections:
  * Minimum Number of Repetitions
  * Maximum Number of Repetitions
  * Freeze Repetitions
* Number and Currency fields
  * Prefix
  * Suffix
* Dynamic Dropdown:
  * Resource URL
  * Choices formula
  * Label formula
  * Value formula
  * Hint formula
* Actions
  * [Simple Actions](broken://pages/-LEkBr90Nq9hbV8krZWy)
  * [Action Syntax](broken://pages/-LTfNL822m6v3asjBCuO)
* [Template parameters](/form-builder/advanced/template-syntax)
  * Control name
  * Formula

See also the blog post [Improvements to variables in formulas](https://blog.orbeon.com/2022/04/improvements-to-variables-in-formulas.html).

### Resolution of repeated controls

When referring to controls that are repeated, for example within repeated grids or repeated sections, a variable can return a sequence of multiple values. Each value corresponds to a repeated control.

However, not all controls in the form are necessarily selected. Instead, the "closest" controls are selected, as follows:

* The closest enclosing repeat iteration between the location of the formula and the control identified by the variable is identified. If there is none, then this is the top-level of the form.
* Then all the controls within that iteration, or within the entire form when there is no such iteration, are selected.

For example, consider

* a repeated grid
* a decimal field called `price` on each row
* an integer field called `quantity` on each row
* a decimal text output field called `row-total` on each row
* a decimal text output field called `total` below the grid

Calculated value expression for `row-total`:

```xpath
$price * $quantity
```

The `row-total` calculated value applies to the closest `price` and `quantity` controls, that is, those on the same row, and each row gets its own row total.

Calculated value expression for `total`:

```xpath
sum($row-total[string() castable as xs:decimal], 0.0)
```

On the other hand, the `total` calculation is outside the repeat, and when it refers to `$row-total`, all `row-total` values are returned. Therefore, the sum applies to all the `row-total` values (assuming they can be cast as `xs:decimal`, in this example).

TODO: Is this the same as using `fr:control-string-value('$price', false())`, or is there a subtle difference?

## Renaming of controls and formulas

\[SINCE Orbeon Forms 2019.1]

When a control or section or grid is renamed, dependent formulas which use the variable notation `$foo` (where `foo` is the control name) are automatically updated.

\[SINCE Orbeon Forms 2022.1]

In addition, other references which use the variable notation `$foo` where `foo` is the control name, are automatically updated, including:

* Repeated grids and sections:
  * Minimum Number of Repetitions
  * Maximum Number of Repetitions
  * Freeze Repetitions
* Number and Currency fields
  * Prefix
  * Suffix
* Dynamic Dropdown:
  * Resource URL
  * Choices formula
  * Label formula
  * Value formula
  * Hint formula
* Actions
  * [Simple Actions](broken://pages/-LEkBr90Nq9hbV8krZWy)
  * [Action Syntax](broken://pages/-LTfNL822m6v3asjBCuO)
* [Template parameters](/form-builder/advanced/template-syntax)
  * Control name
  * Formula

## Where do formulas appear?

### Control Settings dialog

In the Control Settings dialog, formulas are used to specify the following aspects of a control.

#### Validations and alerts

![Validations](/files/-LEkBwwzQBuFcIscjgrv)

* **Constraint:** Boolean expression specifying whether the control is valid.
  * If this field is left blank, then the validity of the control depends on the data type and the "Required" option.
  * Otherwise, the control is valid if in addition to all the other constraint being met, the result of the Boolean expression is `true()`.

#### Formulas

![Formulas](/files/-LEkBwx0uppKKLVgHDn6)

* **Visibility:**
  * Specifies whether the control is visible. This can be either "Yes" (default), "No", or an XPath formula.
  * If a formula is specified, the control is visible only if the formula evaluates to `true()`.
* **Read-Only:**
  * Specifies whether the control is read-only (not editable). This can be either "Yes", "No" (default), or an XPath formula.
  * If a formula is specified, the control is editable only if the formula evaluates to `false()`.
* **Initial value:**
  * String expression returning the initial value of the control when the form first shows. This is only applied in `new` mode.
  * Default: the value set into the field at design time (usually a blank value).
* **Calculated Value:**
  * String expression specifying a calculated value of the control which updates while the form user interacts with the form.
  * Default: the default value of the control, or the value entered by the form user.

### Section/Grid Settings dialog

![Basic Settings and Formulas](/files/-LEkC53L4t74PaFSKYeH)

In the Section/Grid Settings dialog, formulas are used to specify the following aspects of a section or grid:

* **Visibility:** Specifies whether the section or grid is visible. This can be either "Yes" (default), "No", or an XPath formula, in which case the section or grid is visible only if the formula evaluates to `true()`.
* **Read-Only:** Specifies whether the section or grid is read-only (not editable). This can be either "Yes", "No" (default), or an XPath formula, in which case the section or grid is editable only if the formula evaluates to `false()`.

\[SINCE Orbeon Forms 2020.1]

In the Form Settings dialog, an XPath expression can be used to specify whether the entire form is read-only (not editable).

### Actions

In the Section/Grid Settings dialog, formulas are used to specify the following aspects of a section or grid:

TODO

## Examples

See [examples of formulas](/form-builder/formulas/formulas-examples).

## See also

* [Examples of formulas](/form-builder/formulas/formulas-examples)
* [Form Builder Validation](/form-builder/form-editor/validation)
* [Formulas inspector](/form-builder/formulas/formulas-inspector)
* [When the internal data format matters](/form-runner/api/data-formats/form-data#when-the-internal-data-format-matters)
* Blog posts
  * [Better formulas with XPath type annotations](https://blog.orbeon.com/2013/01/better-formulas-with-xpath-type.html)
  * [Formulas for summing values, done right](https://blog.orbeon.com/2013/08/formulas-for-summing-values-done-right.html)
  * [Control required values with formulas in Orbeon Forms 4.7](https://blog.orbeon.com/2014/09/control-required-values-with-formulas.html)
  * [Improvements to variables in formulas](https://blog.orbeon.com/2022/04/improvements-to-variables-in-formulas.html)
* [XForms Validation](/xforms/core/validation)


# Examples of formulas

## Live examples

For live examples of [Formulas](/form-builder/formulas), see [Orbeon Demo: Examples of Formulas](https://demo.orbeon.com/demo/fr/orbeon-features/formulas/new?form-version=1).

![Orbeon Demo: Examples of Formulas](/files/DZjTar63sj3WiCVmSCEc)

## Sum of values in a repeat

\[SINCE Orbeon Forms 4.5]

Scenario: compute the sum of values in multiple repeat repetitions. Say you have:

* a repeated grid
* a decimal field called `price` on each row
* an integer field called `quantity` on each row
* a decimal text output field called `row-total` on each row
* a decimal text output field called `total` below the grid

You want to compute the row totals and athe general total.

Calculated value expression for `row-total`:

```xpath
$price * $quantity
```

Calculated value expression for `total`:

```xpath
sum($row-total[string() castable as xs:decimal], 0.0)
```

Explanation:

* when accessing control values with variables, the "closest" variables are found
* this means that the `row-total` control calculation applies to the closest `price` and `quantity` controls, that is, those on the same row, and each row gets its own total
* the `total` calculation is outside the repeat, and when it refers to `$row-total`, all `row-total` values are returned
* `sum()` is a standard XPath function to compute the sum of a sequence of items
* the predicate `[string() castable as xs:decimal]` excludes values that are blank or not a decimal number
* see this [blog post](https://blog.orbeon.com/2013/08/formulas-for-summing-values-done-right.html) for the use of `string()` within the predicate
* `sum()` supports a second argument which is the value to return in case no value satisfies the predicate (this makes sure that we return a decimal value, as we are using a literal decimal 0.0)

See also:

* [Resolution of repeated controls](/form-builder/formulas#resolution-of-repeated-controls)
* [Formulas for summing values, done right](https://blog.orbeon.com/2013/08/formulas-for-summing-values-done-right.html).
* [Unexpected result with variable inside an `<xf:bind>` iteration #152](https://github.com/orbeon/orbeon-forms/issues/152)

## Constrain a number between two values

Scenario: Make the current integer number field valid only if its value is between two values, say 12 and 17 included.

Expression:

```xpath
. >= 12 and . <= 17
```

Explanation:

* `.` refers to the current value of the control
* `>=` or `ge` means "greater than or equals to"
* `<=` or `le` means "less than or equals to"
* `and` is the logical "and" operator

If you want to refer to a specific control by name, you can use:

```xpath
$my-control >= 12 and $my-control <= 17
```

## Constrain the length of a string between two values

Scenario: Make the current field valid only if its length is between two values, say 2 and 140.

Expression:

```xpath
string-length(.) >= 2 and string-length(.) <= 140
```

Explanation:

* `.` refers to the current value of the control
* The standard `string-length()` function returns the length of its argument
* `>=` or `ge` means "greater than or equals to"
* `<=` or `le` means "less than or equals to"
* `and` is the logical "and" operator

If you want to refer to a specific control by name, you can use:

```xpath
string-length($message) >= 2 and string-length($message) <= 140
```

\[SINCE Orbeon Forms 4.10]

The same can be expressed, for the current control, as:

```xpath
xxf:min-length(2) and xxf:max-length(140)
```

## Validating with a regular expression for an optional value

Scenario: check that a given number value is either blank or has exactly 5 digits.

```xpath
matches(string(.), '\d{5}') or xxf:is-blank(string(.))
```

Explanation:

* the standard `matches()` function applies the regular expression passed as second argument to the first argument, and returns true if it does match
* the built-in `xxf:is-blank()` function returns `true()` if the value passed is blank
* since the data is a number, we use the `string(.)` function to convert the value to a string before passing it to functions

## Make a control read-only based on the value of another control

Scenario: Make a control read-only if the value of the `first-name` control is blank:

Expression:

```xpath
xxf:is-blank($first-name)
```

Explanation:

* `$first-name` returns the value of the control with name "first-name"
* the built-in `xxf:is-blank()` function returns `true()` if the value passed is blank

## Setting a dynamic initial value

Scenario: As a form author, you can set a *static initial value* for a control simply by setting that value at design time. For example:

* Enter a value in an input field
* Select an item in a dropdown list

But not all initial values can be static. For example, you might want a date selection control to contain the current date until the user changes it. In this case, you can use an "Initial Value" expression.

Initial Value expression:

```xpath
current-date()
```

Explanation:

* `current-date()` is a standard XPath function returning the current date.

## Simple calculated values

Scenario: compute the sum of two numbers entered by the user in two fields, "quantity1" and "quantity2".

Calculated Value expression:

```xpath
if ($quantity1 castable as xs:integer and $quantity2 castable as xs:integer)
then $quantity1 + $quantity2
else ''
```

Explanation:

* `if (...) then ... else ...` evaluates a condition and then returns one of two alternatives
* the condition `quantity1 castable as xs:integer` checks that the value from the field "quantity1" is an integer
* `quantity1 + $quantity2` simply adds the two values
* the value `''` represents an empty string This can be specified for example on a Text Output control.

*NOTE: If the value of a control is calculated, by default it is also marked as read-only. If you want a calculated control to be still editable by the user, set its Read-Only property explicitly to `false()`.*

## Access a control in a particular repeat iteration

Given [this form](https://gist.github.com/orbeon/e7272c1b2499c3a5fb5f) and a control called `name` within a repeat:

* `$name[2]`: return the value of the control in the second iteration
* `string-join($name, ', ')`: join all values with commas
* `count($name)`: return the number of values

*NOTE: This works when the expression is outside repeat repetitions. For expressions within the same repeat, `$name` returns the closest control.*

See also [Model bind variables](/xforms/core/model-bind-variables) and this [StackOverflow question](http://stackoverflow.com/questions/27820641/access-to-iterated-controls-in-repeated-sections-in-orbeon/27830585?noredirect=1#comment44118606_27830585).

## Check the role(s) of the current user

See [Form Fields](/form-runner/access-control/form-fields).

## Check the Form Runner mode

### Modes

The Form Runner Detail page can have the following modes:

* `new`
* `edit`
* `view`
* `pdf`
* `email`
* `test` (when you are testing a form within Form Builder)

### With Orbeon Forms 2016.2 and newer

You can use the [`fr:mode()` XPath function](/xforms/xpath/extension-functions/extension-form-runner#frmode) exposed by Form Runner to all XPath expressions.

```xpath
fr:mode() = 'edit'
```

### With Orbeon Forms 2016.1.x and earlier

A special XPath variable named `$fr-mode` is exposed by Form Runner to all XPath expressions.

You can test the mode as follows, for example in a Visibility expression:

```xpath
$fr-mode = 'edit'
```

## Access HTTP request parameters and HTTP headers

It can be useful to access HTTP headers to set default values upon form initialization, for example when single sign-on systems use HTTP headers as a way of communicating information to an application.

XPath expressions have access to a special function, `xxf:request-header()`, which allows retrieving a header by name. Example of setting the default value of a field using an initial value:

```xpath
xxf:get-request-header('full-name')
```

*NOTE: With Orbeon Forms 3.8 and 3.9, headers cannot be reliably accessed after the form is initialized, so this function should be used for setting initial values on controls only. See the next scenario for a workaround.*

## Check the type of an attachment

Scenario: field `my-attachment` must be a PDF file.

Constraint expression:

```xpath
ends-with(lower-case($my-attachment/@filename), '.pdf')
```

Explanation:

* Form Runner stores information about a file into XML attributes:
  * `@filename` accesses the file name as sent by the user's browser
  * `@mediatype` accesses the file type as sent by the user's browser
  * `@size` accesses the file size
* `$my-attachment/@filename` returns the file name associated with attachment "my-attachment"
* The `lower-case()` function converts that name to a lower case value
* The `ends-with()` function checks whether its first argument ends with the second argument

Similarly, you can test the file type:

```xpath
$my-attachment/@mediatype = 'application/pdf'
```

*NOTE: Because the file name and file type are sent by the client's browser, they cannot be trusted. This should only be considered a first level of data validation, and further validation based on the content must be performed at a later time if needed. See also issue* [*#1838*](https://github.com/orbeon/orbeon-forms/issues/1838)*.*

## Set the value of a field from a URL parameter

Scenario: We want to set the value of a field from a URL parameter, but only if that parameter exists. If it doesn't, we want to leave the value of the field as it is.

```xpath
(xxf:get-request-parameter('my-parameter'), .)[1]
```

Explanation:

* If the parameter exists
  * `xxf:get-request-parameter()` returns a single string
  * so you get a sequence containing that string following by the current value of the field
  * we take the first value of the sequence, so the value of the parameter is used
* if the parameter doesn't exist
  * `xxf:get-request-parameter()` returns an empty XPath sequence
  * so you get a sequence containing only the current value of the field
  * we take the first value of that sequence, so we get the current value of the control

## Converting a date/time to a named timezone

The following formula formats the current date/time to an ISO date/time in a specific named timezone:

```xpath
format-dateTime(
  current-dateTime(),
  '[Y0001]-[M01]-[D01]T[H01]:[m01]:[s01]',
  'en',
  (),
  'Europe/Paris'
) 
```

*NOTE: See* [*issue #4981*](https://github.com/orbeon/orbeon-forms/issues/4981) *for availability of a fix that impacts this example.*

## See also

* [Formulas](/form-builder/formulas)
* [Form Builder Validation](/form-builder/form-editor/validation)
* [Formulas inspector](/form-builder/formulas/formulas-inspector)
* [When the internal data format matters](/form-runner/api/data-formats/form-data#when-the-internal-data-format-matters)
* Blog posts
  * [Better formulas with XPath type annotations](https://blog.orbeon.com/2013/01/better-formulas-with-xpath-type.html)
  * [Formulas for summing values, done right](https://blog.orbeon.com/2013/08/formulas-for-summing-values-done-right.html)
  * [Control required values with formulas in Orbeon Forms 4.7](https://blog.orbeon.com/2014/09/control-required-values-with-formulas.html)
  * [Improvements to variables in formulas](https://blog.orbeon.com/2022/04/improvements-to-variables-in-formulas.html)
* [XForms Validation](/xforms/core/validation)


# Formulas inspector

## Introduction

With Orbeon Forms, [formulas](/form-builder/formulas) are very important: they are used for calculating values, making parts of the form visible or readonly, and more. However, they can be difficult to debug, and so far Form Builder didn't have a way to show all formulas in a central location.

This new, still experimental feature allows you to inspect formulas. You access it under the "Test" menu.

![The "Inspect Formulas" button](/files/SRpozOYua3fRRi3PoIW9)

## Availability

\[SINCE Orbeon Forms 2021.1]

This feature considered experimental with Orbeon Forms 2021.1, but only because it is still fairly basic! However, it is still useful, and we hope to improve it in newer versions of Orbeon Forms.

## Usage

The formulas inspector allows you to see, in a table, the following formulas used in the form for:

* Initial Value
* Calculated Value
* Visibility
* Required
* Read-Only

You select the category of formulas from the list. Then the results table shows, in order, all the formulas of that category associated with form controls, as well as the controls they depend on or influence (see below).

The following example show dependencies between "Calculated Value" formulas and controls. A color scheme indicates the dependency relationships between controls via formulas.

![Example showing "Calculated Value" dependencies](/files/jEFVZ4op8EoGu0syam3F)

## Results table

### Basics

Each row shows a form control. The columns are as follows:

1. Position of the result in the list of results
2. Control name as defined in Form Builder
3. Formula
4. Control dependencies (see below)
5. Control datatype as defined in Form Builder
6. Control value if present
7. Path dependencies (see below)

There are several benefits to seeing all the formulas in this overview:

* You can see how many formulas you have in your form.
* You can quickly check them for correctness.
* You can see if the values they use have the correct datatype.

And so on!

### Control dependencies

A formula can [refer to control values](/form-builder/formulas#referring-to-control-values-from-formulas) using the variable notation `$foo` where `foo` is the control name. A formula that refers to other controls with this notation lists these dependencies in the *Control dependencies* column.

In addition, when selecting a given row in the results table, the row highlights, and up to four other different colors are used to highlight variable dependencies:

* **Direct dependency:** controls that the current formula directly depends on
* **Transitive dependency:** controls that the current formula indirectly depends on
* **Direct influence:** controls that are directly influenced by the current formula
* **Transitive influence:** controls that are indirectly influenced by the current formula

When a given row is selected, checking the "Show related rows only" checkbox allows focusing the view on only the controls that depend on or are influenced by the selected row.

Note that when you rename a control in Form Builder, all dependent formulas are automatically updated. See [Renaming of controls and formulas](/form-builder/formulas#renaming-of-controls-and-formulas).

### Path dependencies

In addition to *control dependencies*, a formula produces so-called *path dependencies*. These dependencies represent computational dependencies on the data. These are used to improve the performance of the evaluation of the formula.

* **Green circle:** indicates that path dependencies were successfully established for the formula. The dependent paths show below.
* **Red circle:** indicates that path dependencies were not established for the formula.

If too many formulas, especially in a large form, cannot establish path dependencies, performance of the form can be decreased. The "Path dependencies" column can help indicate whether this is a problem.

## See also

* [Formulas console](/form-builder/formulas/formulas-console)
* [Examples of formulas](/form-builder/formulas/formulas-examples)
* [Formulas](/form-builder/formulas)


# Formulas console

## Availability

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

## Introduction

The Formulas console allows you to view formulas errors that occur when testing a form.

![Formulas console](/files/6o350khPTWLcrRlqDEtM)

Some errors are detected by Orbeon Forms statically, when compiling the form; other errors are detected dynamically, when the form runs and there is user interaction. Both types of errors are shown in the Formulas console.

Each entry shows:

* some context for the error
* the control name, if applicable
* the formula
* the error message
* the count of the number of times the error occurred

The console automatically opens when the first error occurs. You can close and open the console with the icon in the top right of the console area.

## See also

* [Formulas inspector](/form-builder/formulas/formulas-inspector)
* [Examples of formulas](/form-builder/formulas/formulas-examples)
* [Formulas](/form-builder/formulas)
* [Testing a form in web mode](/form-builder/advanced/web-test)


# Summary page

## Purpose

The Form Builder Summary page is your starting point when you work with Form Builder. It allows:

* Listing and searching form definitions
* Creating new form definitions
* Editing existing form definitions
* Reviewing form definitions in read-only mode
* Deleting form definitions

*NOTE: Form definitions are usually stored in a database on the server. You do not usually keep them on your own computer.*

## Accessing the Summary page

You access the Summary page from your web browser through a URL (or web address) of the form:

```
http://[SERVER NAME]/orbeon/fr/orbeon/builder/summary
```

*NOTE: The exact URL might be provided by your IT organization.*

Once you reach the Summary page, you will either see an empty list, or a list containing existing form definitions, as shown in this screenshot.

![The Summary page](/files/-LEkC028ItoCLFeR89a-)

For each form definition, the list shows:

* Creation date
* Last modification date
* Application name
* Form name
* Form title
* Form description if any

If the list doesn't fit entirely on one page, you can navigate to further pages of results using the navigation arrows at the bottom of the results list.

If the list is too wide, a horizontal scrollbar appears at the bottom of the list and allows you to navigate horizontally.

By default, the Summary page lists all the form definitions which you have access to.

*NOTE: Depending on your permissions, the Application Name field and/or Form Name field might be input fields or dropdown menus that restrict your choices.*

### Searching and navigating form definitions

The top of the Summary page features search fields. There are two types of searches:

* **Free text search.** This searches any text within the form definition.
* **Structured search.** This searches on Application Name, Form Name, Form Title, or Form Description.

To search: enter a search term and press the "Search" button or the "enter" key.

Tip: to clear the search and list all the form definitions again, clear all search fields and press the "Search" button or the "enter" key.

![](/files/-LEkC02EWQrIT0HKW0hA)

## Creating a new form definition

To create a new form definition, press the "New" button at the bottom of the page. This opens the Form Builder editor in a separate browser window or tab.

## Reviewing a form definition

[\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1) Select the form definition you want to inspect, then press the "Review" button. Form Builder opens the form in *view mode*, which shows the complete designer interface while disabling actions that would modify the form, such as dragging controls, saving, or publishing. This is useful when you want to share the layout of a form for feedback without risking accidental edits.

## Deleting a form definition

Using the checkboxes that appear on each row, select the form definitions you wish to delete, then press the "Delete" button.

## Permissions

The Summary page follows the [access control rules for deployed forms](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/form-runner/access-control/deployed-forms.md). This means that a user accessing the Summary page will only see the data to which she has access to.


# Form localization

## Availability

This is an [Orbeon Forms PE](https://www.orbeon.com/download) feature.

## Localizing your forms

Form Builder has localization support. This means that your form's titles, labels, help messages, etc. can be specified in multiple languages. At runtime, the form user is presented with a default language and can switch the most appropriate language.

By default, only one language is present, typically English. The default language is configurable by the Form Builder system administrator. By pressing the "⊕" icon, a dropdown dialog shows. The dropdown list allows you to pick a new language to add to the list of languages of the form.

![Adding a language](/files/-LEkC2n3khBL-Uc7xB0X)

When a new language is added:

* It appears in the list of languages at the top right corner of the WYSIWYG area
* All the resources of the previously selected language are copied into the new language

You switch between languages by clicking on the language selector. All localizable resources edits impact the currently selected language.

![Selecting a language](/files/-LEkC2nQlAHzdUBHPn8O)

You can remove the currently selected language by pressing the "⊖" icon. This will remove all the resources associated with that language, so you must be careful before proceeding. A warning dialog will appear before the deletion is completed.

Here is how you typically proceed to create a form in two languages:

* Create the form in the primary language and add all localizable resources such as labels, help messages, hints, etc.
* Add the secondary language.
* Translate all localizable resources now visible on the form.
* When testing the form, you can switch between the two languages to make sure no resource was missed.

## See also

* [Localization](/form-runner/features/localization)
* [Localizing Orbeon Forms](/contributors/localizing-orbeon-forms)


# Form Builder MCP

## Availability

[\[SINCE Orbeon Forms 2025.1.2\]](/release-notes/orbeon-forms-2025.1.2)

This is an early access feature. We're actively working to improve it by creating more tools that expose additional Form Builder functionality to AI agents and by improving the documentation and ergonomics. We believe it is already very useful, which is why we want to make it available to you early. Please let us know if you have any feedback.

Watch the video which shows Form Builder MCP support in the browser:

{% embed url="<https://www.youtube.com/watch?v=-nQ0ed_BfOg>" %}

## What it does

The Form Builder MCP server makes Form Builder's features available to AI agents through the MCP (Model Context Protocol), which is a standard protocol supported by most AI agents. This allows AI agents to interact with Form Builder to:

* create forms based on user instructions
* modify existing forms based on user instructions
* retrieve information about forms, such as their structure and metadata.

Orbeon Forms provides both:

* A Form Builder MCP Server; this is covered in the first section below.
* Support for WebMCP in Form Builder loaded in your browser; this is covered in the second section below.

## MCP Server

<figure><img src="/files/RA7i9wbtgoFN762YmVDM" alt="" width="690"><figcaption><p>AI agent connected to Form Builder MCP</p></figcaption></figure>

### Orbeon Forms configuration

To use the MCE server, set the following property two properties. The first enables the MCP server (it is disabled by default). The second sets the password used to sign the token, which you need to set the value of this property to a secure password.

```xml
<property 
    as="xs:boolean" 
    name="oxf.fb.mcp.enable" 
    value="true"/>
<property 
    as="xs:string"  
    name="oxf.fb.mcp.token.password" 
    value=""/>
```

In order to revoke all tokens issued, simply change the token password.

Once you have those two properties in place, you can generate a token. Open any form in Form Builder and click on the key icon that shows at the top right of the page to reveal the token dialog.

<figure><img src="/files/ULscYcd3LGPx1jm5ibZB" alt="" width="510"><figcaption><p>Creating an MCP token in Form Builder</p></figcaption></figure>

If choosing "Readonly" access, only read-only operations will be allowed, such as listing forms and retrieving form metadata. If choosing "Read/Write" access, all operations will be allowed, including creating and modifying forms.

By default, the token validity is one year. You can change this by setting the following property:

```xml
<property 
    as="xs:integer" 
    name="oxf.fb.mcp.token.validity" 
    value="525600"/>
```

The duration is in minutes, so:

* `1440` means 24 hours (1 day)
* `10080` means 7 days (1 week)
* `44640` means 31 days (1 month)
* `525600` means 365 days (1 year)

### Agent configuration

In what follows:

* `your-form-builder-url` is the URL to your Orbeon Forms, for example `https://example.org/orbeon/fr/mcp/builder`
  * An important part is `/fr/mcp/builder`, which is the path to the MCP server in Form Builder.
  * The domain, port, and prefix (here `/orbeon`) should be those of your Orbeon Forms instance.
* `your-token` is the value of the token you generated in Form Builder.

#### Claude Code

Add the MCP server with:

```
claude mcp add orbeon your-form-builder-url \
    --scope user \
    --transport http \
    --header "Authorization: Bearer your-token"
```

You can then run `claude mcp list` to check it was correctly added and that Claude is able to connect.

#### Codex CLI

Declare an `ORBEON_MCP_TOKEN` environment variable with the value of your token value, then run:

```
codex mcp add orbeon \
    --url your-form-builder-url \ 
    --bearer-token-env-var ORBEON_MCP_TOKEN
```

You can then run `codex mcp list` to check it was correctly properly added.

#### GitHub Copilot CLI

Add the MCP server with:

```
copilot mcp add orbeon \
    --url your-form-builder-url \
    --type http \
    --header "Authorization=Bearer your-token"
```

#### Antigravity and Antigravity CLI

Edit your `~/.gemini/config/mcp_config.json` to add the `orbeon` MCP server, for example:

```json
{
  "mcpServers": {
    "orbeon": {
      "serverUrl": "your-form-builder-url",
      "headers": {
        "Authorization": "Bearer your-token"
      }
    }
  }
}
```

*NOTE: As of August 2026, using `~/.gemini/antigravity-cli/mcp_config.json` doesn't appear to work. Use `~/.gemini/config/mcp_config.json` instead.*

#### Skill (optional)

You can also add to your AI agent a skill file. The latest version of the skill file can be found [in the Orbeon Forms GitHub repository here](https://github.com/orbeon/orbeon-forms/blob/master/.agents/skills/orbeon/SKILL.md). You place such as file in the appropriate location for your AI agent, for example:

```
.agents/skills/orbeon/SKILL.md
```

## WebMCP

<figure><img src="/files/SgSPuNRrlvq524KQb3S9" alt="" width="600"><figcaption><p>AI agent connected to Form Builder through WebMCP</p></figcaption></figure>

As of Summer 2026, WebMCP is a nascent, but very promising standard. The idea is that users will load a WebMCP page in their browser, open an agent in a sidebar, either built in the browser or provided by a browser extension, and the agent in the sidebar will be able to use tools provided by that page. Gemini in Chrome would be a prime candidate to support this, and other vendors like Anthropic or OpenAI could provide similar support through a browser extensions. This could provide a very seamless experience for users.

Today, we get a taste for what using WebMCP will feel like by using a regular external agent, such as Claude or Codex. To set this up:

1. In Chrome, open `chrome://flags/`, search for "MCP", enable both "WebMCP support in DevTools" and "WebMCP for testing", then restart the browser.
2. Set up your agent to add [Chrome DevTools for agents](https://github.com/ChromeDevTools/chrome-devtools-mcp). Make sure to use the `--autoConnect` parameter so your agent can connect to a live browser (for instance, with Claude Code, use `claude mcp add chrome-devtools -s user -- npx chrome-devtools-mcp@latest --autoConnect`).
3. In Chrome, open a form in Form Builder, and ask your agent "with Chrome DevTools, through WebMCP, give me a list of the control in the form I have open in Form Builder".

This set up quite convenient:

* It doesn't require users to generate a token in Form Builder and set up their agent with that token.
* Users can see changes made to the form in real time in the browser. They can also make their own changes, and use both the agent and the Form Builder UI in the same session to update a form.
* In the future, when agents built in the browser, such as Gemini in Chrome, support WebMCP, users will be able to do all of the above directly from their browser, without even having to install an external agent.

## Usage patterns

With MCP support, you can use your AI agent to interact with Form Builder in various ways using prompts such as:

> Using Orbeon, create a new demo form for a personal collection of widgets. Split the form into sections, and use appropriate form controls. Then save and close the form.

The result might look like this:

![Example of a form created by an AI agent](/files/sd0OpCQYl4H5vcTJeL2m)

Further prompts can be used to update the form layout, for example:

> Using Orbeon, edit form 33e71949140e1282b9428770176994bdb24c702a and modify the size of control widget-quantity to half its current width.

Or add validation rules:

> Using Orbeon, update form 33e71949140e1282b9428770176994bdb24c702a to make the type of the widget-quantity field a non-negative integer.


# Advanced


# Edit source

## General rule

It can be tempting to modify the source code of a form definition. But this can be dangerous! The following simple guideline applies:

> Any code which is added to a form definition, and which could not be generated by Form Builder itself, is not guaranteed to be compatible.

The exception is anything that is documented in the Orbeon Forms documentation, for example the [Action Syntax](broken://pages/-LTfNL822m6v3asjBCuO).

## With Orbeon Forms 2017.2 and newer

The "Edit Source" button under the "Advanced" tab in the toolbox opens the "Edit Source" dialog.

![](/files/-LEkBvjyJnZXF5GahqjH)

## With Orbeon Forms 2017.1 and earlier

The "Edit Source" icon in the top right corner of the form area opens the "Edit Source" dialog.

![Edit Source button](/files/-LEkC9AcL_rUDZeFg89a)

## Functionality

This is an advanced feature, and it must be used very carefully.

Form Builder uses XHTML+XForms as form definition representation. The format must be well-formed XML. The dialog shows a text editor containing that representation.

![Source editor](/files/-LEkC9AfGRBJZuS6rZJf)

It is possible to edit the XML representation in the dialog, or to copy/paste it to an external editor for further changes. After saving changes from the dialog, those are immediately taken into account by Form Builder.

*NOTE: You have to be cautious when doing so because it is possible to break the form by making changes! We recommend that you learn about XForms before making such changes.*

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

There is also an ["XHTML Export"](/form-builder/form-editor/buttons-bar) button in the buttons bar, which allows directly downloading the form definition.


# Services and actions

Orbeon Forms comes with a few powerful options to perform complex operations on or with forms. These fall into two main categories:

* *Actions:* They run within a form while the user is working with the form.
* *Simple Processes:* They typically run in association with a form button, such as a "Submit" button.

The following diagram illustrates how Actions and Simple Processes relate to a form:

<figure><img src="/files/NZlRAPQnK9Z8DuWgGLgc" alt="Actions and processes" width="500"><figcaption><p>Actions and processes</p></figcaption></figure>

For more details, see the following blog post: [Making sense of Form Runner Actions](https://www.orbeon.com/2024/09/making-sense-form-runner-actions).

See also:

* [Simple Actions](/form-builder/advanced/services-and-actions/actions)
* [Action Syntax](/form-builder/advanced/services-and-actions/actions-syntax)
* [Action Syntax Examples](/form-builder/advanced/services-and-actions/action-syntax-examples)
* [Database Services](/form-builder/advanced/services-and-actions/database-services)
* [HTTP Services](/form-builder/advanced/services-and-actions/http-services)
* [Synchronizing repeated content](/form-builder/advanced/services-and-actions/synchronize-repeated-content)
* [Simple processes](/form-runner/advanced/buttons-and-processes)


# HTTP services

## Introduction

The HTTP Service Editor allows you to create simple REST services. The idea is that a form can call a service, typically passing XML back and forth.

To create a new HTTP service, click the Add icon under "HTTP Services". The HTTP Service Editor opens.

The following screenshot shows an example of filled-out service:

<figure><img src="/files/A5xFZJhNOsEZEmnNVzLV" alt="Definition tab" width="561"><figcaption><p>Definition tab</p></figcaption></figure>

## Service definition

### Basic settings

The "Definition" tab allows you to set the basic service parameters:

* **Service Name**
  * This is the name of the service, as seen by Form Builder. Must start with a letter, and may not contain spaces.
  * \[SINCE Orbeon Forms 2020.1] The service can be renamed, and actions that use the service are automatically updated.
* **Resource URL**
  * `HTTP` or `HTTPS` URL to which the service must be called.
  * The value is an XPath Value Template, which means that the URL can be dynamic. For example, with Orbeon Forms 2018.1's `fr:control-string-value()` function:

    ```xpath
    https://example.org/{fr:control-string-value('control-1')}
    ```
  * [\[SINCE Orbeon Forms 2024.1\]](/release-notes/orbeon-forms-2024.1) The resource URL can contain variable references to refer to the values of form controls (e.g. `$control-1`).
* **Method**
  * The HTTP method to use: `GET`, `POST`, `PUT` or `DELETE`.
* **Request Body**
  * This applies to the `POST` and `PUT` methods.
  * The XML document to send to the service. When using the "HTML Form" serialization, this is converted to name/value pairs.

### Serializations

#### Basics

Serializations apply to the `POST` and `PUT` methods only.

#### XML serialization

With the "XML" serialization, the XML data is passed using the [external data format](/form-runner/api/data-formats/form-data).

On the wire, this serialization uses the `application/xml` content type.

#### HTML Form serialization

With the "HTML Form" serialization, leaf XML elements in the XML request body are converted to name/value pairs.

On the wire, this serialization uses the `application/x-www-form-urlencoded` encoding, like for standard web forms.

### URL parameters

\[SINCE Orbeon Forms 2016.1]

* This applies to the `GET` and `DELETE` methods.
* You can add as many URL parameters as needed.
* A non-blank URL parameter specifies a default value for the parameter.
* An action can set the value of a parameter.

Here is [how to set URL parameters from an action](/form-builder/advanced/services-and-actions/actions#passing-url-parameters-to-get-and-delete-methods).

### HTTP headers

\[SINCE Orbeon Forms 2026.1]

The HTTP headers section allows you to specify custom HTTP headers to send with the service request, similar to how URL parameters work.

* You can add as many HTTP headers as needed using the "+" button.
* A non-blank header value specifies a default value for the header.
* An action can set the value of a header.

Here is [how to set HTTP headers from an action](/form-builder/advanced/services-and-actions/actions#setting-http-headers).

### JSON support

\[SINCE Orbeon Forms 2016.1]

A JSON *response* from a service is processed and converted to an XML document following the XForms 2.0 scheme (see [JSON support](/xforms/submission/submission-json)).

See [#2480](https://github.com/orbeon/orbeon-forms/issues/2480) for details about sending JSON to services.

### URL Parameters before Orbeon Forms 2016.1

Prior to Orbeon Forms 2016.1, a "request body" is mandatory for the `GET` and `DELETE` methods. The body is not sent to the service, but instead is used to configure request parameters.

The content of the "Request Body" form has to be a well-formed XML document. The name of the root element doesn't matter, but usually `params`or `request` is used. Each child element defines a parameter as shown in the following example:

```xml
<params>
    <userId>1</userId>
    <userName>test</userName>
</params>
```

Here Orbeon invokes the URL:

```
$<Resource URL>?userId=1&userName=test
```

where `$<Resoure URL>` is the content of the input field "Resource URL".

Make sure to select `HTML Form` in the `Serialization` dropdown, otherwise the URL parameters are not appended to the request URL.

## Advanced parameters

The "Advanced" tab allows you to set advanced service parameters:

* **Service Response has Binary Content**
  * \[SINCE Orbeon Forms 2020.1]
  * Check this if you know that the service returns binary content, such as an image. This is necessary if the [`<fr:control-setattachment>`](/form-builder/advanced/services-and-actions/actions-syntax#setting-the-value-of-an-attachment-control) action makes use of the response.
* **HTTP Authentication**
  * Whether to use HTTP authentication.
  * **Username:** Username to use.
  * **Password:** Password to use.
* **SOAP Service**
  * Whether this is a SOAP service
* **SOAP Action**
  * If selected, the value of the `SOAPAction` header.

<figure><img src="/files/-LKSVC9ekJVjEDWkw2j8" alt="Advanced tab" width="562"><figcaption><p>Advanced tab</p></figcaption></figure>

## Testing a service

### Basics

The "Test" button allows you to test the service. Before doing this, you have to set data in the request body for a `POST` or `PUT` request, or you might want to set URL parameters for a `GET` or `DELETE`. Form Builder executes the service, and then provides information about the response returned, including:

* Whether an error occurred (green or red highlight)
* URL called
* Response status code
* Response headers
* Response body

This helps you troubleshoot the service call.

<figure><img src="/files/-LKSVC9h-6gx3VsoxEqt" alt="Test Results tab with response headers" width="562"><figcaption><p>Test Results tab with response headers</p></figcaption></figure>

<figure><img src="/files/-LYcON5AGJymdtf6UQmW" alt="Test Results tab with response body" width="562"><figcaption><p>Test Results tab with response body</p></figcaption></figure>

### XML view of JSON responses

\[SINCE Orbeon Forms 2019.1]

When the service response is in JSON format, you can view the JSON response *translated into XML* following [XForms 2.0](https://www.w3.org/community/xformsusers/wiki/XForms_2.0#External_JSON_values).

This is useful when you want to use XPath expressions to access a JSON response, for example with [datasets](/form-runner/features/datasets) or [item choices](/form-builder/advanced/services-and-actions/actions#setting-the-choices-of-a-selection-control).

See also [JSON support](/xforms/submission/submission-json).

<figure><img src="/files/-LYcON5CKSydIZ8lGqc1" alt="Test Results tab with XML response body" width="562"><figcaption><p>Test Results tab with XML response body</p></figcaption></figure>

## Saving the service

Once your service is defined, the "Save" buttons saves it to the form. You can come back to it and modify it later by clicking on the "Edit" icon next to the service name. You can also delete the service using the trashcan icon.

## Deleting a service

You can delete a saved service using the "Remove" button.

\[SINCE Orbeon Forms 2020.1]

Orbeon Forms warns you if there are existing actions that refer to the service.

## See also

* Blog post: [Making sense of Form Runner Actions](https://www.orbeon.com/2024/09/making-sense-form-runner-actions)
* [Simple Actions](/form-builder/advanced/services-and-actions/actions)
* [Database services](/form-builder/advanced/services-and-actions/database-services)
* [Synchronizing repeated content](/form-builder/advanced/services-and-actions/synchronize-repeated-content)
* [Datasets](/form-runner/features/datasets)
* [JSON support](/xforms/submission/submission-json)


# Database services

## Overview

By using *database services*, a [PE feature](https://www.orbeon.com/pricing), you can use data stored in any table of a relational database, for instance to dynamically populate a dropdown, or to pre-populate fields based on a value entered by users.

## Format of database query responses

A SQL query, when successful, returns an XML document with the following format:

* A root element which is always `<response>`.
* It contains one nested `<row>` element per row.
* Each such element in turn contains one element per selected column, derived from the column name:
  * The name is converted to lowercase.
  * Underscores (`_`) replaced by dashes (`-`).

For example, values for the `dept_no` column end up in `<dept-no>`.

See below for concrete examples.

## Populating a dropdown

In what follows, we'll see how you can populate a *Department* dropdown in your form using values stored in an `departments` table of your relational database.

![Dropdown](/files/-LEkBzNqdB3FwrZMusPE)

### 1. Connect with the database

You start, in Form Builder, by creating a new database service, clicking *Add* in the left sidebar under *Database Services*. This opens the *Database Service Editor*.

Under *Datasource* you type the name of a datasource you setup in your application server. This is the the JNDI name of the datasource, without the `jdbc/` part. If you type `employees`, Orbeon Forms will look for `java:comp/env/jdbc/employees`.

If you're using Tomcat, the simplest way of setting up a datasource is to edit Tomcat's `server.xml`, there add a `<Context>` for Orbeon Forms if you don't have one already, and inside it add a `<Resource>` pointing to your database. On Tomcat, you also need to put the database JDBC driver in Tomcat's `lib` directory.

![Connect to the database](/files/-LEkBzNtDt_aoOt2cFza)

### 2. Write the SQL query

Still in the *Database Service Editor*, you write the SQL query to run in the database. When that query runs, Orbeon Forms creates an XML document with the returned data, and you'll be referring to parts of that document when linking the database service to a specific dropdown.

The user interface in Form Builder looks like this:

![Database Service Editor](/files/-LIq_8NRm3ahccVdRX0G)

Example response:

```xml
<response>
    <row>
        <dept-no>d009</dept-no>
        <dept-name>Customer Service</dept-name>
    </row>
    <row>
        <dept-no>d005</dept-no>
        <dept-name>Development</dept-name>
    </row>
</response>
```

This diagram summarizes the mapping:

![Mapping of columns to XML elements](/files/-LEkBzNvIKleyzuJTLEg)

### 3. Link the dropdown

To "link" the service to the dropdown, you create a new action, set it to run on *Form Load*, call the service you earlier named `list-departments`. Here is how the user interface looks like:

![Action general settings](/files/-LIq_8NVu5wSYVItcAmN)

With the result of the service you want to set the list of possible values of an a *Department* dropdown you have in the form. This is where you extract data from the XML document seen earlier, and you do this with 3 XPath expressions. The first points to the "rows", and will almost always be `/response/row`. Next, you need to tell Orbeon Forms where it can find, inside the `<row>`, the label (the text shown to users) and the value (what is stored in data when users make a selection).

Here is how the user interface looks like:

![Action response](/files/-LIq_8NXoXRtLCDtHQ3K)

This diagram summarizes the mapping:

![Link to the dropdown](/files/-LEkBzNx3LSR0Xowy_9-)

### 4. Select a value

Finally, when your form runs and users make a selection in the dropdown, the *value*, here the content of the `<dept-no>` element, is used to populate the element corresponding to the field in the form data.

## Populating fields using another field value

Say that when users enter a value in *employee number*, you want to lookup the corresponding employee in your database and populate other fields, *First name* and *Last name*, based on the information you find about that employee. We've already know how to establish a [connection with the database](#1-connect-with-the-database), so let's start by seeing how we can use the value of a field in a SQL query.

![Populate fields](/files/-LEkBzO08Bq2LaVhV81h)

### 1. Set a service parameter

Your SQL query can contain *parameters*. Those look like: `<sql:param type="xs:string" select=""/>`. The `type` attribute corresponds to the SQL type to use (e.g. `xs:string`, `xs:decimal`, …). The `select` attribute must be left blank; it is filled-out by the Actions Editor when the service is called.

When the query runs, the value of each parameter is set to the current value of a form field, and you define the mapping between parameter in the SQL query and form field when you create an action. That mapping is done by position; e.g. in the above query, you'll want to set parameter 1 to the control containing an employee id.

![Set service parameter](/files/-LEkBzO2tbjM9xocY4s1)

### 2. Set control values

Finally, you use the *Set Response Control Values* section of the *Actions Editor* dialog to extract the information you're interested in from the XML produced based on the result from the query, and populate fields in the form.

![Set control values](/files/-LEkBzO4zmrXNw_WeAfU)

## Example: fields pre-population

Say you will be passing a request parameter `employee-id=100` to your form, and want to load data from a relational database about the employee with specified id, this to pre-populate some fields, such their first name, last name, hire data, and department, as shown in the screenshot below.

![Pre-populated fields](/files/-MW6UyiZmbUI6w-1AACy)

You can do this as follows, in Form Builder:

1. Create a field to hold the employee id passed through the request parameter. You might want to put this field in another section, for instance named "(Internal)", that you hide from end users by setting in its Section Settings dialog, the Visibility to No. For the field, you can use a Hidden Field, or a Calculated Value if you'd like to see its value at runtime when debugging the form.
2. In the Control Settings for that field, name it `employee-id`, and in the Formulas tab, set its Initial Value to `xxf:get-request-parameter('employee-id')`.
3. Create a database service that retrieves the information about the employee, with a statement like the one below.
4. Create an action that, on form load, runs the database service, passing the value of the `employee-id` field, and setting the value of the first name, last name, hire data, and department fields to values returned by the SQL query.

```sql
SELECT *
  FROM employees
 WHERE employee_id = <sql:param type="xs:string" select=""/>
```

You can get the [source of the form described above](https://gist.github.com/orbeon/1e25851b7219f765a2cde373a72e9dea), and run it for yourself on the instance of Orbeon Forms deployed on demo.orbeon.orbeon, which has the `employees` table used by the above query. After you publish the form, try opening its `/new` page, passing the request parameter `?employee-id=100`.

## See also

* Blog post: [Making sense of Form Runner Actions](https://www.orbeon.com/2024/09/making-sense-form-runner-actions)
* [HTTP services](/form-builder/advanced/services-and-actions/http-services)
* [Simple Actions](broken://pages/-LEkBr90Nq9hbV8krZWy)
* [Synchronizing repeated content](/form-builder/advanced/services-and-actions/synchronize-repeated-content)


# Simple Actions

## Introduction

The Form Builder Actions Editor is an [Orbeon Forms PE](https://www.orbeon.com/download) feature and allows you to implement simple actions in your form. The basic philosophy goes as follows:

1. React to an event occurring on the form, such as the form being loaded or a user action.
2. Call an HTTP or database service:
   * passing in parameters from the form,
   * receiving parameters back from the service.
3. Use the returned parameters to update the form

Actions are tightly coupled with services. In the future, support might be added for actions which do not require services.

## Basic action configuration

### Creating or updating an action

You create a new action or update an existing action in the "Advanced" tab of the toolbox. This opens the Actions Editor.

### Reordering actions

\[SINCE Orbeon Forms 2018.2]

You can reorder actions by drag and drop in the toolbox. Sometimes, the specific order of actions is important, or you might just want to organize them in a way that makes sense for your form.

![List of actions in the toolbox](/files/-LV0esyiorYFXWsQQ7hN)

### With Orbeon Forms 2017.1 or newer

\[SINCE Orbeon Forms 2017.1]

![Actions Editor General Settings](/files/-LEkBz6H8-XIvju96yVl)

This is the meaning of the fields of the dialog:

* **Action Name**
  * This is the name of the action, as seen by Form Builder.
  * Must start with a letter, and may not contain spaces.
* **Run condition**
  * **Always**: Run the action independently from the form mode.
  * **In "new" mode only**: Run the action only in creation mode, that is when the user creates new data, as opposed to editing, viewing, emailing, or generating a PDF.
  * **When formula evaluates to true**: \[SINCE Orbeon Forms 2021.1] Allows you to enter your own condition using XPath, which, if it evaluates to `false`, will prevent the service from running (see [video](https://www.youtube.com/watch?v=_3mIk0OS6G8&)).
* **Run action when**
  * Specifies a phrase built of the following parts:
    * "the control"
      * label / name of the control
        * "changes its value"
        * or "appears or changes its value"
        * or "is activated" (a button is activated or the "Enter" key is pressed in a text line)
    * or "the form loads"
      * "after the controls are ready"
        * This is the default.
        * The action runs when controls are "live", which means you can set their values and list of choices.
      * "after the data is ready"
        * The action runs when the data is ready:
          * It has been loaded from the database if needed.
          * Its initial values and calculations are up to date.
        * You cannot set control values and list of choices as a result.
        * You can store datasets as a result.
      * "before the data's initial values are calculated"
        * The action runs before the data is ready.
        * You cannot set control values and list of choices as a result.
        * You can store datasets as a result.
* **Service to Call.**
  * The service to call as once the action is triggered.

Like for services, once your action is defined, the Save buttons saves it to the form. You can come back to it and modify it later by clicking on the Edit icon next to the action name. You can also delete the action using the Remove button.

## Handling the service request

### User interface

Each row instructs the action to take a value and pass it to the service, and you can add as many such rows as needed using the "+" button, and remove existing entries with the dropdown menu.

![Actions Editor Service Request Actions](/files/H6gJdEssir4epWDoNbOt)

### How a value is provided to the service

* With HTTP services doing a POST:
  * You provide a formula, which must point to an element or attribute node of the request body defined in the HTTP service under "XML Request Body".
* With HTTP services doing a GET or DELETE:
  * The HTTP Service Editor [allows you to define URL parameters](/form-builder/advanced/services-and-actions/http-services#url-parameters) when using the `GET` and `DELETE` HTTP methods.
  * \[SINCE Orbeon Forms 2022.1] The dropdown under "Destination" lists the URL parameter names you have defined in the HTTP Service Editor, allowing you to select the parameter you want to set.
  * \[UNTIL Orbeon Forms 2021.1] The HTTP Service Editor implicitly creates an XML document representing these parameters, as in the example below. The Actions Editor doesn't yet support specifying URL parameters directly by name; instead, you need to enter a formula under "Set Service Request Values", like `//userId` or `//userName`.

    ```xml
    <params>
        <userId>1</userId>
        <userName>test</userName>
    </params>
    ```
* With HTTP headers (see [Setting HTTP headers](#setting-http-headers) below).
* With Database services:
  * To set the first query parameter, use the value "1" (without the quotes), the second, "2", etc.

### Passing a control value

Select "Control Value" in the first dropdown.

Parameters:

* **Control**:
  * Specifies the control whose value must be used.
  * A single "closest" control will be selected:
    * If the control is not within a repeated grid or section, then that unique control is updated.
    * Otherwise, the control which is the closest by following repetitions and repeat indexes is chosen.
* **Destination XPath Expression**:
  * The expression is evaluated in the context of root element of the XML request data to send to the service.
  * The expression must point to an element or attribute node of the request body. If multiple nodes are returned, only the first one is considered.

### Passing a value from a formula

\[SINCE Orbeon Forms 2021.1]

Select "Formula" in the first dropdown.

Parameters:

* **Formula**:
  * The given formula is evaluated and converted to a string.
* **Destination XPath Expression**:
  * The expression is evaluated in the context of root element of the XML request data to send to the service.
  * The expression must point to an element or attribute node of the request body. If multiple nodes are returned, only the first one is considered.

![Action request formula](/files/6fIkiCFp5ODP79cONrCw)

### Setting HTTP headers

\[SINCE Orbeon Forms 2026.1]

You can set HTTP headers dynamically when calling an HTTP service, in the same way you can set URL parameters.

To set an HTTP header:

1. First, define the header in the HTTP Service Editor under the "HTTP Headers" section (see [HTTP headers](/form-builder/advanced/services-and-actions/http-services#http-headers)).
2. In the Actions Editor, under "Service Request Actions", select "Control Value" or "Formula" in the first dropdown.
3. In the destination dropdown, select the header you want to set. Headers are grouped separately from URL parameters in the dropdown list.

Parameters:

* **Control** (when using "Control Value"):
  * Specifies the control whose value must be used as the header value.
* **Formula** (when using "Formula"):
  * An XPath expression that evaluates to the header value.
* **Destination**:
  * The dropdown lists both URL parameters and HTTP headers defined in the HTTP Service Editor, organized into separate groups for easy identification.

![Action request HTTP headers](/files/pR9X6fHAn3P6oFcBW329)

## Handling the service response

### User interface

\[SINCE Orbeon Forms 2017.1]

You access the response in the Service Response Actions tab. This allows adding, removing, and moving response actions:

![Actions Editor Service Response Actions](/files/-LEkBz6c-1Iq_y3pxVmG)

### Setting the value of a control

As a result of running an action, you can set a form control's value from data returned by a service using:

* \[SINCE Orbeon Forms 2017.1] the "Set Control Value" action
* \[UNTIL Orbeon Forms 2016.3] the "Set Response Control Values" section

Parameters:

* **Destination Control:**
  * Specifies the control whose value must be set.
  * A single "closest" control will be selected:
    * If the destination control is not within a repeated grid or section, then that unique control is updated.
    * Otherwise, the control which is the closest by following repetitions and repeat indexes is chosen.
* **Source XPath Expression:**
  * The expression is evaluated in the context of root element of the XML data returned by the service.
  * The expression can point to an element or attribute node of the response body, but can also be a more complex expression. Its result is converted to a string.

### Setting the choices of a selection control

#### Basics

As a result of running an action, you can set a selection control's set of items (AKA "itemset") using:

* \[SINCE Orbeon Forms 2017.1] the "Set Control Choices" action
* \[UNTIL Orbeon Forms 2016.3] the "Set response Selection Control Items" section

Selection controls include dropdown menus, checkboxes, and more.

* **Destination Selection Control.**
  * Specifies the selection control whose items must be set. Only selection controls appear in this list.
  * Depending on the relative position of the source of the action and the target selection controls, one or more "closest" controls can be selected (see the detailed explanation of the behavior below).
* **Choices XPath expression.**
  * The XPath expression must point to a set of element or attribute nodes of the response body returned by the service.
  * For each node returned, an item is created.
* **Label XPath expression.** The XPath expression must return the text of the label for an item. It is relative to the current item node.
* **Value XPath expression.** The XPath expression must return the text of the value for an item. It is relative to the current item node.

#### Adjustment of control values

\[SINCE Orbeon Forms 4.3]

Each selection control's selected value(s) are updated to be in range following the new itemset:

* For single-selection controls: if the item value currently stored in the instance data is not part of the returned set of items, the value is cleared.
* For multiple-selection controls: any of the space-separated values currently stored in the instance data that are not part of the returned set of item values are removed.

#### Behavior starting with Orbeon Forms 2016.1

In the presence of repeated grids or sections, the destination selection control can resolve to zero, one or more concrete controls.

The way this works is that the "closest" controls are searched. This means:

* If the destination selection control is not within a repeated grid or section, then the single destination control is updated.
* If the source of the action is *within the same repeated iteration* as the destination selection control, then that single destination control is updated. Occurrences of the selection control on other repeat repetitions are not updated.
* If the source of the action is at a higher level compared to the destination selection control, then all repetitions of the selection control are updated, and subsequent new repetitions added will also use the new itemset.

#### Behavior up to Orbeon Forms 4.10 included

All the previously available items of the selection control(s) identified are replaced with the items specified. In other words, the itemset for the given control is global. This is the case even in the presence of repeated grids or sections.

#### Internationalization

\[SINCE Orbeon Forms 4.7]

Your service should return localized labels for all the languages supported by your form. For instance, if your form is available in English and French, a service you use to populate a dropdown with a list of countries might return:

```xml
<response>
    <row>
        <value>us</value>
        <lang>en</lang>
        <label>United States</label>
    </row>
    <row>
        <value>us</value>
        <lang>fr</lang>
        <label>États-Unis</label>
    </row>
    <row>
        <value>ch</value>
        <lang>en</lang>
        <label>Switzerland</label>
    </row>
    <row>
        <value>ch</value>
        <lang>fr</lang>
        <label>Suisse</label>
    </row>
</response>
```

After the service is called, the *items*, *label*, and *value* XPath expressions you wrote when defining the action are executed once per language supported by the form, and for each execution the `$fr-lang` variable is set to current language. So in the case of our hypothetical service returning a list of countries, you will define the *items* as `/response/row[lang = $fr-lang]`, the *value* simply as `value`, and *label* as `label`.

While in theory this allows you to have the *values* depend on the language, to avoid unexpected behavior when users switch languages or different users look at the same data using a different language, you should make sure that values are the same for all languages, and only the *labels* differ between languages.

### Storing the response to a dataset

\[SINCE Orbeon Forms 2017.1]

As a result of running an action, you can store the entire set of data returned by the service into a [dataset](/form-runner/features/datasets):

* use the "Save to Dataset" action
* enter a dataset name

Each dataset name identifies a unique dataset for the form. Depending on the use case, dataset names can be reused or be unique.

A dataset name must:

* start with a letter or `_`
* continue with letters, digits, `.`, `-` or `_` (if the name has more than one character)

You access data from a dataset using the [`fr:dataset()`](/xforms/xpath/extension-functions/extension-form-runner#frdataset) function.

For more on datasets, see [Datasets](/form-runner/features/datasets).

*NOTE: Section templates keep their own local datasets, separate from the main form.*

## Namespace handling

At this point, you can't declare custom namespace mappings in the Actions Editor. So say you have a response looking like this:

```xml
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <my:items xmlns:my="http://example.org/my">
            <my:item>
                <my:label>Cat</my:label>
                <my:value>cat</my:value>
            </my:item>
            <my:item>
                <my:label>Dog</my:label>
                <my:value>dog</my:value>
            </my:item>
            <my:item>
                <my:label>Bird</my:label>
                <my:value>bird</my:value>
            </my:item>
        </my:items>
    </soap:Body>
</soap:Envelope>
```

or, using the default namespace mechanism:

```xml
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <items xmlns="http://example.org/my">
            <item>
                <label>Cat</label>
                <value>cat</value>
            </item>
            <item>
                <label>Dog</label>
                <value>dog</value>
            </item>
            <item>
                <label>Bird</label>
                <value>bird</value>
            </item>
        </items>
    </soap:Body>
</soap:Envelope>
```

In both these cases, `<my:item>` (or `<item>`) and nested elements are in the `http://example.org/my` namespace. This means that your XPath expression must match elements in a namespace and that, in theory, you need a custom namespace mapping in form builder. Since this is not supported yet, you can work around the issue by using XPath expressions with wildcards:

* Items: `/soap:Envelope/soap:Body/*:items/*:item`
* Label: `*:label`
* Value: `*:value`

You can even use shorter variations if element names are use consistently, for example:

* Items: `//*:item`
* Label: `*:label`
* Value: `*:value`

These expressions with wildcards ignore namespace information completely, so you have to be careful if your XML document contains elements with the same name but in different namespaces.

## See also

* Blog post: [Making sense of Form Runner Actions](https://www.orbeon.com/2024/09/making-sense-form-runner-actions)
* [Services and actions overview](/form-builder/advanced/services-and-actions)
* [HTTP services](/form-builder/advanced/services-and-actions/http-services)
* [Database services](/form-builder/advanced/services-and-actions/database-services)
* [Synchronizing repeated content](/form-builder/advanced/services-and-actions/synchronize-repeated-content)


# Action Syntax

## Availability

This feature is available since Orbeon Forms 2018.2.

## Rationale

Orbeon Forms supports services and actions. With Orbeon Forms 2018.2, a first step towards more powerful actions is introduced. There is no interface for it yet, but instead an XML configuration which you can paste into the form definition via the ["Edit Source"](/form-builder/advanced/edit-source) dialog.

## Enhancements

In addition to the features available through the [Simple Actions dialog](/form-builder/advanced/services-and-actions/actions), the following enhancements are available:

* Call an action in response to *multiple events*.
* Support *more event types*.
* Call an *arbitrary number* of services.
* Run actions *without* calling services.
* Clear repeated grid or repeated section repetitions.
* Add repeated grid or repeated section repetitions.
* *Repeatedly run* parts of an action. \[SINCE Orbeon Forms 2019.1]
* *Conditionally run* parts of an action. \[SINCE Orbeon Forms 2019.1]

## Updating the form definition

You place listeners and actions within the source code, preferably before the end of the main `<xf:model>` content. For example:

```xml
    <!-- other Form Builder code here -->

    <fr:listener
        version="2018.2"
        .../>
        
    <fr:action name="my-action" version="2018.2">
        ...
    </fr:action>
    
    <!-- Put `<fr:listener>` and `<fr:action>` just above this. -->
</xf:model>
```

## Examples

For concrete examples of Action Syntax use, see [Action Syntax examples](/form-builder/advanced/services-and-actions/action-syntax-examples).

## Listeners

### Basic syntax

A listener looks like this:

```xml
<fr:listener
    version="2018.2"
    modes="..."
    events="..."
    controls="..."
    actions="..."
>
```

| Attribute  | Mandatory                                                                              | Value                                 | Comment                                                                                                                    |
| ---------- | -------------------------------------------------------------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `version`  | Yes                                                                                    | format version                        | always `2018.2`                                                                                                            |
| `modes`    | No                                                                                     | space-separated list of modes         | The listener is enabled for each mode listed only. If absent, the listener is enabled for all modes.                       |
| `events`   | Yes                                                                                    | space-separated list of event names   | When more than one event name is present, the listener reacts if *any* of the listed events is present.                    |
| `controls` | Yes for events which relate to a particular control, like `enabled` or `value-changed` | space-separated list of control names | When more than one control name is present, the listener reacts if an event is dispatched to *any* of the listed controls. |
| `actions`  | No, but nothing will happen if there is not at least one action referenced             | space-separated list of action names  | When more than one action name is present, *all* the specified actions are called when the listener reacts to an event.    |

*NOTE: It is disallowed to mix and match, in a single listener, events for which a control name is required and events for which a control name is not required. Instead, use multiple listeners.*

### Modes supported

* `new`
* `edit`
* `view`
* `pdf`

### Events supported

Controls:

* `enabled`: the control has become enabled
* `disabled`: the control has become disabled
* `visible`: the control has become visible (for example in a wizard page)
* `hidden`: the control has become hidden (for example in a wizard page)
* `value-changed`: the value of an enabled control has changed
* `activated`: the control has been activated (clicked, or enter in text field)
* `item-selected`: an item of an enabled control has been selected
* `item-deselected`: an item of an enabled control has been deselected

Form load:

* `form-load-before-data`: run before the data's initial values are calculated
* `form-load-after-data`: run when the data is ready
* `form-load-after-controls`: run after the controls are ready

See also [Running processes upon page load](/configuration/properties/form-runner/form-runner-detail-page#running-processes-upon-page-load) for the detail of the form load events.

## Actions

### Basic syntax

An action looks like this:

```xml
<fr:action
    version="2018.2" 
    name="my-action" 
>
```

| Attribute | Mandatory | Value          | Comment                               |
| --------- | --------- | -------------- | ------------------------------------- |
| `version` | Yes       | format version | always `2018.2`                       |
| `name`    | Yes       | action name    | must be unique in the form definition |

## Control structures

### Iterating over data

\[SINCE Orbeon Forms 2019.1]

#### Basic usage

```xml
<fr:data-iterate ref="...expression...">
    ...
</fr:data-iterate>
```

`<fr:data-iterate>` allows you to iterate over data. The contained actions are executed once for each value returned by the expression.

Containing actions can include one or more calls to services.

| Attribute | Mandatory | Value                     | Comment                                      |
| --------- | --------- | ------------------------- | -------------------------------------------- |
| `ref`     | Yes       | sequence XPath expression | runs in the current XPath evaluation context |

In the following example, each repetition adds a row to the grid, calls a service, passing the attachment id, and sets the attachment value on the last row.

```xml
<fr:action name="populate-attachments" version="2018.2">

    <fr:service-call service="get-attachments-list"/>
    <fr:repeat-clear repeat="my-grid"/>

    <fr:data-iterate ref="/*/row">
        <fr:repeat-add-iteration repeat="my-grid" at="end"/>
        <fr:service-call service="get-attachment">
            <fr:url-param name="text" value="attachment-id"/>
        </fr:service-call>
        <fr:control-setattachment control="my-attachment" at="end"/>
    </fr:data-iterate>

</fr:action>
```

#### Nesting of iterations

\[SINCE Orbeon Forms 2019.2]

Calls to `<fr:data-iterate>` can be nested. This allows, for example, filling nested repeated sections and/or grids with the result of a service call that returns hierarchical data.

Example:

```xml
<fr:action name="my-action" version="2018.2">
    <fr:service-call service="get-nobel-prizes"/>
    <fr:repeat-clear repeat="prizes"/>
    <fr:data-iterate ref="/*/prizes/_">
        <fr:repeat-add-iteration repeat="prizes"/>
        <fr:control-setvalue value="year"     control="year"     at="end"/>
        <fr:control-setvalue value="category" control="category" at="end"/>
        <fr:repeat-clear repeat="laureates"/>
        <fr:data-iterate ref="laureates/_">
            <fr:repeat-add-iteration repeat="laureates"/>
            <fr:control-setvalue value="firstname"  control="firstname"  at="end"/>
            <fr:control-setvalue value="surname"    control="surname"    at="end"/>
            <fr:control-setvalue value="motivation" control="motivation" at="end"/>
        </fr:data-iterate>
    </fr:data-iterate>
</fr:action>
```

### Conditions

\[SINCE Orbeon Forms 2019.1]

```xml
<fr:if condition="...boolean expression...">
    ...
</fr:if>
```

`<fr:if>` allows you to conditionally run a block of actions.

| Attribute   | Mandatory | Value                    | Comment                                      |
| ----------- | --------- | ------------------------ | -------------------------------------------- |
| `condition` | Yes       | boolean XPath expression | runs in the current XPath evaluation context |

In the following example, with the repetition performed by `<fr:data-iterate>`, the call to the service that retrieves an attachment depends on whether there is a non-blank attachment id provided.

```xml
<fr:action name="populate-attachments" version="2018.2">

    <fr:service-call service="get-attachments-list"/>
    <fr:repeat-clear repeat="my-grid"/>

    <fr:data-iterate ref="/*/row">
        <fr:repeat-add-iteration repeat="my-grid" at="end"/>
        <fr:if condition="xxf:non-blank(attachment-id)">
            <fr:service-call service="get-attachment">
                <fr:url-param name="text" value="attachment-id"/>
            </fr:service-call>
            <fr:control-setattachment control="my-attachment" at="end"/>
        </fr:if>
    </fr:data-iterate>

</fr:action>
```

## Asynchronous actions

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

Previously, actions were always run *synchronously*. That is, they were blocking any other form processing until completed, even when calling, for example, external services.

It is now possible to run actions *asynchronously*. This means that, if the action contains services calls, form processing can continue while service calls are pending in the background. Once services terminate, the rest of the actions is processed.

Changing this mode only matters for actions that include service calls.

You can enable asynchronous actions either:

* at the action level, using the `async="true"` attribute
* at the form level, using the `oxf.fr.detail.actions.async.*.*` property

Example of configuration property:

```xml
<property 
    as="xs:boolean" 
    name="oxf.fr.detail.actions.async.*.*"                            
    value="true"/>
```

It is currently not possible to mix and match synchronous and asynchronous service calls within a given action.

An asynchronous action, by default, causes a response to the client (web browser) to wait "forever" for its completion. This is done to enhance backward-compatibility. This can be disabled:

* at the action level, using the `response-must-await="0ms"` attribute (or `0` followed by any other unit allowed as shown below)
* at the form level, using the `oxf.fr.detail.actions.response-must-await.*.*` property

Example of configuration property:

```xml
<property 
    as="xs:string" 
    name="oxf.fr.detail.actions.response-must-await.*.*"              
    value="0ms"/>
```

If `response-must-await` is set to `0ms`, a response to the client doesn't wait for the completion of asynchronous actions. Instead, the response is sent immediately, and the pending action's service calls continue to wait in the background. The client polls the server at regular intervals to check for the completion of such pending services, and then resumes the processing of the actions. This is useful when an action calls long-running services, and you don't want to keep the client waiting for its completion.

Possible values for this property and attribute are:

* `forever`
* `"$length$unit"` (whitespace allowed around and between tokens)
  * `$length` is a positive long integer value
  * `$unit` is as described below

| Unit | Description  |
| ---- | ------------ |
| `h`  | hours        |
| `m`  | minutes      |
| `s`  | seconds      |
| `ms` | milliseconds |

Practically, units in the `ms` and `s` ranges are the most useful.

Examples:

* `response-must-await="0ms"`: don't wait
* `response-must-await="200ms"`: wait up to 200 milliseconds
* `response-must-await="5s"`: wait up to 5 seconds
* `response-must-await="forever`"\`: wait indefinitely

For example, the Form Runner Landing page runs multiple background services to load lists of published forms. The page waits a few hundreds of milliseconds for the completion of those services, and if they take longer, for example due to a slow database, the page is shown to the user, and the services continue to run in the background.

In the future, asynchronous actions will likely be enabled by default.

## Resolution of form controls

Many individual actions refer to form controls. When form controls are present in a repeated section or repeated grid, there can be ambiguity as to which control is being referred to.

For this reason, an optional `at` attribute can be specified. That attribute is a space-separated list of tokens, one for each repetition level.

A token can take the following values:

* `start`: the first repetition
* `end`: the last repetition
* a strictly positive integer: the repetition at that specific position
* `all`: all controls [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

When the `at` attribute is not present, it defaults to being relative to the action source. This usually means targeting the first repetition.

The following example sets the value of the control `my-repeated-value` in all repetitions of the `my-repeated-value` repeated grid to `42`

```xml
<fr:control-setvalue 
    value="'42'" 
    control="my-repeated-value" 
    at="all"/>
```

Assume now that the control `my-repeated-value` is within a repeated grid, itself nested within two levels of repeated sections. The following example sets the value of the control `my-repeated-value` in the third repetition of the last repetition of the first repetition to `42`:

```xml
<fr:control-setvalue 
    value="'42'" 
    control="my-repeated-value" 
    at="start end 3"/>
```

## Individual actions

### Calling a service

```xml
<fr:service-call service="...service name...">
    <fr:value value="..." ref="..."/>
</fr:service-call>
```

`<fr:service-call>` calls a service by name.

| Attribute | Mandatory | Value                       | Comment                     |
| --------- | --------- | --------------------------- | --------------------------- |
| `service` | Yes       | name of the service to call | must be an existing service |

#### Passing a value

```xml
<fr:value value="..." ref="..."/>
```

or:

```xml
<fr:value control="..." ref="..."/>
```

When calling an [HTTP service](/form-builder/advanced/services-and-actions/http-services), you can set XML request body values using nested `<fr:value>` elements.

| Attribute | Mandatory | Value                  | Comment                                              |
| --------- | --------- | ---------------------- | ---------------------------------------------------- |
| `control` | No        | control name           | either this or `value` must be specified             |
| `value`   | No        | value expression       | either this or `control` must be specified           |
| `ref`     | Yes       | destination expression | points to an element or attribute in the request XML |

#### Passing a URL parameter

```xml
<fr:url-param name="..." value="..."/>
```

When calling an [HTTP service](/form-builder/advanced/services-and-actions/http-services), you can pass URL parameters using nested `<fr:url-param>` elements.

| Attribute | Mandatory | Value            | Comment                                    |
| --------- | --------- | ---------------- | ------------------------------------------ |
| `control` | No        | control name     | either this or `value` must be specified   |
| `value`   | No        | value expression | either this or `control` must be specified |
| `name`    | Yes       | parameter name   | URL parameter name                         |

#### Passing a SQL parameter

```xml
<fr:sql-param index="..." value="..."/>
```

When calling a [database service](/form-builder/advanced/services-and-actions/database-services), you can pass parameters using nested `<fr:sql-param>` elements.

| Attribute | Mandatory | Value            | Comment                                    |
| --------- | --------- | ---------------- | ------------------------------------------ |
| `control` | No        | control name     | either this or `value` must be specified   |
| `value`   | No        | value expression | either this or `control` must be specified |
| `index`   | Yes       | positive integer | SQL query parameter index                  |

### Removing all repetitions of a repeat

```xml
<fr:repeat-clear
    repeat="..."
    at="..."/>
```

| Attribute | Mandatory | Value                                                                  | Comment                                 |
| --------- | --------- | ---------------------------------------------------------------------- | --------------------------------------- |
| `repeat`  | Yes       | repeated grid or repeated section name                                 |                                         |
| `at`      | No        | space-delimited position tokens: `start`, `end`, or a positive integer | missing leading tokens default to `end` |

This action starts by identifying a single repeated grid or section with the `repeat` attribute. See `<fr:repeat-add-iteration>` for details. The only difference is that with this action, `at` is only used to identify the ancestor repeated sections if any.

With `my-repeated-grid` nested within `my-repeated-section`:

* Remove all repetitions of `my-repeated-section`:

  ```xml
  <fr:repeat-clear repeat="my-repeated-section"/>
  ```
* Remove all repetitions of the last `my-repeated-grid`:

  ```xml
  <fr:repeat-clear repeat="my-repeated-grid" at="end"/>
  ```
* Remove all repetitions of the first `my-repeated-grid`:

  ```xml
  <fr:repeat-clear repeat="my-repeated-grid" at="start"/>
  ```
* Remove all repetitions of the second `my-repeated-grid`:

  ```xml
  <fr:repeat-clear repeat="my-repeated-grid" at="2"/>
  ```

### Adding repetitions to a repeat

```xml
<fr:repeat-add-iteration
    repeat="..."
    at="..."/>
```

| Attribute        | Mandatory | Value                                                                            | Comment                                                                                              |
| ---------------- | --------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `repeat`         | Yes       | repeated grid or repeated section name                                           |                                                                                                      |
| `at`             | No        | space-delimited position tokens: `start`, `end`, or a positive integer           | missing leading tokens default to `end`                                                              |
| `apply-defaults` | No        | whether to apply initial value formulas when adding iterations:`true` or `false` | if missing, use the repeated section or grid's "Apply initial value formulas when adding iterations" |

`apply-defaults` is \[SINCE Orbeon Forms 2026.1]/\[SINCE Orbeon Forms 2025.1.1]/\[SINCE Orbeon Forms 2024.1.5]/\[SINCE Orbeon Forms 2023.1.9].

This action starts by identifying a single repeated grid or section with the `repeat` attribute. If the repeated grid or section is at the top-level, there is only one possible match. If the repeated grid or section is *nested* within one or more repeated sections, then a single repetition of the ancestor repeated sections is determined using the optional `at` attribute.

With `my-repeated-grid` nested within `my-repeated-section`:

* Insert a new repetition at the end of the last `my-repeated-grid`:

  ```xml
  <fr:repeat-add-iteration repeat="my-repeated-grid" at="end end"/>
  ```

  or:

  ```xml
  <fr:repeat-add-iteration repeat="my-repeated-grid" at="end"/>
  ```

  or:

  ```xml
  <fr:repeat-add-iteration repeat="my-repeated-grid"/>
  ```
* Insert a new repetition at the end of the first `my-repeated-grid`:

  ```xml
  <fr:repeat-add-iteration repeat="my-repeated-grid" at="start end"/>
  ```
* Insert a new repetition at the start of the first `my-repeated-grid`:

  ```xml
  <fr:repeat-add-iteration repeat="my-repeated-grid" at="start start"/>
  ```
* Insert a new repetition after repetition 2 of the third `my-repeated-grid`:

  ```xml
  <fr:repeat-add-iteration repeat="my-repeated-grid" at="3 2"/>
  ```
* Insert a new repetition at the end of `my-repeated-section`:

  ```xml
  <fr:repeat-add-iteration repeat="my-repeated-section" at="end"/>
  ```

  or:

  ```xml
  <fr:repeat-add-iteration repeat="my-repeated-section"/>
  ```

### Removing repetitions from a repeat

```xml
<fr:repeat-remove-iteration
    repeat="..."
    at="..."/>
```

| Attribute | Mandatory | Value                                                                  | Comment                                 |
| --------- | --------- | ---------------------------------------------------------------------- | --------------------------------------- |
| `repeat`  | Yes       | repeated grid or repeated section name                                 |                                         |
| `at`      | No        | space-delimited position tokens: `start`, `end`, or a positive integer | missing leading tokens default to `end` |

This action starts by identifying a single repeated grid or section with the `repeat` attribute. See `<fr:repeat-add-iteration>` for details.

With `my-repeated-grid` nested within `my-repeated-section`:

* Remove the last repetition of the last `my-repeated-grid`:

  ```xml
  <fr:repeat-remove-iteration repeat="my-repeated-grid" at="end end"/>
  ```

  or:

  ```xml
  <fr:repeat-remove-iteration repeat="my-repeated-grid" at="end"/>
  ```

  or:

  ```xml
  <fr:repeat-remove-iteration repeat="my-repeated-grid"/>
  ```
* Remove the last repetition of the first `my-repeated-grid`:

  ```xml
  <fr:repeat-remove-iteration repeat="my-repeated-grid" at="start end"/>
  ```
* Remove repetition 2 of the third `my-repeated-grid`:

  ```xml
  <fr:repeat-remove-iteration repeat="my-repeated-grid" at="3 2"/>
  ```

### Setting the value of a control

```xml
<fr:control-setvalue/>
```

`<fr:control-setvalue/>` sets the value of a form control.

| Attribute | Mandatory | Value                                                                    | Comment                                 |
| --------- | --------- | ------------------------------------------------------------------------ | --------------------------------------- |
| `control` | Yes       | control name                                                             | identifies the control to modify        |
| `value`   | Yes       | value expression                                                         | resolves to the value to set            |
| `at`      | No        | space-delimited position tokens: `start`, `end`, `all`, positive integer | missing leading tokens default to `end` |

```xml
<fr:control-setvalue
    control="my-control"
    value="adjust-date-to-timezone(current-date(), ())"/>
```

### Clearing the value of a control

\[SINCE Orbeon Forms 2020.1]

```xml
<fr:control-clear/>
```

`<fr:control-clear/>` clears the value of a control. For attachment controls, this clears the file but also the file metadata (filename, mediatype, and size).

| Attribute | Mandatory | Value                                                                  | Comment                                 |
| --------- | --------- | ---------------------------------------------------------------------- | --------------------------------------- |
| `control` | Yes       | control name                                                           |                                         |
| `at`      | No        | space-delimited position tokens: `start`, `end`, or a positive integer | missing leading tokens default to `end` |

Example:

```xml
<fr:control-clear control="my-control"/>
```

### Setting the choices of a selection control

```xml
<fr:control-setitems/>
```

`<fr:control-setitems/>` sets the choices (or "itemset") of a selection control such as a dropdown or radio buttons.

| Attribute | Mandatory | Value                                                                  | Comment                                 |
| --------- | --------- | ---------------------------------------------------------------------- | --------------------------------------- |
| `control` | Yes       | control name                                                           |                                         |
| `items`   | Yes       | XPath expression returning one XPath item for each choice              |                                         |
| `label`   | Yes       | relative XPath expression returning the label for the current choice   |                                         |
| `hint`    | No        | relative XPath expression returning the hint for the current choice    | \[SINCE Orbeon Forms 2020.1]            |
| `value`   | Yes       | relative XPath expression returning the value for the current choice   |                                         |
| `at`      | No        | space-delimited position tokens: `start`, `end`, or a positive integer | missing leading tokens default to `end` |

*NOTE: Hints are only supported for checkboxes and radio buttons as of Orbeon Forms 2020.1.*

### Writing to a dataset

```xml
<fr:dataset-write
    name="..."/>
```

| Attribute | Mandatory | Value        | Comment |
| --------- | --------- | ------------ | ------- |
| `name`    | Yes       | dataset name |         |

This action takes the latest service result and saves it to the dataset specified by name. Example:

```xml
<fr:dataset-write
    name="my-dataset"/>
```

### Clearing a dataset

\[SINCE Orbeon Forms 2022.1.2]

```xml
<fr:dataset-clear
    name="..."/>
```

| Attribute | Mandatory | Value        | Comment |
| --------- | --------- | ------------ | ------- |
| `name`    | Yes       | dataset name |         |

This action clears the content of the dataset with the given name. Specifically, it replaces it with a single anonymous root element (`<_/>`). Example:

```xml
<fr:dataset-clear
    name="my-dataset"/>
```

### Calling a process

\[SINCE Orbeon Forms 2019.1]

```xml
<fr:process-call
    scope="oxf.fr.detail.process"
    name="send"/>
```

| Attribute | Mandatory | Value          | Comment |
| --------- | --------- | -------------- | ------- |
| `scope`   | Yes       | property scope |         |
| `name`    | Yes       | process name   |         |

### Navigating to a page or URL

\[SINCE Orbeon Forms 2019.1]

```xml
<fr:navigate
    location="https://www.bbc.com/news"/>
```

| Attribute  | Mandatory | Value                                               | AVT      | Comment                       |
| ---------- | --------- | --------------------------------------------------- | -------- | ----------------------------- |
| `location` | Yes       | path or URL                                         | 2024.1.1 |                               |
| `target`   | No        | `_self` or `_blank` or name of the browsing context |          | where to display the location |

### Setting the value of an attachment control

\[SINCE Orbeon Forms 2019.1]

```xml
<fr:control-setattachment
    control="..."/>
```

| Attribute | Mandatory | Value        | Comment |
| --------- | --------- | ------------ | ------- |
| `control` | Yes       | control name |         |

When the response of a service is binary, this action allows setting the value of an attachment control to the content of the service response:

```xml
<fr:control-setattachment
    control="my-attachment"/>
```

This supports the following controls:

* `<fr:attachment>`
* `<fr:image-attachment>`

The mediatype and received by the service and the actual size of the attachment are automatically set. However, the filename is not set automatically.

See also the following actions:

* `<fr:control-setfilename>`
* `<fr:control-setmediatype>`
* `<fr:control-setsize>`

### Setting the filename of an attachment control

\[SINCE Orbeon Forms 2019.1]

```xml
<fr:control-setfilename
    control="..."
    value="..."/>
```

| Attribute | Mandatory | Value            | Comment                      |
| --------- | --------- | ---------------- | ---------------------------- |
| `control` | Yes       | control name     |                              |
| `value`   | Yes       | value expression | value of the filename to set |

This action allows setting the filename of an attachment control. Example:

```xml
<fr:control-setfilename
    control="my-attachment"
    value="'My Image.png'"/>
```

This supports the following controls:

* `<fr:attachment>`
* `<fr:image-attachment>`

### Setting the mediatype of an attachment control

\[SINCE Orbeon Forms 2019.1]

```xml
<fr:control-setmediatype
    control="..."
    value="..."/>
```

| Attribute | Mandatory | Value            | Comment                       |
| --------- | --------- | ---------------- | ----------------------------- |
| `control` | Yes       | control name     |                               |
| `value`   | Yes       | value expression | value of the mediatype to set |

This action allows setting the mediatype of an attachment control. Example:

```xml
<fr:control-setmediatype
    control="my-attachment"
    value="'image/png'"/>
```

This supports the following controls:

* `<fr:attachment>`
* `<fr:image-attachment>`

Note that the `<fr:control-setattachment>` action automatically sets a mediatype.

### Setting the size of an attachment control

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

```xml
<fr:control-setsize
    control="..."
    value="..."/>
```

| Attribute | Mandatory | Value            | Comment                  |
| --------- | --------- | ---------------- | ------------------------ |
| `control` | Yes       | control name     |                          |
| `value`   | Yes       | value expression | value of the size to set |

This action allows setting the mediatype of an attachment control. Example:

```xml
<fr:control-setsize
    control="my-attachment"
    value="123"/>
```

This supports the following controls:

* `<fr:attachment>`
* `<fr:image-attachment>`

Note that the `<fr:control-setattachment>` action automatically sets a size.

### Setting the focus on a form control

\[SINCE Orbeon Forms 2022.1.2]

```xml
<fr:control-setfocus
    control="..."/>
```

| Attribute | Mandatory | Value        | Comment |
| --------- | --------- | ------------ | ------- |
| `control` | Yes       | control name |         |

This action allows setting the focus on a form control. Example:

```xml
<fr:control-setfocus
    control="my-text-field"/>
```

### Marking a form control visited or unvisited

\[SINCE Orbeon Forms 2022.1.2]

```xml
<fr:control-setvisited
    control="..."
    visited=""/>
```

| Attribute | Mandatory | Value             | Comment            |
| --------- | --------- | ----------------- | ------------------ |
| `control` | Yes       | control name      |                    |
| `visited` | No        | `true` or `false` | defaults to `true` |

This action allows setting whether a form control is visited or not. Example:

```xml
<fr:control-setvisited
    control="my-text-field"
    visited="false"/>
```

In Orbeon Forms, a form control can be *visited* or not. Visited controls have been visited by the user, which means that the user navigated through the form control, possibly without changing its value. One way to visit form controls is to navigate using the "Tab" key, or to click on the form control and then click outside of it. Another way is to use the default "Save" or "Send" buttons, which by default visit all the form controls before proceeding. The notion is used to determine whether to show validation errors associated with that form control. [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1) Form controls can also be visited when they are calculated, visible, and their value changes.

When working with actions, some form controls can benefit from having their "visited" status reset so that the user doesn't see extra errors appear. The `<fr:control-setvisited>` action allows doing that.

### Alert for debugging

\[SINCE Orbeon Forms 2022.1, 2021.1.2, 2020.1.7] The `<fr:alert>` action is intended to be used for debugging, allowing you to get some visibility on the value of intermediary results, or more generally the value of any expression is at a given point.

The value of the `message` attribute uses the [AVT syntax](/xforms/core/attribute-value-templates), so the example below would show a dialog with the message "The answer is 42".

```xml
<fr:alert message="The answer is {40 + 2}"/>
```

| Attribute | Mandatory | Value       | AVT | Comment |
| --------- | --------- | ----------- | --- | ------- |
| `message` | Yes       | path or URL | Yes |         |

### Copying control content

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

#### Description

The `<fr:copy-content>` action allows you to copy form content:

* from non-repeated form controls to other non-repeated form controls
* from non-repeated form controls to repeated form controls
* from repeated form controls to other repeated form controls

See also [Synchronizing repeated content](/form-builder/advanced/services-and-actions/synchronize-repeated-content).

#### Example

Assuming that:

* `control-1`, `control-2`, `control-3`, and `control-4` are all within repeated grids or repeated sections
* `non-repeated-control` is non-repeated, and `control-5` is repeated

Then, in the following example, each `<fr:map>` expresses that all repeated controls denoted by the `left` attribute (source) are copied to the corresponding repeated controls denoted by the `right` attribute (destination).

```xml
<fr:copy-content warn="true">
    <fr:map left="control-1"            right="control-3"/>
    <fr:map left="control-2"            right="control-4"/>
    <fr:map left="non-repeated-control" right="control-5" right-at="all"/>
</fr:copy-content>
```

![Example form showing copied content](/files/YcFZEb28vPL7wCRq1nqD)

#### Repeated source and destination

When both the source and destination are repeated, the destination repeat's iterations are enforced, which means:

* Extra iterations in the destination are removed from the end if needed, to match the number of iterations in the source.
* New iterations in the destination are added at the end if needed, to match the number of iterations in the source.
* For each iteration, the values of controls specified with `<fr:map>` are copied over.

Each `<fr:map>` element can individually refer to separate repeated grids and sections. In other words, not all `left` need to be in the same repeated grid or section, and not all `right` need to be in the same repeated grid or section.

#### Non-repeated source and destination

When the source is non-repeated and the destination is also non-repeated, the value of the control is simply copied.

#### Non-repeated source and repeated destination

When the source is non-repeated and the destination is repeated, the value of the control is copied to one or more of the destination controls. The `right-at` attribute can be used to control this behavior:

* `start`: first control only
* `end`: last control only
* strictly positive integer: specific position
* `all`: all controls

#### Repeated source and non-repeated destination

This scenario is not supported yet.

#### Warning the user

When the `warn` attribute is set to `true`, the action will show a warning dialog if the action will overwrite existing content in the destination. An overwrite is considered when:

* The destination has more iterations than the source.
* The destination has the same number of iterations as the source or less, but the action would overwrite a non-blank destination value.

The default is `false`.

## Error handling

With Orbeon Forms 2018.2, errors when running services are silently ignored and the action continues.

With Orbeon Forms 2019.1, errors:

1. cause the entire action to stop.
2. run the `oxf.fr.detail.process.action-service-error` process

The default implementation of the service error process is as follows:

```xml
<property as="xs:string"  name="oxf.fr.detail.process.action-service-error.*.*">
    error-message("service-error")
</property>
```

You can provide your own service error process in properties-local.xml.

## Evaluation context of XPath expressions

The context item used for XPath evaluations is set as follows:

* At the beginning of an action, it is the root element of the form data.
* Within an `<fr:data-iterate>`, and until a service response is available, it is the current iteration item.
* After a service call, whether at the top-level or within an `<fr:data-iterate>`, it is the root element of the preceding action response.

\[SINCE Orbeon Forms 2019.1]

You can explicitly set the XPath evaluation context to the current iteration item within a `<fr:data-iterate>` with the `expression-context` attribute set to `current-iteration`.

In the following example, without the `expression-context="current-iteration"` attribute, the second `<fr:value>` would evaluate within the context of the first service call's response.

```xml
<fr:data-iterate ref="/*/row">
    <fr:service-call service="my-first-service">
        <fr:value
            value="foo"
            ref="/some/path"/>
    </fr:service-call>
    <fr:service-call service="my-second-service">
        <fr:value
            expression-context="current-iteration"
            value="bar"
            ref="/some/other/path"/>
    </fr:service-call>
</fr:data-iterate>
```

To be more explicit, the attribute can also be set on the first `<fr:value>`:

```xml
<fr:data-iterate ref="/*/row">
    <fr:service-call service="my-first-service">
        <fr:value
            expression-context="current-iteration"
            value="foo"
            ref="/some/path"/>
    </fr:service-call>
    <fr:service-call service="my-second-service">
        <fr:value
            expression-context="current-iteration"
            value="bar"
            ref="/some/other/path"/>
    </fr:service-call>
</fr:data-iterate>
```

## See also

* Blog post: [Making sense of Form Runner Actions](https://www.orbeon.com/2024/09/making-sense-form-runner-actions)
* [Services and actions overview](/form-builder/advanced/services-and-actions)
* [Action Syntax examples](/form-builder/advanced/services-and-actions/action-syntax-examples)
* [Editing the source code of the form definition](/form-builder/advanced/edit-source)
* [Synchronizing repeated content](/form-builder/advanced/services-and-actions/synchronize-repeated-content)
* [Simple Actions](/form-builder/advanced/services-and-actions/actions)
* [HTTP services](/form-builder/advanced/services-and-actions/http-services)
* [Database services](/form-builder/advanced/services-and-actions/database-services)


# Action Syntax examples

## Overview

This document provides examples of the [Action Syntax](/form-builder/advanced/services-and-actions/actions-syntax) used to define actions in Form Builder.

## Example 1: List of Nobel Prize winners

The Nobel Prize organization exposes a [REST API](https://www.nobelprize.org/about/developer-zone-2/). We would like to create a form that queries that API to return the 2023 Nobel Prize winners, and show these details in a table.

To do this, we start by creating a simple form with a nested repetition:

* a repeated section for the Nobel Prizes
* a nested repeated grid for the laureates

Here is how the form looks like in Form Builder:

![Nobel Prize form](/files/zmhIPYCo3I9zxxezBvuw)

We then also create an HTTP Service endpoint:

![Nobel Prize service](/files/1VTLw54WRMXeeUMeLho0)

It points to the following API endpoint:

```
https://api.nobelprize.org/2.1/nobelPrizes?nobelPrizeYear=2023
```

This returns data in JSON format (with `...` to indicate omitted parts):

```json
{
  "nobelPrizes": [
    {
      "awardYear": "2023",
      "category": {
        "en": "Chemistry",
        ...
      },
      ...,
      "laureates": [
        {
          "id": "1029",
          "knownName": {
            "en": "Moungi Bawendi"
          },
          ...,
          "motivation": {
            "en": "in recognition of the extraordinary services he has rendered by the discovery of the laws of chemical dynamics and osmotic pressure in solutions",
            ...
          }
        },
        ...
      ],
      ...
    }
  ],
  "meta": ...,
  "links": ...
}
```

Form Runner operates on an XML view of JSON data so that you can use XPath formulas. You can see it directly in the "XML Response Body" of the HTTP Service Editor. Here is what that view looks like:

```xml
<json type="object">
    <nobelPrizes type="array">
        <_ type="object">
            <awardYear>2023</awardYear>
            <category type="object">
                <en>Chemistry</en>
                ...
            </category>
            ...
            <laureates type="array">
                <_ type="object">
                    <id>1029</id>
                    <knownName type="object">
                        <en>Moungi Bawendi</en>
                    </knownName>
                    ...
                    <motivation type="object">
                        <en>for the discovery and synthesis of quantum dots</en>
                       ...
                    </motivation>
                    ...
                </_>
                ...
            </laureates>
        </_>
    </nobelPrizes>
    ...
    <meta type="object">
        ...
    </meta>
    <links type="object">
        ...
    </links>
</json>
```

Finally, we write, using the Form Builder's Action Syntax, an action that:

* runs upon form load
* calls the `get-nobel-prizes` service
* clears the `prizes` repeat
* iterates over the Nobel Prizes (`/*/nobelPrizes/_`), and for each entry
  * adds iterations to the `prizes` repeat
  * sets values in the controls `year` and `category` from the `awardYear` and `category/en` fields in the data
  * clears the `laureates` repeat
  * iterates over the laureates (`laureates/_`), and for each entry
    * adds iterations to the `laureates` repeat
    * sets values in the controls `known-name` and `motivation` from the `knownName` and `motivation/en` fields in the data

There is a nested iteration due to the nested repeats. here is what the complete listener and action look like:

```xml
<fr:listener version="2018.2" events="form-load-after-controls" actions="my-action"/>

<fr:action name="my-action" version="2018.2">
    <fr:service-call service="get-nobel-prizes"/>
    <fr:repeat-clear repeat="prizes"/>
    <fr:data-iterate ref="/*/nobelPrizes/_">
        <fr:repeat-add-iteration repeat="prizes" at="end"/>
        <fr:control-setvalue value="awardYear" control="year" at="end"/>
        <fr:control-setvalue value="category/en" control="category" at="end"/>
        <fr:repeat-clear repeat="laureates"/>
        <fr:data-iterate ref="laureates/_">
            <fr:repeat-add-iteration repeat="laureates"/>
            <fr:control-setvalue value="knownName" control="known-name" at="end"/>
            <fr:control-setvalue value="motivation/en" control="motivation" at="end"/>
        </fr:data-iterate>
    </fr:data-iterate>
</fr:action>
```

When you test or run the deployed form, you see the Nobel Prize winners for 2023:

![Nobel Prize winners](/files/XkE0H5SGrJTJic5LstT6)

## See also

* Blog post: [Making sense of Form Runner Actions](https://www.orbeon.com/2024/09/making-sense-form-runner-actions)
* [Services and actions overview](/form-builder/advanced/services-and-actions)
* [Action Syntax](/form-builder/advanced/services-and-actions/actions-syntax)
* [Editing the source code of the form definition](/form-builder/advanced/edit-source)
* [Synchronizing repeated content](/form-builder/advanced/services-and-actions/synchronize-repeated-content)
* [Simple Actions](/form-builder/advanced/services-and-actions/actions)
* [HTTP services](/form-builder/advanced/services-and-actions/http-services)
* [Database services](/form-builder/advanced/services-and-actions/database-services)


# Synchronizing repeated content

## Availability

This is available since Orbeon Forms 2018.2.

This is an Orbeon Forms PE feature.

## Rationale

Often, forms require some kind of [master-detail interface](https://en.wikipedia.org/wiki/Master%E2%80%93detail_interface), where you first enter a list of initial information, and then complete the information for each item of the list in more details on a separate screen.

A way of doing this with Orbeon Forms consists in:

* using a [repeated grid](/form-builder/form-editor/repeated-grids) for the basic information
* using a [repeated section](/form-builder/form-editor/section-settings#repeated-content), later in the form, for the details

The difficulty is how to synchronize the two. This is doable with custom XForms code, but it is difficult and error-prone. So Orbeon Forms 2018.2 and newer includes a component which does this synchronization automatically for you.

## Configuration

With Orbeon Forms 2018.2, there is no user interface for this yet. You insert the component by editing the source of the form definition with the ["Edit Source"](/form-builder/advanced/edit-source) toolbox button, and insert a piece of XML in the source.

The synchronization component looks like this:

```xml
<fr:synchronize-repeated-content>
```

It takes the following required XML attributes:

* `left`: the name of the source (or "left") repeated grid or repeated section
* `right`: the name of the destination (or "right") repeated grid or repeated section
* `sync-on-form-load` (optional): [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1) can be set to `none` to disable right grid synchronization based on left grid on form load; disabling synchronization on load is a way to support having multiple lefts defined to synchronize with a single right, but only one left doing so at any given time, e.g. because the other lefts are irrelevant
* `version`: must be `2018.2`

Names are as you see them in the Form Builder user interface with the ["Grid Settings"](/form-builder/form-editor/repeated-grids#grid-settings) or ["Section Settings"](/form-builder/form-editor/section-settings#basic-settings) dialogs.

In addition, it can automatically copy the value of controls from the source to the destination. You specify this with nested `<fr:map>` elements. They take the following required XML attributes:

* `left`: the name of the source (or "left") control
* `right`: the name of the destination (or "right") control

Names are as you see them in the Form Builder user interface with the ["Control Settings"](/form-builder/form-editor/control-settings#basic-options) dialog.

## How synchronization works

The synchronization takes place from "left" to "right":

* Upon form initialization, unless you specified `sync-on-form-load="none"` (see above), the iterations and values are enforced, which means:
  * Extra iterations in the destination are removed from the end if needed, to match the number of iterations in the source.
  * New iterations in the destination are added at the end if needed, to match the number of iterations in the source.
  * For each iteration, the values of controls specified with `<fr:map>` are copied over.
* When iterations are added in the source:
  * Corresponding iterations are added in the destination, at the same position.
* When iterations are moved in the source:
  * Corresponding iterations are moved in the destination.
* When iterations are removed in the source:
  * Corresponding iterations are removed in the destination.
* When values of controls specified by `<fr:map>` change in the source:
  * The values of the matching controls in the destination are updated.

## Full example

The following example does the following:

* Synchronize repeated iterations between repeated grid `master-grid` and repeated section `detail-section`.
* For each iteration, copy over the value of the `traveler-name` and `traveler-birth-date` form controls.

```xml
    <!-- other Form Builder code here -->

    <fr:synchronize-repeated-content left="master-grid" right="detail-section" version="2018.2">
        <fr:map left="traveler-name"       right="traveler-name-readonly"/>
        <fr:map left="traveler-birth-date" right="traveler-birth-date-readonly"/>
    </fr:synchronize-repeated-content>
    
    <!-- Put `<fr:synchronize-repeated-content>` just above this. -->
</xf:model>
```

The following screenshots show how the form looks like when using the wizard view:

![Master view](/files/-LTYnfaNjlPhVGc1REtm)

![Detail view](/files/-LTYoaUnrRohhrx9eueJ)

## Paging large repeated sections

\[SINCE Orbeon Forms 2019.1]

When paging of large repeated sections is enabled, optionally with a repetition label, each master repetition appears as its own wizard page. This reduces the size of wizard pages and makes it easier to fill out a form.

![Detail view](/files/-Lp5J3_-6nxxGQc-77Ph)

![Detail view](/files/-Lp5J3_1gCqwAEhuRZlK)

See [Paging large repeated sections](/form-runner/features/wizard-view#paging-large-repeated-sections) for detail of the configuration.

## Limitations

With Orbeon Forms 2018.2, the `<fr:synchronize-repeated-content>` has the following limitations:

* The *source* grid or section must be visible for the synchronization to work. The destination, on the other hand, can be hidden.
* Both repeated content must be at the top-level: they cannot be nested within other repeated sections.
* Synchronization within section templates is not supported.

## See also

* [Simple Actions](broken://pages/-LEkBr90Nq9hbV8krZWy)
* [HTTP services](/form-builder/advanced/services-and-actions/http-services)
* [Database services](/form-builder/advanced/services-and-actions/database-services)
* [Repeated grids](/form-builder/form-editor/repeated-grids)
* [Repeated sections](/form-builder/form-editor/section-settings#repeated-content)
* [Paging large repeated sections](/form-runner/features/wizard-view#paging-large-repeated-sections)
* Blog posts
  * [Synchronized master-detail views](https://blog.orbeon.com/2019/01/synchronized-master-detail-views.html)
  * [Paging large repeated sections](https://blog.orbeon.com/2019/09/paging-large-repeated-sections.html)
  * [Repeated sections](https://blog.orbeon.com/2014/01/repeated-sections.html)


# Testing a form in web mode

## How to test a form

The Buttons bar includes a "Test" menu button. Clicking on it opens an overlay with a preview of your form:

![Form Preview overlay](/files/qxr2zUtX7gi6H0bWd0Ex)

Here, many of your form's functions are available, including:

* entering data into fields
* running formulas
* showing the Wizard if it is configured
* running actions and services

You can also [test PDF production](/form-builder/advanced/pdf-test) from this overlay, using the "Test PDF" button.

The following is not available:

* reading data from the database
* saving data
* submitting data
* switching to the View mode and other page navigation

Using the "Close" button takes you back to Form Builder.

At the bottom, you have the [Formulas console](/form-builder/formulas/formulas-console), which shows automatically in case of errors with formulas.

## See also

* [Testing PDF production](/form-builder/advanced/pdf-test)
* [Testing in offline mode](/form-builder/advanced/offline-test)
* [The `open-rendered-format()` action](/form-runner/advanced/buttons-and-processes/actions-form-runner#open-rendered-format)


# Testing PDF production

## Availability

\[SINCE Orbeon Forms 2021.1]

## How to test PDF production

There are two ways to access the "Test PDF" function:

1. directly from the Form Builder's main window's "Test" menu
2. from the "Test Form" window

![The "Test" menu](/files/SagUaMYWBY5WIgyBEANW)

When accessed directly, "Test PDF" shows the following dialog:

![The "Test PDF Production" dialog](/files/Qqy2M3DDJhrEPndRbnnt)

When accessed from the "Test Form" window, "Test PDF" shows the following dialog:

![The "Test PDF Production" dialog](/files/sir38T3kECR5j9J02OsB)

You have access to the following options:

* **Use entered form data**
  * This option only shows when accessed from the "Test Form" window.
  * When selected, any data currently entered in the form will be used to produce the PDF.
* **Disable the evaluation of "Initial Values" formulas**
  * If "Use entered form data" is selected, this option is disabled as the form data takes precedence.
  * When selected, all "Initial Value" formulas are disabled, as if they were not present.
* **Disable the evaluation of "Initial Values" formulas**
  * When selected, all "Initial Value" formulas are disabled, as if they were not present.
* **Disable the evaluation of "Calculated Values" formulas**
  * When selected, all "Calculated Value" formulas are disabled, as if they were not present.
* **Show controls, grids, and sections with dynamic visibility**
  * When selected, all controls, grids, and sections that can be dynamically shown are shown.
  * This means that those with visibility "Yes" or "Formula" are always shown. Controls with visibility "No" remain not visible.
  * Use cases include:
    * Verifying that every control, grid, or section that can appear in the PDF appears correctly.
    * Exporting the PDF file for printing and manual filling.
* **Use PDF template**
  * This only shows if the form has attached PDF templates.
  * See below for details.

Selecting "Continue" opens the resulting PDF, which will show in your browser window.

## PDF templates

The "Test PDF" dialog lets you choose between the [automatic PDF](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/pdf-automatic.md) or one of the available [PDF templates](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/pdf-templates.md) when testing.

![Selecting a PDF template for PDF testing](/files/VxLwDbia98F5Ql4akMgj)

## Showing hints and alerts

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

Two options allow you to show controls hints and alerts in the resulting PDF:

![Options to show hints, alerts, and required](/files/lfwRX6povNWf9uG7O62q)

This is an example of the resulting PDF with hints and alerts:

![PDF output with alerts and hints](/files/chsgljvzNXNjzCszv0kB)

See also [The `open-rendered-format()` action](/form-runner/advanced/buttons-and-processes/actions-form-runner#open-rendered-format).

## See also

* Blog post: [Testing PDF production in Form Builder](https://blog.orbeon.com/2021/11/testing-pdf-production-in-form-builder.html)
* [PDF Production](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/pdf-production.md)
* [Automatic PDF](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/pdf-automatic.md)
* [PDF templates](https://github.com/orbeon/orbeon-forms-doc/blob/master/form-builder/pdf-templates.md)
* [The `open-rendered-format()` action](/form-runner/advanced/buttons-and-processes/actions-form-runner#open-rendered-format)
* [Testing in web mode](/form-builder/advanced/web-test)
* [Testing in offline mode](/form-builder/advanced/offline-test)


# Testing offline functionality

## Availability

\[SINCE Orbeon Forms 2021.1]

## Rationale

Since its inception, Orbeon Forms has had a hybrid architecture for forms:

* the user interface runs in the browser, implemented in JavaScript (and more recently Scala.js)
* the form's logic and validations runs on the server

This architecture has benefits, such as protecting the confidentiality of internal data that never leaves the server.

This is still the case with the Orbeon Forms 2021.1, however we made lots of internal changes to support running Form Runner in a pure JavaScript environment, and we sometimes refer to this mode as the "Offline mode", even though it doesn't always imply being offline. This should, however, in the future, allow running forms entirely offline, as well as embedded within mobile apps.

For API details, see [Form Runner offline embedding API](/form-runner/link-embed/offline-embedding-api).

## Preview

Orbeon Forms allows testing that most aspects of a form work in . From Form Builder, simply use the new "Test Offline" button to see whether your form operates and renders properly in this new mode.

![The "Test Offline" button](/files/dg3BArV298HYicvcmGzb)

When you do this:

* The form definition edited in Form Builder is compiled to a serialized representation.
* The JavaScript-based form runtime is loaded in the Form Builder test window, loads the compiled form, and renders it.

From the user's perspective, this works almost exactly like the "Test" button which has always been present in Form Builder.

As of Orbeon Forms 2023.1, there are limitations, including the following:

* The APIs to compile and embed forms are not fully documented.
* Some controls are not fully supported, including the Formatted Text Area as well as attachment controls.
* Some formulas might not work.
* Performance needs some improvements.
* There is no XML Schema support for datatype validation (although this is rarely used).

## See also

* [Form Runner offline embedding API](/form-runner/link-embed/offline-embedding-api)
* [Testing PDF production](/form-builder/advanced/pdf-test)
* [Testing in web mode](/form-builder/advanced/web-test)


# Email Settings dialog

## Availability

* \[SINCE Orbeon Forms 2018.1] Email Settings dialog in the Form Builder enabling form authors to configure an email template for the current form.
* \[SINCE Orbeon Forms 2022.1] Enhanced Email Settings dialog, with support for multiple email templates, as shown in the screenshots below.
* This is an Orbeon Forms PE feature.

## Purpose

The dialog allows you to configure the content of emails sent by Orbeon Forms. You can use this emailing capability to send a confirmation message to users who submitted the data, or to inform someone within your organization that new data has been submitted and requires processing. The Email Settings dialog allows you to define what the subject and the body of those emails are, and to whom they are sent to using an *email template*. As the name suggests, the email template can have placeholders that get filled when the email is sent, for instance with values entered by users in the form.

## Multiple email templates

\[SINCE Orbeon Forms 2022.1] You can have as many email templates per form as you'd like. This enables you to create different templates, for various languages, or have a template to inform users their data has been received and another template to email the person who needs to process the data. Additionally, if your form is used to implement a workflow, you can have different templates: for example, to notify a user that their request has been accepted or rejected. This opens up numerous possibilities for implementing workflows with Orbeon Forms.

![Email Settings dialog](/files/hl31lLa4Wo9WUkFIwp51)

## Usage

You can open the "Email Settings" by clicking on the "Email Settings" button, under the "Advanced" tab, in the toolbox.

![](/files/-LEkBvjyJnZXF5GahqjH)

* Template name – Each email template must have a *name*. This name is not exposed to end users; it is only for your own use, to understand what each template is for, and to be able to refer to a specific template in the [`email(template = "my-template")` action](/form-runner/advanced/buttons-and-processes/actions-form-runner/actions-form-runner-email).\
  Localization – If your form is available in multiple languages, a "Language" dropdown shows next to the template name, allowing you to select which language this template is for. When an email is sent, the template that corresponds to the user's language is automatically used.
* [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1) Enabling the template using a formula - An optional formula can be entered to allow the template to be conditionally sent or not.
* [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1) Dynamic email headers - Headers can be specified using form control values, formulas, or static texts. Custom headers are also supported (see below).
* Dynamic email subject and body – An email subject or body can be dynamic and include the value of form controls or formulas, as shown in the examples above. For more, see [Template syntax](/form-builder/advanced/template-syntax).
* Attachments - Attachment controls can be included in the email.
* Excluded from All Control Values - It is possible to specify here which controls will be excluded from the "All Control Values" parameter value. See [Template syntax](/form-builder/advanced/template-syntax).

## Custom headers

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

Custom headers can be added to the list of email headers by using the following property:

```xml
<property 
    as="xs:string" 
    name="oxf.fb.email.custom-headers.*.*"
    value="X-Priority X-SMTPAPI"/>
```

The value is a space-separated list of custom header names.

## See also

* Blog post: [Support for multiple email templates in Form Builder](https://blog.orbeon.com/2023/04/support-for-multiple-email-templates-in.html)
* [Template syntax](/form-builder/advanced/template-syntax)


# Field-level encryption

## Availability

* \[SINCE Orbeon Forms PE 2018.2.2]
* \[SINCE Orbeon Forms PE 2019.1] Encryption of attachments is also supported

## Overview

This feature allows form authors to mark certain fields so any data entered by users in those fields is encrypted when stored in the database. This is typically useful for fields used to capture personal information, also referred to as "personally identifiable information" (PII), "sensitive personal information" (SPI), or "personal information," depending on the context. Encrypting such information can help you with compliance with privacy laws, such as GDPR.

## Marking fields for encryption

In Form Builder, to mark a field for encryption, open the "Control Settings" dialog for that field, and click on the "Encrypt data at rest" checkbox, as done in the screenshot below.

![Checkbox to mark field for encryption](/files/-LHG9JANwUU13KLHX8uk)

## Configuration

### Setting the encryption password

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1) Set the `oxf.fr.field-encryption.password` property to the encryption password to be used for field-level encryption.

```xml
<property
	as="xs:string"
	name="oxf.fr.field-encryption.password"
	value="SET THIS PASSWORD"/>
```

{% hint style="warning" %}
The field encryption password must remain stable to ensure that existing encrypted values can be read. If the password is changed or lost, the existing data will no longer be readable.
{% endhint %}

### Changing the encryption password

[\[SINCE Orbeon Forms 2024.1.3\]](/release-notes/orbeon-forms-2024.1.3) You can change the field encryption password using the following procedure:

1. Make a database backup. This is essential, as the following steps will re-encrypt all your data in place, which can result in data loss if something goes wrong during re-encryption.
2. Set `oxf.fr.field-encryption.password.read-fallback` to the current value of `oxf.fr.field-encryption.password`.
3. Set `oxf.fr.field-encryption.password` to the new password.
4. Go to the Forms Admin page and click "Re-encrypt" for the forms containing encrypted fields.
5. Once re-encryption is complete, remove the `oxf.fr.field-encryption.password.read-fallback` property, as the data is now encrypted with the new password.
6. Open existing data that contains encrypted fields to verify that you can read the data with the new password.

```xml
<property
	as="xs:string"
	name="oxf.fr.field-encryption.password"
	value="NEW PASSWORD"/>
<property
	as="xs:string"
	name="oxf.fr.field-encryption.password.read-fallback"
	value="OLD PASSWORD"/>
```

### Upgrading from Orbeon Forms 2022.1 or older

Before Orbeon Forms 2023.1, the general `oxf.crypto.password` property was used instead of `oxf.fr.field-encryption.password`. If you are upgrading from an earlier version of Orbeon Forms to version 2023.1 or newer, make sure to set `oxf.fr.field-encryption.password` to the same value previously used for `oxf.crypto.password`. If you fail to do this, Orbeon Forms will report an error when you try to read or write encrypted data.

Once you have set `oxf.fr.field-encryption.password`, we recommend changing `oxf.crypto.password` to a different value. It is generally safe to change `oxf.crypto.password`, even regularly, as it is not used to encrypt data at rest.

## When encryption happens

When storing form data, encryption is done before it is sent to the implementation of persistence API. When retrieving form data, decryption is done after it is received from the implementation of the persistence API. This means that implementations of the persistence API don't need to worry about encrypting or decrypting data when storing or reading form data. Also, it means that values of fields marked for encryption will be encrypted on the wire on the way to and from your database, further increasing the security of your solution.

<figure><img src="/files/-LHGAGUQAWDdybB39MMZ" alt="" width="520"><figcaption><p>Data encryption is done before it reaches the implementation of the persistence API</p></figcaption></figure>

## Making changes to what needs to be encrypted

Say you edit an existing form definition in Form Builder and change the fields to be encrypted:

* If you publish the form as a new version, then changes apply to new data captured with the new version. (Existing data tied to earlier versions will continue to be encrypted per the earlier versions of those form definitions.)
* If you publish the form overwriting an existing version:
  * When retrieving form data form the persistence API, fields that were encrypted at the time the form data was stored will be decrypted (even if the list of fields to encrypt has changed in the current form definition).
  * When storing form data, fields marked for encryption in the form definition will be encrypted.

This means that, from the perspective of fields encryption, overwriting an existing form definition at publication time doesn't cause any problem. This is because encrypted fields are marked as such in the form data, so Orbeon Forms can decrypt them even if the list of fields to be encrypted has changed since the last time the form data was saved.

However, overwriting an existing form definition at publication time won't re-encrypt existing data. For this to happen, you need to [trigger a re-encryption](/form-runner/form-runner-pages/forms-admin-page#re-encryption) from the Forms Admin page.

## Limitations

1. Currently, the search API isn't able to handle encrypted fields.
   * This has the following consequences:
     * You can't search values of encrypted fields, whether from a [Summary page](/form-builder/summary-page) or using the [search API](/form-runner/api/persistence/search).
     * Neither the Summary page nor the search API is able to show or return the values of encrypted fields (it will instead show/return the encrypted values).
   * Consequently, fields marked for encryption shouldn't be marked to be shown on the Summary page, or to be included in bulk edit.
2. Re-encryption doesn't support changing the encryption key, that is decrypting with a first key (the old key) and encrypting with a second key (the new key). If you need to change the encryption key, for now you need to:
   1. For any published form, with encrypted fields, for which data exists, edit the form to unmark all encrypted fields, and re-publish the form overwriting the relevant version.
   2. From the Form Runner Home page, trigger a re-encryption for all the forms that had encrypted fields.
   3. Change the key in your properties. After this, none of the fields in your form data will be encrypted in the database.
   4. Repeat step 1, but this time marking fields for encryption.
   5. Repeat step 2, after which the relevant fields will be encrypted with the new key.

We expect these limitations to be lifted in future versions of Orbeon Forms.

## See also

* Blog post: [Field-level encryption](https://blog.orbeon.com/2019/04/field-level-encryption.html)
* [Basic Settings](/form-builder/form-editor/control-settings)
* [Re-encryption](/form-runner/form-runner-pages/forms-admin-page#re-encryption)
* [Encryption in the form data format](/form-runner/api/data-formats/form-data#encryption)


# Messages

[\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)

![Messages dialog](/files/jMjF4pr6erdJhj2hVAMN)

## Purpose

Some of the [Form Runner actions](/form-runner/advanced/buttons-and-processes/actions-form-runner) display messages to users. For instance, when a form is saved, a message is displayed to confirm that the form was saved successfully. The Messages dialog allows you to customize those messages directly in Form Builder, instead of using properties such as `oxf.fr.resource.*.*.en.detail.messages.save-success`.

## Usage

You can open the Messages dialog by clicking on the "Messages" button, under the "Advanced" tab, in the toolbox.

![](/files/-LEkBvjyJnZXF5GahqjH)

Each message must have a language, a name, and a value.

The message name can be one of the pre-defined names present in [Form Runner's `resources.xml`](https://github.com/orbeon/orbeon-forms/blob/master/form-runner/jvm/src/main/resources/apps/fr/i18n/resources.xml) (e.g. `save-success` or `upload-error`) or a custom name, which can be entered by selecting "Other".

When a message is defined in the Messages dialog, it overrides any message defined in a property.

## Supported actions

The following [Form Runner actions](/form-runner/advanced/buttons-and-processes/actions-form-runner) take a message as parameter:

* [`confirm`](/form-runner/advanced/buttons-and-processes/actions-form-runner#confirm)
* [`success-message`](/form-runner/advanced/buttons-and-processes/actions-form-runner#success-message-and-error-message)
* [`error-message`](/form-runner/advanced/buttons-and-processes/actions-form-runner#success-message-and-error-message)

For example, you can reference the pre-defined message `save-success` by calling the following action:

```
success-message(resource = "save-success")
```

Or simply:

```
success-message("save-success")
```

## Custom messages

When overriding existing processes or introducing custom processes in the properties, you can also reference custom messages that are not used internally by Form Runner. You can do so by selecting "Other" in the name dropdown and entering a custom name.

For example, you might want to define a custom `email-and-save` process to email and save your form, and display a custom `sent-and-saved` message (defined via the Messages dialog) upon success. This allows you to use a more specific message than the pre-defined `save-success` message.

```xml
<property as="xs:string" name="oxf.fr.detail.buttons.acme.foo">
    summary clear pdf review email-and-save
</property>

<property as="xs:string"  name="oxf.fr.detail.process.email-and-save.acme.foo">
    email(template = "email-template-name")
    then save
    then success-message("sent-and-saved")
</property>

<property
    as="xs:string"
    name="oxf.fr.resource.*.*.en.buttons.email-and-save"
    value="Email &amp; save"/>
```

## See also

* Blog post: [Customizing Form Runner messages directly from Form Builder](https://blog.orbeon.com/2023/10/customizing-form-runner-messages.html)


# Section templates

## Section template libraries

Form Builder supports defining reusable sections called *section templates*.

Section templates are grouped within *section template libraries*. There can be one library per application name. In addition, a global section template library with the `orbeon` application name is available. This library is made available to all form definitions, no matter what their application name is.

## How section templates work

You create, edit, and publish a section template library from Form Builder as you do with a regular form definition. The only difference is that the form name for a section template library is `library`. This is a special name recognized by Form Builder.

When you publish a section template library form definition, all top-level sections in that form definition are made available to other form definitions created in Form Builder, depending on their application name, and appear in the Form Builder toolbox, which shows:

* **Global section templates.**
  * Those are created and updated with the `orbeon/library` form definition.
* **Application section templates.**
  * Those are created in the current application's `library` form definition.
  * For example all form definitions with app name `acme` can use section templates defined in the `acme/library` form definition in addition to the global `orbeon/library` section templates.

The title of the section in the section template library form definition is used as the title of the section template that shows in the toolbox.

For example, you can create a generic "US Address" section and reuse it in multiple forms:

![Defining a section template in Form Builder](/files/-LEkC77FzUho25tFKGNL)

Section templates can contain:

* nested grids, with or without repeats
* nested subsections, with or without repeats
* services and actions (see below)

When you click on a given section template, the section is inserted into the form after the currently selected section. You can then select a new title for the section. It is possible to include a section template more than once.

The section appears with read-only fields (which means that you cannot change properties of the controls once inserted):

![Using section templates in Form Builder](/files/-LEkC77Hav7WAzwGuB1f)

When deploying the form, section templates appear like regular editable sections:

![Section templates in Form Runner](/files/-LEkC77JUg_T2xQZO49q)

\[SINCE Orbeon Forms 2021.1]

In Form Builder, the inserted section templates show, at the top, the following information:

* section template library name
* section template section name
* section template library version

[\[SINCE Orbeon Forms 2023.1.1\]](/release-notes/orbeon-forms-2023.1.1)

When inserting a section template, the following information is copied over to the current form:

* section title
* section help
* custom CSS classes
* Visibility and Readonly formulas for the section itself

Visibility and Readonly formulas are special, as, when edited within the library:

* it usually doesn't make sense for these formulas to refer to control values *within* the section itself
* they can refer to data and form controls from other sections

However, when inserted into a form, these formulas typically do not have access to other sections. As a workaround, they can depend on the enclosing form's data using formulas of the following shape:

```
xxf:instance('|fr-form-instance|')//my-control-name
```

In addition, they can depend on values from known sections using the `fr:control-string-value()` function, for example:

```
fr:control-string-value('my-control-name', false(), 'my-section-name')
```

If possible, the section name from the library is now preserved. If there is already a section with the same name in the enclosing form, a new name is generated.

After insertion, you can modify those settings using the "Section/Grid Settings" dialog.

## Services and actions

### Introduction

Actions involving controls in a given section are automatically included with the section template, along with the services called by the actions.

### Cross-section actions

\[SINCE Orbeon Forms 2021.1]

When editing a library form, you can define actions involving controls present in more than one section. For example, you can define an action that sets the value of a control in one section based on the value of a control in another section, and that is triggered by the activation of a button that is in a third section.

However, when using sections from the library in a form, nothing prevents you from only including one of the sections. What should then happen with cross-section actions?

With Orbeon Forms 2021.1, there is limited support for this kind of scenarios. For each section template:

1. Orbeon Forms gathers actions that:
   * can update at least one control of the current section (for example with "Set control value" result)
   * or update no control at all but are triggered by a control of the current section (for example with a button activation)
2. Gathers the services that are used by the actions kept in step 1.

A resulting section template will include the actions and services gathered above in steps 1 and 2.

Following this logic, a given action or service can get included in multiple sections.

Limitations:

* In general, an action can update controls in multiple sections. If that's the case, then that action will be included in all sections that include the controls that are updated by the action. This means that the action and associated service can run more than once.
* If a section template includes cross-section actions, and the related sections are all included in the destination form, they will be automatically connected. If a section is missing, however, then the behavior might be unexpected. It is up to the form author to ensure that the resulting form includes the required section templates.

\[SINCE Orbeon Forms 2022.1.2]

Actions and related listeners using the [Action Syntax](broken://pages/-LTfNL822m6v3asjBCuO) are also included in section templates.

## Updating section templates

When you open Form Builder, the latest version of the controls and section templates is retrieved from the database and shown in the toolbox.

If you make changes to section templates by modifying and publishing a library form, you must reload the toolbox in your form to reflect the latest changes using the "Reload Toolbox" icon at the top of the toolbox (Orbeon Forms 2017.1 and earlier) or the "Reload Toolbox" button in the "Advanced" tab (Orbeon Forms 2017.2).

![](/files/-LEkBvjyJnZXF5GahqjH)

For more about reloading, see [Reloading the toolbox](/form-builder/form-editor/toolbox#reloading-the-toolbox)

Note that when you publish your form, the section templates *currently* loaded in Form Builder at the time of publishing are included with the published form. This means that changes to section templates after the deployment of a form do not affect the deployed form. If you need to update a deployed form with a new version of controls, you must re-publish the form.

## Versioning of section templates

\[SINCE Orbeon Forms 2019.1]

When [versioning](/form-runner/features/versioning) is available:

* Form Builder allows you to publish multiple versions of a section templates library.
* Form Builder allows you to select which version of a section templates library you want to use in a particular form definition.

For example, if you have published versions 1, 2 and 3 of the `acme` library form, the toolbox shows a dropdown with those versions.

![Version 2 of the section templates library](/files/-LXUm77CZ4gxcOG8JZqq)

When you select a particular version, the toolbox reloads with the latest published form definition with that version.

A section templates library may contain different versions of a given section template, identified by section name, or add or remove section templates compared with other versions of the library.

If the form definition currently being edited is using a section template which is not present in the section template library chosen, the section content appears empty.

For more about reloading, see [Reloading the toolbox](/form-builder/form-editor/toolbox#reloading-the-toolbox)

## Merging section templates

### Availability

\[SINCE Orbeon Forms 2017.2]

### Merging

You can merge section templates into your current form definition with the "Merge Section Template" icon associated with the section:

![Unmerged section template](/files/-LEkC77RFu2I_C7fz1Ks)

After activating the icon, a dialog shows:

![Control Names dialog](/files/-LEkC77TZZ0VoJXA9NK1)

The dialog shows a list of all control names within the section template and how they will be changed after the merge. Since control names are unique within a form definition, two controls cannot have the same name. Names that are available show in green. Names that conflict show in yellow and an automatic name is generated.

You can optionally set a prefix and/or a suffix for all names. This can help prevent automatic generation of names. For example, you could insert a US Address twice:

* first, with all control names prefixed by `shipping-`
* second, with all control names prefixed by `billing-`

![Control Names dialog with prefix](/files/-LEkC7JH1q3eWq5l0ZFd)

*NOTE: The enclosing section name itself is not part of this renaming process, as the section was already part of the containing form definition and therefore its name was already unique.*

After merging the section template, it becomes part of the current form definition and is completely unlinked from the original section template. You can modify the section and its content as if it had been directly created by hand within the form definition:

![Merged section template](/files/-LEkC77ay5wduG7aw4pK)

This also means that if you make changes to the section templates library and reload the toolbox, the merged section template does not update.

### Limitations

Services and actions in the section template are *not* merged into the enclosing form.

## See also

* [Toolbox](/form-builder/form-editor/toolbox)
* [HTTP services](/form-builder/advanced/services-and-actions/http-services)
* [Database services](/form-builder/advanced/services-and-actions/database-services)
* [Simple Actions](broken://pages/-LEkBr90Nq9hbV8krZWy)


# Template syntax

## Availability

\[SINCE Orbeon Forms 2018.1]

Templates apply to:

* Email Settings
  * Subject
  * Body
* Control Settings
  * Label
  * Hint
  * Help Message
  * Explanatory Text \[SINCE Orbeon Forms 2019.1]

## Templates

You define a template by setting a placeholder with the following syntax:

```
{$foo}
```

where `foo` is a name which must match one of the template parameters configured below.

## Template parameters

You can add new parameters with the "+" icon and remove them with the "-" icon.

The following options are available for each template parameter:

<figure><img src="/files/-M4UilXN1QzxU33iH-wo" alt="Template parameter options" width="148"><figcaption><p>Template parameter options</p></figcaption></figure>

* Parameter name: this must be unique among parameters.
* Parameter value:
  * **Control Value:** the value of a form control.
  * **XPath Expression:** calculated expression.
  * **All Control Values:** the value of all form controls.
    * This is only available for the email body.
    * This is experimental as of Orbeon Forms 2018.1.
  * **Links** \[SINCE Orbeon Forms 2020.1]
    * Links are available for the following:
      * Email Body
      * Label
      * Hint
      * Help Message
      * Explanatory Text
    * Links include:
      * Link to the "edit" page
      * Link to the "view" page
      * Link to the "new" page
      * Link to the "summary" page
      * Link to the "home" page
      * Link to the PDF file
  * **Automatic PDF only** [\[SINCE Orbeon Forms 2023.1\]](/release-notes/orbeon-forms-2023.1)
    * Image (form logo by default)
    * Form title
    * Page number
    * Page count

*NOTE: For the email subject and body, an XPath expression runs in the context of the form data's root element. However, the* [*Form Runner function library*](/xforms/xpath/extension-functions/extension-form-runner) *is not yet available to expressions as of Orbeon Forms 2018.1. For labels, hints and help messages, the Form Runner function library is available.*

A template may omit references to any or all template parameters.

## Links

\[SINCE Orbeon Forms 2020.1]

Links are intended to point to some Orbeon Forms pages or resources (namely, the PDF file). The end user might see such links in explanatory text and email bodies, in particular.

<figure><img src="/files/-M4UilXSzCfo29i3G1n1" alt="Links in an email template" width="802"><figcaption><p>Links in an email template</p></figcaption></figure>

To insert the URL, you use the template syntax within the "URL" field of the link dialog:

<figure><img src="/files/-MAgmRGWi2IxhIO9RyYq" alt="Editing a link URL" width="482"><figcaption><p>Editing a link URL</p></figcaption></figure>

Form Runner requires the ability to know how to reach Form Runner. In some cases (use of a reverse proxy), Orbeon Forms cannot determine this automatically. For this purpose, the following property allows setting the external Form Runner URL. By default, it is empty, and can set it to an absolute URL as follows:

```xml
<property 
    as="xs:string"
    name="oxf.fr.external-base-url"
    value="https://orbeon.acme.org/forms"/>
```

You can't use links to point back to Form Runner when using [embedding](/form-runner/link-embed/java-api) or the \[ [Form Runner proxy portlet](/form-runner/link-embed/liferay-proxy-portlet).

## Localization

When the form definition has more than one language:

* Each language has its own localized template.
* Template parameters are not localized and are available no matter what language is selected.

## Examples

In the following example of a dynamic control label, the `$name` variable refers to the subsequent `name` parameter. The Template Parameters section declares that `name` parameter to refer to the `name` control.

<figure><img src="/files/-LImUPKirryB_JmDLxOc" alt="Dynamic label configuration" width="942"><figcaption><p>Dynamic label configuration</p></figcaption></figure>

This is how this would appear in the running form:

<figure><img src="/files/-LImUPKkm1VXRZ4-udcg" alt="Dynamic label at runtime" width="362"><figcaption><p>Dynamic label at runtime</p></figcaption></figure>

In the following example of dynamic email subject, the `$title` and `$author` variables refer also to the subsequent parameters.

<figure><img src="/files/-LFZtEcvvfVnf-hiy65c" alt="Email Subject" width="802"><figcaption><p>Email Subject</p></figcaption></figure>

The text of the template is localized as usual when the form definition has more than one language.

## See also

* [Control settings](/form-builder/form-editor/control-settings)
* [Email settings](/form-builder/advanced/email-settings)


# XML Schemas support

## Availability

This is an [Orbeon Forms PE](https://www.orbeon.com/download) feature.

## Working with an XML Schema

The toolbox's "XML Schema" button under the "Advanced" tab allows you to attach an XML Schema to the form. This serves two purposes:

* The schema is used to validate the form data.
* Simple types present in the schema are made available in the Validation Details dialog.

NOTE: Form Builder does not handle imported/included sub-schemas. If you attempt to attach such a schema, a warning message is displayed.


# Extensibility


# Extension API

## Availability

Since Orbeon Forms 2016.1.

## Introduction

Form Builder exposes the following developer extension API:

* API to add a custom Form Settings tab
* API to add a custom Control Settings tab

Each extension is implemented with an XBL component. The component interacts with the enclosing dialog via events.

## Setting up an XBL component

First, make sure you have chosen a prefix-to-namespace mapping for your components, as explained in [Automatic inclusion of XBL bindings](/xforms/xbl/guide/bindings).

Below, we assume the following example mapping:

```markup
<property
  as="xs:string"
  name="oxf.xforms.xbl.mapping.acme"
  value="http://www.acme.com/xbl"/>
```

In practice, you would probably choose a prefix different from `acme`, and a namespace different from `http://www.acme.com/xbl`.

For more on XBL components, see [XBL documentation](/xforms/xbl).

## Custom Form Settings tab

### Setup

In order to add a custom Form Settings tab, the following property must be set to a non-blank value:

```markup
<property
  as="xs:string"
  name="oxf.fb.extension.form-settings"
  value="acme:form-settings"/>
```

Here, the value `acme:form-settings` refers to the XBL component implementing the custom tab:

* `acme` is the prefix you have mapped with the `oxf.xforms.xbl.mapping.acme` property above
* `form-settings` is the name you give your XBL component (it doesn't have to be `form-settings`)

### The XBL component

You then create the file implementing the component under:

```
WEB-INF/resources/xbl/acme/form-settings/form-settings.xbl
```

Here is a template for the new XBL component:

```markup
<xbl:xbl xmlns:xh="http://www.w3.org/1999/xhtml"
         xmlns:xf="http://www.w3.org/2002/xforms"
         xmlns:xs="http://www.w3.org/2001/XMLSchema"
         xmlns:xxf="http://orbeon.org/oxf/xml/xforms"
         xmlns:fr="http://orbeon.org/oxf/xml/form-runner"
         xmlns:xbl="http://www.w3.org/ns/xbl"
         xmlns:xxbl="http://orbeon.org/oxf/xml/xbl"

         xmlns:acme="http://www.acme.com/xbl">

    <xbl:binding element="acme|form-settings" id="acme-form-settings">
        <xbl:handlers>
            <xbl:handler event="fb-initialize" phase="target">

                <!-- Example: access the form instance root -->
                <xf:var name="root" value="event('form-instance')"/>

                <!-- Further initialization -->

            </xbl:handler>
            <xbl:handler event="fb-apply" phase="target">

                <!-- Example: access the form instance root -->
                <xf:var name="root" value="event('form-instance')"/>

                <!-- Further code to save settings  -->

            </xbl:handler>
        </xbl:handlers>
        <!-- Local models -->
        <xbl:implementation>
            <xf:model>

                <!-- Local instance -->
                <xf:instance>
                    <some-local-instance/>
                </xf:instance>

                <!-- Further model content -->

            </xf:model>
        </xbl:implementation>
        <!-- View template -->
        <xbl:template>
            <xh:div>
                This will appear as the tab's content.
            </xh:div>
        </xbl:template>
    </xbl:binding>

</xbl:xbl>
```

### Responding to events

#### Events dispatched

Form Builder dispatches events to the component, following a predefined lifecycle:

* `fb-initialize` is dispatched to initialize the tab when the dialog shows.
* `fb-apply` is dispatched to save the settings, if any, to the form definition.

Handlers for these events can access the form definition and read from / write to it. Component authors have to be very careful not damaging the form definition in the process.

#### Event parameters

`fb-initialize` and `fb-apply` both take the following parameters:

| Parameter Name  | Type                | Value                                               |
| --------------- | ------------------- | --------------------------------------------------- |
| `form`          | `element(xh:html)`  | root element of the form definition                 |
| `form-instance` | `element(form)`     | root element of the form definition's form instance |
| `form-metadata` | `element(metadata)` | root element of the form definition's form metadata |

## Custom Control Settings tab

### Setup

In order to add a custom Control Settings tab, the following property must be set to a non-blank value:

```markup
<property
  as="xs:string"
  name="oxf.fb.extension.control-settings"
  value="acme:control-settings"/>
```

Here, the value `acme:control-settings` refers to the XBL component implementing the custom tab:

* `acme` is the prefix you have mapped with the `oxf.xforms.xbl.mapping.acme` property above
* `control-settings` is the name you give your XBL component (it doesn't have to be `control-settings`)

### The XBL component

You then create the file implementing the component under:

```
WEB-INF/resources/xbl/acme/control-settings/control-settings.xbl
```

For instance, see [`control-settings.xbl`](https://gist.github.com/orbeon/f327f6b1b336dc5f8be841c86fe08d81), an example of custom Control Settings that lets form authors enter, for each control, a "question identifier", which value is stored in an attribute of the form data, on the element corresponding that corresponds to the current control.

For an example template, see above for `acme:form-settings`.

### Responding to events

#### Events dispatched

Form Builder dispatches the following events to the component:

* `fb-initialize` is dispatched to initialize the tab when the dialog shows.
* `fb-apply` is dispatched to save the settings, if any, to the form definition.

Handlers for these events can access the form definition and read from / write to it. Component authors have to be very careful not damaging the form definition in the process.

#### Event parameters

`fb-initialize` and `fb-apply` both take the following parameters:

| Parameter Name  | Type                | Value                                               |
| --------------- | ------------------- | --------------------------------------------------- |
| `form`          | `element(xh:html)`  | root element of the form definition                 |
| `form-instance` | `element(form)`     | root element of the form definition's form instance |
| `form-metadata` | `element(metadata)` | root element of the form definition's form metadata |
| `data-holders`  | `element()*`        | all data holders for the given control              |

In addition, `fb-initialize` takes the following parameters:

| Parameter Name          | Type        | Value                                             |
| ----------------------- | ----------- | ------------------------------------------------- |
| `original-control-id`   | `xs:string` | original control id, such as `first-name-control` |
| `original-control-name` | `xs:string` | original control name, such as `first-name`       |

In addition, `fb-apply` takes the following parameters:

| Parameter Name | Type        | Value                                  |
| -------------- | ----------- | -------------------------------------- |
| `control-name` | `xs:string` | new control name, such as `first-name` |

Between `fb-initialize` and `fb-apply`, the control name (and id) might have been changed in the dialog by the user. The `original-control-name` and `control-name` parameters reflect that change when needed.


# Integration

## Form Builder URLs

### URL formats

The following URL patterns are followed:

* Summary page: `/fr/orbeon/builder/summary`
* New empty form definition: `/fr/orbeon/builder/new`
* Edit existing form definition: `/fr/orbeon/builder/edit/$id`
* [\[SINCE Orbeon Forms 2025.1\]](/release-notes/orbeon-forms-2025.1) Review existing form definition in read-only mode: `/fr/orbeon/builder/view/$id` (see also [Reviewing a form definition](/form-builder/summary-page#reviewing-a-form-definition))

### Specifying the initial language of the form

\[SINCE Orbeon Forms 4.10]

When navigating to `/fr/orbeon/builder/new`, Form Builder picks as default form language the language of the Form Builder user interface. For example, if the Form Builder language is English, by default the new form's language is set to English as well.

You can override this default by specifying the `fb-language` URL parameter (not to be confused with `fr-language`). For example this sets the language of the form to Italian:

```
/fr/orbeon/builder/new?fb-language=it
```

### Bypassing the New Form dialog

\[SINCE Orbeon Forms 4.10]

When navigating to `/fr/orbeon/builder/new`, Form Builder usually presents the user with the New Form dialog, which asks for at least an app and form name.

![New Form Dialog](/files/-LEkByzD34XkgNzpeiFH)

It is possible to bypass that dialog by providing the following URL parameters:

* `fr-app`: requested app name
* `fr-form`: requested form name
* `fr-title`: requested form title
* `fr-description`: requested form description

The dialog is actually bypassed only if the values passed are valid, specifically:

* `fr-app`
  * must be non-blank and only contain ASCII letters, digits, "-" or "\_"
  * if Form Builder permission are enabled, the app name must be allowed by those permissions (see [Form Builder Permissions](/form-runner/access-control/editing-forms#form-builder-permissions))
* `fr-form`
  * must be non-blank and only contain ASCII letters, digits, "-" or "\_"

In case the app and/or form name are missing or invalid, Form Builder presents the user with the New Form dialog. For example, with the following path:

```
/fr/orbeon/builder/new?fr-app=acme&fr-form=o/rder&fr-title=This+is+a+wonderful+form!
```

![New Form Dialog with Error](/files/-LEkByzJBvA8EbTeCPOR)

## See also

* [Form Runner Integration](/form-runner/architecture-and-integration/integration)
* [Form Builder Permissions](/form-runner/access-control/editing-forms)




---

[Next Page](/llms-full.txt/1)

