Dashboard > Community Wiki > ... > Controls overview > Creating a custom control
Creating a custom control Log In View a printable version of the current page.

Added by Daniel Natali , last edited by Boris Kraft on Aug 23, 2008  (view change)
Labels: 

We usually create a custom control by reusing existing ones and grouping it all together. Magnolia provides a lot of standard controls which we may use. Edit, checkbox, list, combos, hidden, etc. 

We will create a custom control which represents a table of fields/properties that allow the editor to set some behavior on each input control that will be display to the final user.

Let suppose we have a paragraph that displays to the user a registration form. We will need the username, the password, city, state, the zip code, the Full Name, maybe the phone number, etc. We want the magnolia developer to configure what fields the dialog will display, and the editor will enter the field labels, which ones are required, and maybe a default value. On the other hand, we want this dialog to be reuse across the web site, so, maybe we will use the same control on a purchase form with different fields.

 

 
First of all, we will create the Class that represents a row on the table (the field), we will call it FormElement.

public class FormElement {

	 private String name;
         private boolean hide;
         private boolean mandatory;
         private String label;
         private String defaultValue;
         // getters and setters...
}

Next, we'll create the DialogFormElementControl class that will have the responsibility of rendering the custom control on the paragraph dialog. This class extends the DialogBox Magnolia class and contains a list of our FormElement class.

public class DialogFormElementControl extends info.magnolia.cms.gui.dialog.DialogBox
{
  protected Logger log = LoggerFactory.getLogger(this.getClass());
  private List<FormElement> formElements = new ArrayList<FormElement>();
}

We need to register the new control on Magnolia Console. Go to configuration -> modules -> adminInterface -> controls, and register the new class with a name, in this case formElement.
 
 
As we mention before, we want the developer to add the control to the Dialog and provide which fields should the editor configure. So, on the selected dialog, we'll create a reference to the control, and add a content node with the list
of options describing the custom fields. We will use the same technique as the select control.

On the tabElement, we create a content node named "elements", we'll provide the following information:

controlType: formElement

description: Enter the information for the form elements

label: Form Elements

name: formElement

Also, we'll create a content node named "options", which will have all the fields (as content nodes) that the control will display on the dialog. In each option, we create a node data with the name value. We can put here any property we want the developer to have control.
 

 
Now we have to change the created DialogFormElementControl to add the read options logic and render the HTML for the dialog.

The following method reads the option nodes for the configured dialog.

@SuppressWarnings("unchecked")

protected Collection getOptionNodes(Content configNode) throws PathNotFoundException, RepositoryException, AccessDeniedException
{

Content optionsNode = null;

if (configNode.hasContent("options"))
{
  optionsNode = configNode.getContent("options");
}
else {
   String repository = getConfigValue("repository", "website");
   String path = getConfigValue("path");

   if (StringUtils.isNotEmpty(path))
     optionsNode = ContentUtil.getContent(repository, path);
}

   if (optionsNode \!= null)
     return ContentUtil.getAllChildren(optionsNode);
   else
    return new ArrayList();
}

 
 
The following method creates each FormElement that will be used to iterate and create the HTML table for the dialog.  While we are iterating over the options, we will need to load the saved values if this is not the first time the editor enters the dialog. This is done, by calling the super method getStorageNode().

@SuppressWarnings("unchecked")

public void setOptions(Content configNode) {

    FormElement element;
    try
    {
       for (Iterator it = getOptionNodes(configNode).iterator(); it.hasNext(); formElements.add(element)) {
           Content n = (Content) it.next();
           element = new FormElement();
           String name = NodeDataUtil.getString(n, "name");
           String labelValue =  NodeDataUtil.getString(this.getStorageNode(),"label_" + name);
           String mandatoryValue =  NodeDataUtil.getString(this.getStorageNode(),"mandatory_" + name);
           String defaultValue =  NodeDataUtil.getString(this.getStorageNode(),"default_" + name);
           String hideValue =  NodeDataUtil.getString(this.getStorageNode(),"hide_" + name);

           element.setName(name);
           element.setLabel(labelValue);
           element.setDefaultValue(defaultValue);
           element.setHide(Boolean.parseBoolean(hideValue));
           element.setMandatory(Boolean.parseBoolean(mandatoryValue));
       }

    } catch (RepositoryException e) {

      if (log.isDebugEnabled())
         log.debug("Exception caught: " + e.getMessage(), e);
    }
}

 
 
