Alternatives to Alfresco Share for Building Generic Forms

Alfresco introduced Share, it’s own collaboration portal based on Spring Surf technology and they have a pretty good idea to reduce the coding effort for building forms for your custom data types. It is called Forms Engine. Here we will point out some alternatives to Alfresco Share.

The following diagram pretty much explains the whole idea. You have services on Alfresco side that can return metadata about a particular node for form creation, persist the form, validate the posted values, etc.

In between you have some facilities on Share that can render the form and consume the configuration using this underlying services.

And finally on browser side you have forms engine that can manage the rendered form, client side validations, fancy AJAX interactions, etc.

Alternatives to Alfresco

See the following steps taken from Forms Developer Guide that explains the flow in detail.

  1. The browser requests a page that contains the Form UI Component.
  2. The Form UI Component uses the ConfigService to look for XML form configuration for the item requested.
  3. If configuration was found for the item a list of fields to be displayed is determined and passed to the formdefinitions REST API, if no configuration is found the REST API is called without any parameters.
  4. The formdefinitions REST API calls the JavaScript FormService.
  5. The JavaScript FormService calls the Java FormService. To process the request an appropriate FormProcessor for the item “kind” is located and asked to generate a form definition. During this process one or more Filters may be called to further process the generated form. The REST API receives the form definition and generates a JSON representation.
  6. The Form UI component takes the JSON form definition and combines it with the form configuration retrieved earlier to produce a Form UI template model. The FreeMarker template is then processed to produce the HTML/JavaScript output that is sent back to the browser. The browser executes the returned HTML/JavaScript which instantiates the Forms Runtime object used to manage the generated form.

But Share is not the only option to use if you want something similar. You could continue to use your favorite UI framework and provide the same flexibility.

Take the following webscript as an example, what it does it to dump the metadata of a given node recursively. It outputs everything you need about that particular node, the attributes, the type of the attributes, the relationships, aspects, etc.

It is even better than the one provided Alfresco by default, because this webscript returns all the metadata about a node recursively including all the children nodes which also enables you to build nested forms.

