Creating custom form fields - BloomReach Experience - Open Source CMS

This article covers a Hippo CMS version 11. There's an updated version available that covers our most recent release.

04-07-2016

Creating custom form fields

Enterprise Forms support custom form field types. This page shows what components are needed to add a custom "password field" to a project. You can add any other custom field in a similar way. In this example we assume you have a project called "My Project" with a JCR namespace "myproject".

Add field definition to the CMS's content CND file

The JCR node type for the custom form field will be defined within the project's namespace in the CND file located in the 'bootstrap-configuration' module.

At the top of the CND file, declare the Enterprise Forms 'eforms' namespace so it can be referenced:

<eforms='http://www.onehippo.org/jcr/eforms/nt/1.0'>

At the bottom of the CND file, add the new node type: 

[myproject:passwordfield] > eforms:textfield
- myproject:length (long) = "40" autocreated

Create the field's model on the CMS side

Add a model class for the new form field to the CMS module:

import javax.jcr.RepositoryException;

import org.hippoecm.frontend.model.JcrNodeModel;

import com.onehippo.cms7.eforms.cms.model.AbstractFieldModel;

public class PasswordFieldModel extends AbstractFieldModel {

    private long length;

    public PasswordFieldModel(JcrNodeModel nodeModel) throws RepositoryException {
        super(nodeModel);
    }

    @Override
    public String getFieldType() {
        return "password";
    }

    public long getLength() {
        return length;
    }

    public void setLength(final long length) {
        this.length = length;
    }

}

Create the field's panel for in the drag and drop area

Create a Wicket panel for the drag and drop area of the form editor and add it to the CMS module:

import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.form.PasswordTextField;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.resource.CssResourceReference;
import org.apache.wicket.request.resource.ResourceReference;

import com.onehippo.cms7.eforms.cms.model.AbstractFieldModel;
import com.onehippo.cms7.eforms.cms.panels.AbstractFieldPanel;

public class PasswordFieldPanel extends AbstractFieldPanel implements IHeaderContributor {

    private static final ResourceReference PASSWORD_CSS = new CssResourceReference(PasswordFieldPanel.class,
            "PasswordFieldPanel.css");

    public PasswordFieldPanel(String id, AbstractFieldModel fieldModel) {
        super(id, fieldModel);

        PasswordFieldModel model = (PasswordFieldModel) fieldModel;
        PasswordTextField textField = new PasswordTextField("field");
        textField.add(new AttributeAppender("size", new Model(model.getLength()), " "));
        textField.setEnabled(false);
        add(textField);
    }

    @Override
    public void renderHead(final IHeaderResponse response) {
        response.render(CssHeaderItem.forReference(PASSWORD_CSS));
    }

}

Also add the corresponding HTML file PasswordFieldPanel.html to the same Java package:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd"
      xml:lang="en"
      lang="en">

<wicket:extend>
  <input type="password" class="textfield" wicket:id="field"/>
</wicket:extend>

</html> 

You can also add a CSS file PasswordFieldPanel.css to the same Java package for any custom styling of the panel.

Create a properties panel for the CMS

Create a Wicket properties panel for editing the field's properties, and add it to the CMS module:

import javax.jcr.RepositoryException;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.StringResourceModel;

import com.onehippo.cms7.eforms.cms.form.FormPlugin;
import com.onehippo.cms7.eforms.cms.model.AbstractFieldModel;
import com.onehippo.cms7.eforms.cms.model.SingleValuePropertyModel;
import com.onehippo.cms7.eforms.cms.properties.panels.AbstractFieldPropertiesPanel;

public class PasswordFieldPropertiesPanel extends AbstractFieldPropertiesPanel {

    public PasswordFieldPropertiesPanel(final String id, final FormPlugin formPlugin,
            final AbstractFieldModel fieldModel) throws RepositoryException {
        super(id, formPlugin, fieldModel);

        Label lengthLabel = new Label("length", new StringResourceModel("password.length", this, null));
        TextField<String> lengthField = new TextField<String>("length-field", new SingleValuePropertyModel<String>(
                fieldModel.getNodeModel(), "myproject:length"));
        lengthField.add(new OnChangeAjaxBehavior() {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                target.add(formPlugin);
            }
        });

        add(lengthLabel);
        add(lengthField);
    }
}

Also add the corresponding HTML file PasswordFieldPropertiesPanel.html to the same Java package:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.4-strict.dtd"
      xml:lang="en"
      lang="en">

<wicket:extend>
  <div class="property length">
    <span class="label" wicket:id="length">[HINT]</span><br/>
    <input type="text" wicket:id="length-field"/>
  </div>
</wicket:extend>

</html> 

Also add the corresponding properties file PasswordFieldPropertiesPanel.properties to the same Java package:

field.type.password=Password
password.length=Render Size 

The form field on the CMS side

The Field interface must be implemented for the new form field. All the field's classes and some additional metadata is accessed through this class. Add it to the project's CMS module. 

