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 repository-data-application 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 YAML definition to the repository-data-application module of the project:
definitions: config: /hippo:namespaces/eforms/form/editor:templates/_default_/root/cluster.options: field.include: [com.example.fields.PasswordField]
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>