Setup
-
Build this plugin with
mvn clean package -DskipTests. A fat jar will be located in the target folder -
Create a folder called
pluginsin your mesh directory if it does not exist yet -
Put the fat jar inside your
pluginsfolder -
Create the folders
plugins/forms,plugins/forms/templates -
Create a file
plugins/forms/config.yml. See the configuration section for more details -
Create handlebars templates in your
plugins/forms/templatesfolder -
To enable mail sending:
-
Configure
mailsettings inconfig.yml -
Add
fromAddressinconfig.yml -
Create templates for usermails and adminmails
-
Configure paths to templates as
adminMailTemplateanduserMailTemplateinconfig.yml(without the .hbs extension) -
Configure
adminMailSubjectanduserMailSubjectinconfig.yml
-
API
A specification of the API can be found in the file api.yaml. You can paste the contents of that file to https://editor.swagger.io/ to get a nice overview.
Configuration
Fields
Example Configuration File
templatesPath: "plugins/forms/templates"
poll:
resultTemplate: poll-results
elements:
custom: com.acme.CustomElement
mail:
hostname: localhost
port: 25
authMethods:
username:
password:
ssl: false
trustAll: false
keepAlive: true
maxPoolSize: 10
ownHostname:
attachFileUploads: false
fromAddress: "webmaster@gentics.com"
adminMailTemplate: "adminemail"
adminMailSubject: "Formular [[FORMNAME]] ausgefΓΌllt"
userMailTemplate: "useremail"
userMailSubject: "Herzlichen Dank"
defaultExportFormat: CSV
reCaptcha:
verificationUrl: "your-verification-url"
siteKey: "your-site-key"
secretKey: "your-secret-key"
friendlyCaptcha:
siteKey: "your-site-key"
secretKey: "your-secret-key"
verificationUrl: "your-verification-url"
proxyOptions:
host: proxy.acme.com
port: 4711
user: proxyuser
password: proxypassword
active: false
fileUpload:
allowedFileTypes:
all:
- ".*"
image:
- "image/.*"
audio:
- "audio/.*"
video:
- "video/.*"
allowedFileNames:
archive:
- ".*\\.zip"
- ".*\\.rar"
- ".*\\.7z"
- ".*\\.bz"
- ".*\\.bz2"
- ".*\\.gz"
documents:
- ".*\\.doc"
- ".*\\.docx"
- ".*\\.xls"
- ".*\\.xlsx"
- ".*\\.pdf"
- ".*\\.txt"
- ".*\\.ods"
- ".*\\.odt"
defaultAllowed:
- "all"
custom:
test: "test"
testobject:
testfield: "another test field"
Default Elements
Common properties
| Key | Type | Description | Default value |
|---|---|---|---|
|
string |
Field name |
REQUIRED |
|
string |
Field type (see the list of element types below) |
REQUIRED |
|
string |
Field label. Localized labels and the rendering subtype are resolved via the formβs |
|
|
boolean |
Flag to activate/deactivate fields. Deactivated fields will not be rendered in the form any more (but previously POSTed data is still available) |
|
|
boolean |
Flag for multivalue fields |
|
|
boolean |
Flag for mandatory field |
|
|
validation |
Validation rules for POSTed data, as a nested object (see Server-side Validation) |
|
Every element may have arbitrary additional properties (collected into the elementβs properties map), which will only be used for rendering.
Available element types
The plugin ships with the following built-in element types:
-
string -
number -
boolean -
datetime -
time -
binary -
catalog -
usermail -
adminmail
Registering custom elements
Element types are discovered automatically at class-load time: the ElementTypeRegistry scans the package com.gentics.mesh.plugin.forms.model for classes annotated with @FormElementType and maps the declared JSON type name(s) to their implementation class. No entry in config.yml is required.
To add a custom element type:
-
Create a class in the
com.gentics.mesh.plugin.forms.modelpackage that extendscom.gentics.mesh.plugin.forms.model.FormElement. -
Annotate it with
@FormElementType(β¦β), passing the JSON type name(s) the class should handle.
package com.gentics.mesh.plugin.forms.model;
@FormElementType("mytype")
public class MyElement extends FormElement {
// ...
}
A single class can handle multiple type names by passing an array, e.g. @FormElementType({"c1", "c2"}). If two classes claim the same type name, the last one discovered wins.
Server-side Validation
Server-side validation will check whether the user has filled mandatory fields. Furthermore, entered values will be checked against the fieldβs validation object. The validation property is a nested object with the following keys:
| Key | Type | Description |
|---|---|---|
|
integer |
Value must consist of digits and be greater than or equal to |
|
integer |
Value must consist of digits and be less than or equal to |
|
integer |
Value strings must have at least length |
|
integer |
Value strings must have at most length |
|
object |
Object with a single key |
|
object |
Upload restrictions for |
The binary object inside validation configures upload restrictions for binary elements:
| Key | Type | Description |
|---|---|---|
|
integer |
Maximum allowed file size in MB |
|
array of strings |
Allowed MIME-type regexes (validated against the detected content type) |
|
array of strings |
Allowed file-name regexes (validated against the file name) |
|
array of strings |
Keys referencing the configured |
Example of a validation object:
{
"minLength": 3,
"maxLength": 50,
"regexValidation": { "regex": "^[A-Za-z ]+$" }
}
Error message translations
The following translations must be available for validation error messages (set via the POST /translations endpoint):
| Key | Params | Description |
|---|---|---|
|
- |
Title for missing mandatory fields. |
|
Field label |
Error message for missing mandatory fields. |
|
- |
Title for custom validation. |
|
Field name, invalid value |
Error message for custom validation violation. |
|
- |
Title for unsafe value. |
|
Invalid field name |
Error message for unsafe value. |
|
- |
Title for minimum value error message. |
|
Value |
Error message for minimum value violation. |
|
- |
Title for maximum value error message. |
|
Value |
Error message for maximum value violation. |
|
- |
Title for minimum length error message. |
|
Value |
Error message for minimum length violation. |
|
- |
Title for maximum length error message. |
|
Value |
Error message for maximum length violation. |
|
- |
Title for pattern error message. |
|
Value |
Error message for regular expression violation. |
|
- |
Title for file size error message. |
|
Filename, filesize |
Error message for file size violation. |
|
- |
Title for file type error message. |
|
Filename |
Error message for file type violation. |
|
- |
Title for invalid email error message. |
|
Error message for invalid email. |
ReCaptcha
The plugin supports Google reCAPTCHA (v2). On submission the plugin reads the captcha response token from the configured HTTP request header (responseTokenKey) and verifies it against the verification URL (secret + response are POSTed, and the response must contain success: true). Mandatory settings for reCAPTCHA to work are siteKey and secretKey.
Configuration Options
These are the options available for the reCAPTCHA implementation:
| Key | Type | Description | Default value |
|---|---|---|---|
|
string |
Site key from reCAPTCHA |
empty |
|
string |
Secret key from reCAPTCHA |
empty |
|
string |
Name of the HTTP request header carrying the captcha response token |
|
|
string |
URL against which the captcha response is verified |
Friendly Captcha
Mandatory settings for the friendlyCaptcha to work are siteKey and secretKey
Configuration Options
These are the options available for the FriendlyCaptcha Implementation
| Key | Type | Description | Default value |
|---|---|---|---|
|
string |
Key from the FriendlyCaptcha |
empty |
|
string |
Secret from FriendlyCaptcha |
empty |
|
string |
Key for the field containing the hash of the verification |
frc-captcha-solution |
|
string |
URL against which the captcha is verified |
|
|
boolean |
Activates recommended behaviour as mentioned here |
false |
Endpoint
The endpoint for the EU which is mentioned here must be activated and the Worldwide one can be deactivated, so check that before using Friendlycaptcha.
Poll rendering
The main handlebars template for rendering poll results is by default <templatesPath>/poll-results.hbs.
The handlebars helper pollResultTemplate will resolve the handlebars partial for a poll result element analogously to the formsElementTemplate helper will resolve partials for normal form elements. Partials for poll result templates must be located in <templatesPath>/poll-results. A dedicated poll-result partial is only resolved for the boolean (checkbox) and catalog element types; all other element types fall back to the generic poll-result partial.
Extending
For extending the forms-plugin, a new maven project must be created, that uses the forms-plugin as dependency and builds a custom mesh-plugin, containing the forms-plugin together with the custom extension classes.
Example pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gentics</groupId>
<artifactId>custom-forms-plugin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<mesh.version>0.31.4</mesh.version>
<vertx.version>3.5.4</vertx.version>
<forms.plugin.version>1.6.16</forms.plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>com.gentics.mesh</groupId>
<artifactId>mesh-plugin-api</artifactId>
<version>${mesh.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.gentics.mesh</groupId>
<artifactId>mesh-rest-client</artifactId>
<version>${mesh.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-templ-handlebars</artifactId>
<version>${vertx.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-mail-client</artifactId>
<version>${vertx.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.gentics.mesh.plugin.commercial</groupId>
<artifactId>mesh-forms-plugin</artifactId>
<version>${forms.plugin.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Verticle>service:com.gentics.mesh.plugin.plugin-service</Main-Verticle>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>gentics.nexus.releases-oss</id>
<name>Gentics Nexus OSS Maven Repository</name>
<url>https://repo.gentics.com/repository/maven-releases-oss/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>gentics.nexus.releases</id>
<name>Gentics Nexus Maven Repository</name>
<url>https://repo.gentics.com/repository/maven-releases/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>gentics.nexus.snapshots</id>
<name>Gentics Snapshots Repository</name>
<url>https://repo.gentics.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>
Example src/main/resources/mesh-plugin.json
{
"name": "Custom Forms Plugin",
"apiName": "forms",
"description": "Custom Plugin for management of forms",
"license": "Commercial",
"inception": "2019-05-14",
"author": "Gentics",
"version": "1.0"
}
Custom Elements
-
Custom elements can be implemented by extending class
com.gentics.mesh.plugin.forms.model.FormElement -
Mapping for custom element types to classes must be added to
elementsin the configuration fileconfig.yml
Handlebars helpers
The following helpers are available for rendering mail bodies or HTML markup:
-
formsElementTemplate ELEMENT_TYPE: returns the path to the template partial for the given type (when rendering forms) -
pollResultTemplate ELEMENT_TYPE: returns the path to the template partial for the given type (when rendering poll results) -
formsInputType INPUT_ELEMENT: returns the suggested input type for the given element -
formatDate DATE TARGET_FORMAT [SOURCE_FORMAT]: returns the given date formatted with TARGET_FORMAT. When no SOURCE_FORMAT is specified, the input is assumed to be in the form YYY-MM-DD. -
formatTime TIME TARGET_FORMAT [SOURCE_FORMAT]: same asformatDatefor a time. When no SOURCE_FORMAT is specified the input is assumed to be in the form HH:mm. Note that bothformatDateandformatTimecan be used to format with any pattern parsable by the DateTimeFormatter class. The distinct helpers are just provided with different default source formats for convenience reasons. -
startsWith checks if string variable starts with match string
{{#startsWith element.name 'hidden_element'}}
<!-- true action here -->
{{else}}
<!-- false action here -->
{{/startsWith}}
Data structure
The internal data structure in Mesh consists of the following nodes for every project, where the forms-plugin is activated.
.
βββ "Forms Plugin" (forms_plugin)
βββ "Form #1" (form)
β βββ "Data" (form_folder)
β β βββ Form Post Data (form_[uuid])
β β βββ ...
β βββ "Downloads" (form_folder)
β βββ "form-export_Form #1_2023-10-16_081237" (form_download)
β βββ ...
βββ "Form #2" (form)
β βββ ...
βββ "Form #3" (form)
βββ ...
The forms plugin uses the following schemas:
| Schema | Description |
|---|---|
|
Root node of the data structure. Contains the languages, the forms plugin uses for the project. |
|
Schema for form definitions. Contains the JSON definition of the form and is container for form specific data. |
|
Schema for containers (children of |
|
Schema for POSTed form data for a specific form. The form uuid is used as part of the schema name. Nodes will be created under the "Data" |
|
Schema for downloads, which either contain the uploaded binaries, or exports of POSTed data. Nodes will be created under the "Downloads" |