import org.apache.wicket.request.resource.PackageResourceReference;
import org.apache.wicket.request.resource.ResourceReference;

import com.onehippo.cms7.eforms.cms.fields.Field;
import com.onehippo.cms7.eforms.cms.model.AbstractFieldModel;
import com.onehippo.cms7.eforms.cms.panels.AbstractFieldPanel;
import com.onehippo.cms7.eforms.cms.properties.panels.AbstractFieldPropertiesPanel;

public class PasswordField implements Field {

    @Override
    public String getId() {
        return "password";
    }

    @Override
    public String getName() {
        return "Password Field";
    }

    @Override
    public String getNodeName() {
        return "myproject:passwordfield";
    }

    @Override
    public ResourceReference getIcon() {
        return new PackageResourceReference(PasswordField.class, "password.png");
    }

    @Override
    public Class<? extends AbstractFieldModel> getModel() {
        return PasswordFieldModel.class;
    }

    @Override
    public Class<? extends AbstractFieldPanel> getPanel() {
        return PasswordFieldPanel.class;
    }

    @Override
    public Class<? extends AbstractFieldPropertiesPanel> getPropertiesPanel() {
        return PasswordFieldPropertiesPanel.class;
    }
}

You can add an icon for the new field in the picker by adding a file 'password.png' to the same Java class.

Alter the CMS's pom.xml to include the field's resources

There are field resources that need to be included in the package. Add the following to the CMS pom.xml in the <build> section

<resources>
  <resource>
    <filtering>false</filtering>
    <directory>${basedir}/src/main/java</directory>
    <includes>
      <include>**/*.html</include>
      <include>**/*.css</include>
      <include>**/*.png</include>
      <include>**/*.gif</include>
      <include>**/*.js</include>
      <include>**/*.properties</include>
    </includes>
  </resource>
  <resource>
    <filtering>false</filtering>
    <directory>${basedir}/src/main/resources</directory>
  </resource>
</resources>

Add the field to Enterprise Forms plugin configuration

The new form field must be added to the Enterprise Forms configuration in the repository. Add the following XML to the file 'hippoecm-extension.xml' in the 'bootstrap-configuration' module of the project:

<sv:node sv:name="hippo-namespace-eforms-add-fields">
  <sv:property sv:name="jcr:primaryType" sv:type="Name">
    <sv:value>hippo:initializeitem</sv:value>
  </sv:property>
  <sv:property sv:name="hippo:sequence" sv:type="Double">
    <sv:value>31001.1</sv:value>
  </sv:property>
  <sv:property sv:name="hippo:contentroot" sv:type="String">
    <sv:value>/hippo:namespaces/eforms/form/editor:templates/_default_/root/cluster.options/field.include</sv:value>
  </sv:property>
  <sv:property sv:name="hippo:contentpropset" sv:type="String">
    <sv:value>com.example.fields.PasswordField</sv:value>
  </sv:property>
</sv:node> 

Create a bean for the field in the site

import org.hippoecm.hst.content.beans.Node;

import com.onehippo.cms7.eforms.hst.beans.AbstractFieldBean;
import com.onehippo.cms7.eforms.hst.beans.FieldType;

@Node(jcrType = "myproject:passwordfield")
public class PasswordFieldBean extends AbstractFieldBean {

    public Long getLength() {
        final Long length = getProperty("myproject:length");

        if (length == null) {
            return 0L;
        }

        return length;
    }

    @Override
    public FieldType getType() {
        return FieldType.TEXT;
    }
}

Don't forget to add the package to the 'hst-beans-annotated-classes' context parameter in web.xml:

  <context-param>
    <param-name>hst-beans-annotated-classes</param-name>
    <param-value>
      classpath*:org/onehippo/**/*.class
      ,classpath*:com/onehippo/**/*.class
      ,classpath*:com/example/common/beans/**/*.class
    </param-value>
  </context-param> 

Create a field for the site

import com.example.common.beans.PasswordFieldBean;
import com.onehippo.cms7.eforms.hst.model.AbstractField;
import com.onehippo.cms7.eforms.hst.model.Form;

public class PasswordField extends AbstractField {

    private Long length;

    public PasswordField(PasswordFieldBean bean, Form form) {
        super(bean, form);
        this.length = bean.getLength();
    }

    public long getLength() {
        return length;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("PasswordField");
        sb.append("{length=").append(length);
        sb.append('}');
        return sb.toString();
    }

}

Add the field to the form's JSP template (eformsrenderfield.tag)

You can use the field's getType() to determine its type. See the following example.

  <c:when test="${field.type eq 'passwordfield'}">
    <div class="eforms-field">
      <label><c:out value='${field.label}'/><span class="eforms-req"><c:out value='${field.requiredMarker}'/></span></label>
      <input type="password" name="${field.formRelativeUniqueName}" class="${field.styleClass}"
             <c:if test='${field.length gt 0}'>size="${field.length}"</c:if> />
      <span class="eforms-hint"><c:out value='${field.hint}'/></span>
    </div>
  </c:when>
Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?