{
<#if classdefs.name?exists>
    "name" : "${shortType(classdefs.name.toString())}",
</#if>

<#if classdefs.title?exists>
    "title" : "${classdefs.title}",
    <#else>
        "title" : "",
</#if>

"parent" : {
<#-- @ftlvariable name="classdefs" type="org.alfresco.service.cmr.dictionary.ClassDefinition" -->
<#if classdefs.parentName?exists>
    "name" : "${shortType(classdefs.parentName.toString())}",
    "title" : "${classdefs.parentName.getLocalName()}",
    "url" : "${"/butterfly/metadata/class/" + shortType(classdefs.parentName.toString())}"
</#if>
},
"defaultAspects" : {
<#if classdefs.defaultAspects?exists>
    <#list classdefs.defaultAspects as aspectdef>
        "${shortType(aspectdef.name.toString())}" : {
        "name" : "${shortType(aspectdef.name.toString())}",
        <#if aspectdef.title?exists>
            "title" : "${aspectdef.title}",
        </#if>
        "url" : "${"/butterfly/metadata/class/" + shortType(aspectdef.name.toString())}"
        }
        <#if aspectdef_has_next>,</#if>
    </#list>
</#if>
},
"properties" : {
<#-- @ftlvariable name="propertydefs" type="org.alfresco.service.cmr.dictionary.ClassDefinition" -->
<#list propertydefs as propertydefs>
    "${shortType(propertydefs.name.toString())}": {
    <#if propertydefs.name?exists>
        "name" : "${shortType(propertydefs.name.toString())}",
    </#if>
    <#if propertydefs.title?exists>
        "title" : "${propertydefs.title}",
    </#if>
    <#if propertydefs.description?exists>
        "description" : "${propertydefs.description}",
    </#if>
    <#if propertydefs.defaultValues?exists>
        "defaultValues" : "${propertydefs.defaultValues}",
        <#else>
            "defaultValues" : "",
    </#if>
    <#if propertydefs.dataType?exists>
        "dataType" : "${shortType(propertydefs.dataType.name.toString())}",
    </#if>
    "multiValued" : ${propertydefs.multiValued?string},
    "mandatory" : ${propertydefs.mandatory?string}
    }
    <#if propertydefs_has_next>,</#if>
</#list>
},
"associations" : {
<#assign flag = false>
<#list assocdefs as assocdefs>
    <#if (assocdefs.isChild()==false)&&(flag== true)>
        <#assign flag = false>,
    </#if>
    <#if assocdefs.isChild() == false>
        <#assign flag=true>
            "${shortType(assocdefs.name.toString())}": {
            <#if assocdefs.name?exists>
                "name" : "${shortType(assocdefs.name.toString())}",
            </#if>
            <#if assocdefs.title?exists>
                "title" : "${assocdefs.title}",
            </#if>
            <#if assocdefs.description?exists>
                "description" : "${assocdefs.description}",
                <#else>
                    "description" : "",
            </#if>
            "source" : {
            <#if assocdefs.getSourceClass().name?exists>
                "class" : "${shortType(assocdefs.getSourceClass().name.toString())}",
            </#if>
            <#if assocdefs.getSourceRoleName()?exists>
                "role" : "${assocdefs.getSourceRoleName().toString()}",
            </#if>
            "mandatory" : ${assocdefs.isSourceMandatory()?string},
            "many" : ${assocdefs.isSourceMany()?string}
            },
            "target" : {
            <#if assocdefs.getTargetClass().name?exists>
                "class" : "${shortType(assocdefs.getTargetClass().name.toString())}",
            </#if>
            <#if assocdefs.getTargetRoleName()?exists>
                "role" : "${assocdefs.getTargetRoleName().toString()}",
            </#if>
            "mandatory" : ${assocdefs.isTargetMandatory()?string},
            "many" : ${assocdefs.isTargetMany()?string}
            }
            }
    </#if>
</#list>
},
"childassociations" : {
<#assign flag = false>
<#list assocdefs as assocdefs>
    <#if (assocdefs.isChild()==true)&&(flag== true)>
        <#assign flag = false>,
    </#if>
    <#if assocdefs.isChild() == true>
        <#assign flag=true>
            "${shortType(assocdefs.name.toString())}": {
            <#if assocdefs.name?exists>
                "name" : "${shortType(assocdefs.name.toString())}",
            </#if>
            <#if assocdefs.title?exists>
                "title" : "${assocdefs.title}",
            </#if>
            <#if assocdefs.description?exists>
                "description" : "${assocdefs.description}",
                <#else>
                    "description" : "",
            </#if>
            "source" : {
            <#if assocdefs.getSourceClass().name?exists>
                "class" : "${shortType(assocdefs.getSourceClass().name.toString())}",
            </#if>
            <#if assocdefs.getSourceRoleName()?exists>
                "role" : "${assocdefs.getSourceRoleName().toString()}",
            </#if>
            "mandatory" : ${assocdefs.isSourceMandatory()?string},
            "many" : ${assocdefs.isSourceMany()?string}
            },
            "target" : {
            <#if assocdefs.getTargetClass().name?exists>
                "class" : "${shortType(assocdefs.getTargetClass().name.toString())}",
            </#if>
            <#if assocdefs.getTargetRoleName()?exists>
                "role" : "${assocdefs.getTargetRoleName().toString()}",
            </#if>
            "mandatory" : ${assocdefs.isTargetMandatory()?string},
            "many" : ${assocdefs.isTargetMany()?string}
            },

            <#if assocdefs.getRequiredChildName()?exists>
                "requiredChildName" : "${assocdefs.getRequiredChildName()}",
            </#if>
            <#if assocdefs.getDuplicateChildNamesAllowed() == true>
                "duplicateChildNameAllowed" : true
                <#else>
                    "duplicateChildNameAllowed" : false
            </#if>

            }
    </#if>
</#list>
}
}

What you have to know is to develop a form builder for your favorite UI framework of your choice. For example I developed a generic form builder for GWT using the output coming from this webscript. You are not bound to Share 🙂

Read More Post