We must overwrite the init method, to call the setOptions() method on this class.
 

@Override
 public void init(HttpServletRequest request, HttpServletResponse response, Content storageNode, Content configNode) throws RepositoryException {

    super.init(request, response, storageNode, configNode);
    setOptions(configNode);
}

Also, we must overwrite the drawHtml method which provides the actual HTML that will be rendered on the dialog. Here, we define a helper method to draw the table

public void drawHtml(Writer out) throws IOException {
    drawHtmlPre(out);
    out.write(drawTable());
    drawHtmlPost(out);
}

The drawTable method is responsible for creating the table iterating over all FormElements which were created on the setOptions method. To render the checkbox or the input text, we will reuse the Magnolia components and call it's getHtml() method for each one. You might used a velocity template or any other framework that creates the HTML for you. In this case, I choose to write the HTML directly on the class.

private String drawTable() {

		StringBuffer sb = new StringBuffer();

		sb.append("<table border=\"1\" cellpadding=\"0\" cellspacing=\"0\">");

		sb.append("<thead>");
		sb.append("<tr>");
		sb.append("	<th>Order</th>");
		sb.append("<th>Label Name</th>");

		sb.append("<th>Text to be shown on website</th>");
		sb.append("<th>Hide?</th>");
		sb.append("<th>Mandatory?</th>");
		sb.append("<th>Predefined Value</th>");
		sb.append("</tr>");
		sb.append("</thead>");

		int order = 1;

		for (FormElement element : this.formElements) {

			sb.append("<tr>");
			sb.append("	<td>");
			sb.append(order++);
			sb.append("	</td>");
			sb.append("	<td>");
			sb.append(element.getName());
			sb.append("	</td>	");
			sb.append("	<td>");

			Edit edit = new Edit();
			edit.setName("label_" + element.getName());
			edit.setValue(element.getLabel());
			sb.append(edit.getHtml());
			sb.append("	</td>");

			sb.append("	<td>");

			Button hideCheckbox = new Button();
			hideCheckbox.setName("hide_" + element.getName());
			hideCheckbox.setButtonType(ButtonSet.BUTTONTYPE_CHECKBOX);
			hideCheckbox.setValue("true");
			hideCheckbox.setLabel("");

			if (element.isHide()) {
				hideCheckbox.setState(Button.BUTTONSTATE_PUSHED);
			}

			sb.append(hideCheckbox.getHtml());

			sb.append("	</td>");
			sb.append("	<td>");

			Button mandatoryCheckbox = new Button();
			mandatoryCheckbox.setButtonType(ButtonSet.BUTTONTYPE_CHECKBOX);
			mandatoryCheckbox.setName("mandatory_" + element.getName());
			if (element.isMandatory()) {
				mandatoryCheckbox.setState(Button.BUTTONSTATE_PUSHED);
			}

			mandatoryCheckbox.setValue("true");
			mandatoryCheckbox.setLabel("");
			sb.append(mandatoryCheckbox.getHtml());

			sb.append("	</td>");
			sb.append("	<td>");

			Edit defaultEdit = new Edit("default_" + element.getName(),
					StringUtils.defaultIfEmpty(element.getDefaultValue(), ""));

			sb.append(defaultEdit.getHtml());
			sb.append("	</td>");
			sb.append("</tr>");

		}

		sb.append("</table>");

		return sb.toString();
}

Once we have the class written, Magnolia will write the HTML on the dialog and it will vbe presented to the editor. We can use the following taglib from Magnolia which provides access to the storage Node of that dialog to create the logic on the JSP paragraph page.

<cms:out nodeDataName="label_city"/>
<cms:out nodeDataName="mandatory_city"/>

That's all.
Hope this is useful.

Powered by a free Atlassian Confluence Open Source Project License granted to Magnolia International. Evaluate Confluence today.
Powered by Atlassian Confluence 2.7, the Enterprise Wiki. Bug/feature request - Atlassian news - Contact administrators