Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  1. Simply open $OFBIZ_HOME/plugins/ofbizDemo/widget/OfbizDemoScreens.xml file from the ofbizDemo plugin (you just created)


    Code Block
    languagexml
    <?xmlversion="1.0"encoding="UTF-8"?>
    <screens xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://ofbiz.apache.org/Widget-Screen" xsi:noNamespaceSchemaLocationschemaLocation="http://ofbiz.apache.org/Widget-Screen http://ofbiz.apache.org/dtds/widget-screen.xsd">
        <screen name="main">
            <section>
                <actions>
                    <set field="headerItem" value="main"/><!-- this highlights the selected menu-item with name "main" -->
                </actions>
                <widgets>
                    <decorator-screen name="OfbizDemoCommonDecorator" location="${parameters.mainDecoratorLocation}">
                        <decorator-section name="body">
                            <label text="Hello World!! :)"/>
                        </decorator-section>
                    </decorator-screen>
                </widgets>
            </section>
        </screen>
    </screens>

    We have only added the <label text="Hello World!! :)" />

  2. Now you will need to restart OFBiz by reloading data($ ./gradlew loadAll ofbiz). It's required as you have created a new component with some security data for your component (Setup by default in your component data directory as OfbizDemoSecurityGroupDemoData.xml) and as you will restart it, the ofbizdemo component will also be loaded.
  3. As OFBiz restarted direct your browser to your application here https://localhost:8443/ofbizDemo
  4. You will be asked to log in. Login with user: admin password: ofbiz.
  5. As you log in you will see the ofbizdemo application up with the hello world message you have put on screen as shown below the given image.
    That's it, congratulations your first component is set up and running.

...

Code Block
languagexml
titleOfbizDemoForms.xml
<?xml version="1.0" encoding="UTF-8"?>
<forms xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://ofbiz.apache.org/Widget-Form" xsi:noNamespaceSchemaLocationschemaLocation="http://ofbiz.apache.org/Widget-Form http://ofbiz.apache.org/dtds/widget-form.xsd">
 
    <form name="AddOfbizDemo" type="single" target="createOfbizDemo">
        <!-- We have this utility in OFBiz to render form based on service definition. 
             Service attributes will automatically lookedup and will be shown on form
        -->
        <auto-fields-service service-name="createOfbizDemo"/>
        <field name="ofbizDemoTypeId" title="${uiLabelMap.CommonType}">
            <drop-down allow-empty="false" current-description="">
                <!---We have made this drop down options dynamic(Values from db) using this -->
                <entity-options description="${description}" key-field-name="ofbizDemoTypeId" entity-name="OfbizDemoType">
                    <entity-order-by field-name="description"/>
                </entity-options>
            </drop-down>
        </field>
        <field name="submitButton" title="${uiLabelMap.CommonAdd}"><submit button-type="button"/></field>
    </form>
</forms>

...

Code Block
languagexml
titleAdding Form Location to the Main Screen
 <?xml version="1.0" encoding="UTF-8"?>
<screens xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://ofbiz.apache.org/Widget-Screen" xsi:noNamespaceSchemaLocationschemaLocation="http://ofbiz.apache.org/Widget-Screen http://ofbiz.apache.org/dtds/widget-screen.xsd">
 
    <screen name="main">
        <section>
            <actions>
                <set field="headerItem" value="main"/> <!-- this highlights the selected menu-item with name "main" -->
            </actions>
            <widgets>
                <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                    <decorator-section name="body">
                        <screenlet title="Add Ofbiz Demo">
                            <include-form name="AddOfbizDemo" location="component://ofbizDemo/widget/OfbizDemoForms.xml"/>
                        </screenlet>
                    </decorator-section>
                </decorator-screen>
            </widgets>
        </section>
    </screen>
</screens>

...

Code Block
languagexml
titleOfbizDemoMenus.xml
<?xml version="1.0" encoding="UTF-8"?>
<menus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://ofbiz.apache.org/Widget-Menu" xsi:noNamespaceSchemaLocationschemaLocation="http://ofbiz.apache.org/Widget-Menu http://ofbiz.apache.org/dtds/widget-menu.xsd">
    <menu name="MainAppBar" title="${uiLabelMap.OfbizDemoApplication}" extends="CommonAppBarMenu" extends-resource="component://common/widget/CommonMenus.xml">
        <menu-item name="main" title="${uiLabelMap.CommonMain}"><link target="main"/></menu-item>
        <menu-item name="findOfbizDemo" title="${uiLabelMap.OfbizDemoFind}"><link target="FindOfbizDemo"/></menu-item>
    </menu>
</menus>

...

3.) Define new Java Class in file OfbizDemoServices.java here in services directory and implement a method, which is going to be invoked by your service definition, as shown below:

OfbizDemoServices.java


package com.companyname.ofbizdemo.services;
import java.util.Map;

import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.service.DispatchContext;
import org.apache.ofbiz.service.ServiceUtil;

public class OfbizDemoServices {

    public static final String module = OfbizDemoServices.class.getName();

    public static Map<String, Object> createOfbizDemo(DispatchContext dctx, Map<String, ? extends Object> context) {
        Map<String, Object> result = ServiceUtil.returnSuccess();
        Delegator delegator = dctx.getDelegator();
        try {
            GenericValue ofbizDemo = delegator.makeValue("OfbizDemo");
            // Auto generating next sequence of ofbizDemoId primary key
            ofbizDemo.setNextSeqId();
            // Setting up all non primary key field values from context map
            ofbizDemo.setNonPKFields(context);
            // Creating record in database for OfbizDemo entity for prepared value
            ofbizDemo = delegator.create(ofbizDemo);
            result.put("ofbizDemoId", ofbizDemo.getString("ofbizDemoId"));
            Debug.log("==========This is my first Java Service implementation in Apache OFBiz. OfbizDemo record created successfully with ofbizDemoId: "+ofbizDemo.getString("ofbizDemoId"));
        } catch (GenericEntityException e) {
            Debug.logError(e, module);
            return ServiceUtil.returnError("Error in creating record in OfbizDemo entity ........" +module);
        }
        return result;
    }


4.) Stop the server and re-start using "./gradlew ofbiz", it will compile your class and will make it available when ofbiz restarts which updated jar file.

5.) Test service implemented using webtools --> Run Service option(https://localhost:8443/webtools/control/runService) or simply update/add the service name/new service being called by your controller request to use this service instead and use add form in your app that you prepared earlier. By doing this your Add OfbizDemo form will call this java service. Find below to new service createOfbizDemoByJavaService which will be called by your controller request.


<request-map uri="createOfbizDemoByJavaService">
    <security https="true" auth="true"/>
    <event type="service" invoke="createOfbizDemoByJavaService"/>
    <response name="success" type="view" value="main"/>
</request-map>


To make sure this new service implementation is being executed, you can check this line in the console log that you have put in your code using Debug.log(....). For logging in OFBiz, you must always use Debug class methods in Java classes.

Console Log


[java] 2021-07-22 00:18:06,918 (http-bio-0.0.0.0-8443-exec-2) [  OfbizDemoServices.java:27 :INFO ] ==========This is my first Java Service implementation in Apache OFBiz. OfbizDemo record created successfully with ofbizDemoId: ......


Service in Groovy

To utilize features of on-the-fly compilation and fewer lines of code you can implement services for building business logic in OFBiz using Groovy DSL.

...

1.) Add new service definition to services/services.xml file as:

services.xml


<service name="createOfbizDemoByGroovyService" default-entity-name="OfbizDemo" engine="groovy"
        location="component://ofbizDemo/groovyScripts/OfbizDemoServices.groovy" invoke="createOfbizDemo" auth="true">
    <description>Create an Ofbiz Demo record using a service in Groovy</description>
    <auto-attributes include="pk" mode="OUT" optional="false"/>
    <auto-attributes include="nonpk" mode="IN" optional="false"/>
    <override name="comments" optional="true"/>
</service>


2.) Add new groovy services file here  component://ofbizDemo/script/com/companyname/ofbizdemo/OfbizDemoServices.groovy

3.) Add service implementation to the file OfbizDemoServices.groovy

OfbizDemoServices.groovy


import org.apache.ofbiz.entity.GenericEntityException;

def
createOfbizDemo() {
    result = [:];
    try {
        ofbizDemo = delegator.makeValue("OfbizDemo");
        // Auto generating next sequence of ofbizDemoId primary key
        ofbizDemo.setNextSeqId();
        // Setting up all non primary key field values from context map
        ofbizDemo.setNonPKFields(context);
        // Creating record in database for OfbizDemo entity for prepared value
        ofbizDemo = delegator.create(ofbizDemo);
        result.ofbizDemoId = ofbizDemo.ofbizDemoId;
        logInfo("==========This is my first Groovy Service implementation in Apache OFBiz. OfbizDemo record "
                  +"created successfully with ofbizDemoId: "+ofbizDemo.getString("ofbizDemoId"));
      } catch (GenericEntityException e) {
          logError(e.getMessage());
          return error("Error in creating record in OfbizDemo entity ........");
      }
      return result;


4.) Stop the server and re-start using"./gradlew ofbiz", this time we just need to load the new service definition, no explicit compilation is required as it's a service implementation in Groovy.

5.) Test service implemented using webtools --> Run Service option(https://localhost:8443/webtools/control/runService) or simply update/add the service name/new service being called by your controller request to use this service instead and use add form in your app that you prepared earlier for testing. By doing this your Add OfbizDemo form will call this groovy service. Find below the new service createOfbizDemoByGroovyService which will be called by your controller request.

controller.xml


<request-map uri="createOfbizDemoByGroovyService">
    <security https="true" auth="true"/>
    <event type="service" invoke="createOfbizDemoByGroovyService"/>
    <response name="success" type="view" value="main"/>
</request-map>


To make sure this new service implementation is being executed, you can check this line in the console log that you have put in your code using Debug.log(....). For logging in OFBiz, you must always use Debug class methods in Java classes.


2021-07-22 00:29:08,212 |jsse-nio-8443-exec-7 |GroovyBaseScript |I| ==========This is my first Groovy Service implementation in Apache OFBiz. OfbizDemo record created successfully with ofbizDemoId: .....


To get more details about using Groovy DSL for service and events implementation in Apache OFBiz you can refer document created by Jacopo Cappellato in OFBiz Wiki here.

...

1.) Add a new events directory to package and a new Events class file as mentioned here:

OfbizDemoEvents.java


package com.companyname.ofbizdemo.events;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.UtilMisc;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.service.GenericServiceException;
import org.apache.ofbiz.service.LocalDispatcher;

public class OfbizDemoEvents {

 public static final String module = OfbizDemoEvents.class.getName();

    public static String createOfbizDemoEvent(HttpServletRequest request, HttpServletResponse response) {
        Delegator delegator = (Delegator) request.getAttribute("delegator");
        LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
        GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");

        String ofbizDemoTypeId = request.getParameter("ofbizDemoTypeId");
        String firstName = request.getParameter("firstName");
        String lastName = request.getParameter("lastName");

        if (UtilValidate.isEmpty(firstName) || UtilValidate.isEmpty(lastName)) {
            String errMsg = "First Name and Last Name are required fields on the form and can't be empty.";
            request.setAttribute("_ERROR_MESSAGE_", errMsg);
            return "error";
        }
        String comments = request.getParameter("comments");

        try {
            Debug.logInfo("=======Creating OfbizDemo record in event using service createOfbizDemoByGroovyService=========", module);
            dispatcher.runSync("createOfbizDemoByGroovyService", UtilMisc.toMap("ofbizDemoTypeId", ofbizDemoTypeId,
                    "firstName", firstName, "lastName", lastName, "comments", comments, "userLogin", userLogin));
        } catch (GenericServiceException e) {
            String errMsg = "Unable to create new records in OfbizDemo entity: " + e.toString();
            request.setAttribute("_ERROR_MESSAGE_", errMsg);
            return "error";
        }
        request.setAttribute("_EVENT_MESSAGE_", "OFBiz Demo created succesfully.");
        return "success";
    }
}


2.) Add controller request of calling this event as:

controller.xml


 <request-map uri="createOfbizDemoEvent">
    <security https="true" auth="true"/>
    <event type="java" path="com.companyname.ofbizdemo.events.OfbizDemoEvents" invoke="createOfbizDemoEvent"/>
    <response name="success" type="view" value="main"/>
    <response name="error" type="view" value="main"/>
</request-map>



3.) Stop and start the server by rebuilding it as we need to compile the Java event class that we have added in #1.

...

References: https://cwiki.apache.org/confluence/display/OFBIZ/FAQ+-+Tips+-+Tricks+-+Cookbook+-+HowTo#FAQ-Tips-Tricks-Cookbook-HowTo-DifferenceBetweenEventAndService and http://ofbiz.135035.n4.nabble.com

CriteriaServicesEvents
Require DefinitionYesNo
Implementation possibilitiesEntity auto, Java, Simple (XML) & GroovySimple (XML), Java & Groovy
Return TypeMapString
Used to write business logicYesNo
Job Scheduling possibleYesNo

Customizing User Interface

...

2.) Add two Freemarker files at location $ OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/crud/AddOfbizDemo.ftl and ListOfbizDemo.ftl, as shown below:

AddOfbizDemo.ftl


<div class="screenlet-body">
  <form id="createOfbizDemoEvent" method="post" action="<@ofbizUrl>createOfbizDemoEvent</@ofbizUrl>">
    <input type="hidden" name="addOfbizDemoFromFtl" value="Y"/>
    <fieldset>
      <div>
        <span class="label">${uiLabelMap.OfbizDemoType}</span>
        <select name="ofbizDemoTypeId" class='required'>
          <#list ofbizDemoTypes as demoType>
            <option value='${demoType.ofbizDemoTypeId}'>${demoType.description}</option>
          </#list>
        </select>*
      </div>
      <div>
        <span class="label">${uiLabelMap.OfbizDemoFirstName}</span>
        <input type="text" name="firstName" id="firstName" class='required' maxlength="20" />*
      </div>
      <div>
        <span class="label">${uiLabelMap.OfbizDemoLastName}</span>
        <input type="text" name="lastName" id="lastName" class='required' maxlength="20" />*
      </div>
      <div>
        <span class="label">${uiLabelMap.OfbizDemoComment}</span>
        <input type="text" name="comments" id="comments" class='inputBox' size="60" maxlength="255" />
      </div>
    </fieldset>
    <input type="submit" value="${uiLabelMap.CommonAdd}" />
  </form>
</div>


ListOfbizDemo.ftl


 <div class="screenlet-body">
  <#if ofbizDemoList?has_content>
    <table cellspacing=0 cellpadding=2 border=0 class="basic-table">
      <thead><tr>
        <th>${uiLabelMap.OfbizDemoId}</th>
        <th>${uiLabelMap.OfbizDemoType}</th>
        <th>${uiLabelMap.OfbizDemoFirstName}</th>
        <th>${uiLabelMap.OfbizDemoLastName}</th>
        <th>${uiLabelMap.OfbizDemoComment}</th>
      </tr></thead>
      <tbody>
        <#list ofbizDemoList as ofbizDemo>
          <tr>
            <td>${ofbizDemo.ofbizDemoId}</td>
            <td>${ofbizDemo.getRelatedOne("OfbizDemoType").get("description", locale)}</td>
            <td>${ofbizDemo.firstName?default("NA")}</td>
            <td>${ofbizDemo.lastName?default("NA")}</td>
            <td>${ofbizDemo.comments!}</td>
          </tr>
        </#list>
       </tbody>
    </table>
  </#if>
</div>


2.) Add new Groovy file for data fetching logic at location $ OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/WEB-INF/actions/crud/ListOfbizDemo.groovy and add code as shown to list out OfbizDemo records:


ofbizDemoTypes = delegator.findList("OfbizDemoType", null, null, null, null, false);
context.ofbizDemoTypes = ofbizDemoTypes;
ofbizDemoList = delegator.findList("OfbizDemo", null, null, null, null, false);
context.ofbizDemoList = ofbizDemoList;


3.) Add new screen file with Ofbiz default decorator to OfbizDemoScreens.xml with newly added freemarker and groovy files as:

OfbizDemoScreens.xml


<screen name="AddOfbizDemoFtl">
    <section>
        <actions>
            <set field="titleProperty" value="PageTitleAddOfbizDemos"/>
            <set field="headerItem" value="addOfbizDemoFtl"/>
            <script location="component://ofbizDemo/webapp/ofbizDemo/WEB-INF/actions/crud/ListOfbizDemo.groovy"/>
        </actions>
        <widgets>
            <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                <decorator-section name="body">
                    <screenlet title="${uiLabelMap.OfbizDemoListOfbizDemos}">
                        <platform-specific>
                            <html><html-template location="component://ofbizDemo/webapp/ofbizDemo/crud/ListOfbizDemo.ftl"/></html>
                         </platform-specific>
                    </screenlet>
                    <screenlet title="${uiLabelMap.OfbizDemoAddOfbizDemoServiceByFtl}">
                        <platform-specific>
                            <html><html-template location="component://ofbizDemo/webapp/ofbizDemo/crud/AddOfbizDemo.ftl"/></html>
                        </platform-specific>
                    </screenlet>
                </decorator-section>
            </decorator-screen>
        </widgets>
    </section>
</screen>


4.) Add a new controller request and a new item for the OfbizDemo menu as:

controller.xml


<!--Request Mapping-->
<request-map uri="AddOfbizDemoFtl">
    <security https="true" auth="true"/>
    <response name="success" type="view" value="AddOfbizDemoFtl"/>
</request-map>

<!--View Mapping-->
<view-map name="AddOfbizDemoFtl" type="screen" page="component://ofbizDemo/widget/OfbizDemoScreens.xml#AddOfbizDemoFtl"/>


OfbizDemoMenus.xml


<menu-item name="addOfbizDemoFtl" title="${uiLabelMap.OfbizDemoAddFtl}"><link target="AddOfbizDemoFtl"/></menu-item


5.) Add new UI Labels as used by your app.

...

We will be doing it by defining a custom decorator for your application view. A decorator in OFBiz is nothing but a screen that you define and reuse afterward by including in your other screens of the application. You are already doing it with the default decorator (main-decorator –> ApplicationDecorator) which comes with OFBiz. Just observe your screens you have prepared so far, you will find that, you were using this main decorator, please refer below line in OfbizDemoScreens.xml.

OfbizDemoScreens.xml


<decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">


Info

The mainDecoratorLocation is available in parameters map as it is defined in webapp's web.xml

web.xml


<context-param>
    <description>The location of the main-decorator screen to use for this webapp; referred to as a context variable in screen def XML files.</description>
    <param-name>mainDecoratorLocation</param-name>
    <param-value>component://ofbizDemo/widget/CommonScreens.xml</param-value>
</context-param>


Now is the time to define your own decorator with custom styling. 

...

5.) Open $ OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/WEB-INF/web.xml and make entries for css and js directories in allowedPaths at the end as shown below:

web.xml


<init-param>
    <param-name>allowedPaths</param-name>
    <param-value>/error:/control:/select:/index.html:/index.jsp:/default.html:/default.jsp:/images:/includes/maincss.css:/css:/js</param-value>
</init-param>


6.) Add a new directory named "includes" at location $ OFBIZ_HOME/plugins/ofbizDemo/webapp/ofbizDemo/ and create two new files in this new directory you just added named PreBody.ftl and PostBody.ftl. We will be using(including) these two files in our decorator screen to build a complete HTML page.


PreBody.ftl


<html>
  <head>
    <title>${layoutSettings.companyName}</title>
    <meta name="viewport" content="width=device-width, user-scalable=no"/>
    <#if webSiteFaviconContent?has_content>
      <link rel="shortcut icon" href="">
    </#if>
    <#list layoutSettings.styleSheets as styleSheet>
      <link rel="stylesheet" href="${StringUtil.wrapString(styleSheet)}" type="text/css"/>
    </#list>
    <#list layoutSettings.javaScripts as javaScript>
      <script type="text/javascript" src="${StringUtil.wrapString(javaScript)}"></script>
    </#list>
  </head>
  <body data-offset="125">
    <h4 align="center"> ==================Page PreBody Starts From Decorator Screen========================= </h4>
    <div class="container menus" id="container">
      <div class="row">
        <div class="col-sm-12">
          <ul id="page-title" class="breadcrumb">
            <li>
                <a href="<@ofbizUrl>main</@ofbizUrl>">Main</a>
            </li>
            <li class="active"><span class="flipper-title">${StringUtil.wrapString(uiLabelMap[titleProperty])}</span></li>
            <li class="pull-right">
              <a href="<@ofbizUrl>logout</@ofbizUrl>" title="${uiLabelMap.CommonLogout}">logout</i></a>
            </li>
          </ul>
        </div>
      </div>
      <div class="row">
        <div class="col-lg-12 header-col">
          <div id="main-content">
              <h4 align="center"> ==================Page PreBody Ends From Decorator Screen=========================</h4>
              <h4 align="center"> ==================Page Body starts From Screen=========================</h4>


PostBody.ftl


<#-- Close the tags opened in the PreBody section -->
          </div>
        </div>
      </div>
    </div>
    <h4 align="center"> ==================Page PostBody and Page body in general ends here from Decorator Screen=========================</h4>
  </body>
</html>


7.) Open Common Screens file of your component $ OFBIZ_HOME/plugins/ofbizDemo/widget/CommonScreens.xml, this is the file we will define our custom decorator.

8.) Update screen named "OfbizDemoCommonDecorator"(which will serve as a custom decorator for your app) as shown below:

CommonScreens.xml


<screen name="OfbizDemoCommonDecorator">
    <section>
        <actions>
            <property-map resource="OfbizDemoUiLabels" map-name="uiLabelMap" global="true"/>
            <property-map resource="CommonUiLabels" map-name="uiLabelMap" global="true"/>
   
<set field="layoutSettings.companyName" from-field="uiLabelMap.OfbizDemoCompanyName" global="true"/>
            
<!-- Including custom CSS Styles that you want to use in your application view. [] in field can be used to
                 set the order of loading CSS files to load if there are multiple -->
            <set field="layoutSettings.styleSheets[]" value="/ofbizDemo/css/bootstrap.min.css"/>
   
            <!-- Including custom JS that you want to use in your application view. [] in field can be used to
                 set the order of loading of JS files to load if there are multiple -->
           <set field="layoutSettings.javaScripts[+0]" value="/ofbizDemo/js/bootstrap.min.js" global="true"/>
        </actions>
        <widgets>
            <section>
                <condition>
                    <if-has-permission permission="OFBIZDEMO" action="_VIEW"/>
                </condition>
                <widgets>
                    <platform-specific><html><html-template location="component://ofbizDemo/webapp/ofbizDemo/includes/PreBody.ftl"/></html></platform-specific>
                    <decorator-section-include name="pre-body"/>
                    <decorator-section-include name="body"/>
                    <platform-specific><html><html-template location="component://ofbizDemo/webapp/ofbizDemo/includes/PostBody.ftl"/></html></platform-specific>
                </widgets>
                <fail-widgets>
                    <label style="h3">${uiLabelMap.OfbizDemoViewPermissionError}</label>
                </fail-widgets>
            </section>
        </widgets>
    </section>
</screen>


In the code above you may have noticed the layoutSettings.styleSheets[] and layoutSettings.javaScripts[+0] notations. You can use the layoutSettings. notation for any files.

...

9.) Use this decorator in your Freemarker screen that you created in the last part as:

OfbizDemoScreens.xml


<screen name="AddOfbizDemoFtl">
    <section>
        <actions>
            <set field="titleProperty" value="OfbizDemoAddOfbizDemoFtl"/>
            <set field="headerItem" value="addOfbizDemoFtl"/>
            <script location="component://ofbizDemo/webapp/ofbizDemo/WEB-INF/actions/crud/ListOfbizDemo.groovy"/>
        </actions>
        <widgets>
            <decorator-screen name="OfbizDemoCommonDecorator" location="${parameters.mainDecoratorLocation}">
                <decorator-section name="body">
                     <label style="h4" text="${uiLabelMap.OfbizDemoListOfbizDemos}"/>
                     <platform-specific>
                         <html><html-template location="component://ofbizDemo/webapp/ofbizDemo/crud/ListOfbizDemo.ftl"/></html>
                     </platform-specific>
                     <label style="h4" text="${uiLabelMap.OfbizDemoAddOfbizDemoFtl}"/>
                     <platform-specific>
                         <html><html-template location="component://ofbizDemo/webapp/ofbizDemo/crud/AddOfbizDemo.ftl"/></html>
                     </platform-specific>
                </decorator-section>
            </decorator-screen>
        </widgets>
    </section>
</screen>


10.) Update your FTL files to follow HTML web standards and apply CSS on it as:

AddOfbizDemo.ftl


 <form method="post" action="<@ofbizUrl>createOfbizDemoEventFtl</@ofbizUrl>" name="createOfbizDemoEvent" class="form-horizontal">
  <div class="control-group">
    <label class="control-label" for="ofbizDemoTypeId">${uiLabelMap.OfbizDemoType}</label>
    <div class="controls">
      <select id="ofbizDemoTypeId" name="ofbizDemoTypeId">
        <#list ofbizDemoTypes as demoType>
          <option value='${demoType.ofbizDemoTypeId}'>${demoType.description}</option>
        </#list>
      </select>
    </div>
  </div>
  <div class="control-group">
    <label class="control-label" for="firstName">${uiLabelMap.OfbizDemoFirstName}</label>
    <div class="controls">
      <input type="text" id="firstName" name="firstName" required>
    </div>
  </div>
  <div class="control-group">
    <label class="control-label" for="lastName">${uiLabelMap.OfbizDemoLastName}</label>
    <div class="controls">
      <input type="text" id="lastName" name="lastName" required>
    </div>
  </div>
  <div class="control-group">
    <label class="control-label" for="comments">${uiLabelMap.OfbizDemoComment}</label>
    <div class="controls">
      <input type="text" id="comments" name="comments">
    </div>
  </div>
  <div class="control-group">
    <div class="controls">
      <button type="submit" class="btn">${uiLabelMap.CommonAdd}</button>
    </div>
  </div>
</form>


ListOfbizDemo.ftl


<table class="table table-bordered table-striped table-hover">
    <thead>
        <tr>
          <th>${uiLabelMap.OfbizDemoId}</th>
          <th>${uiLabelMap.OfbizDemoType}</th>
          <th>${uiLabelMap.OfbizDemoFirstName}</th>
          <th>${uiLabelMap.OfbizDemoLastName}</th>
          <th>${uiLabelMap.OfbizDemoComment}</th>
        </tr>
    </thead>
    <tbody>
        <#list ofbizDemoList as ofbizDemo>
            <tr>
              <td>${ofbizDemo.ofbizDemoId}</td>
              <td>${ofbizDemo.getRelatedOne("OfbizDemoType").get("description", locale)}</td>
              <td>${ofbizDemo.firstName?default("NA")}</td>
              <td>${ofbizDemo.lastName?default("NA")}</td>
              <td>${ofbizDemo.comments!}</td>
            </tr>
        </#list>
    </tbody>
</table


10. Now restart OFBiz as you have made entries to allowedPaths in web.xml. As it reloads hit https://localhost:8443/ofbizDemo/control/AddOfbizDemoFtl you should see the page with custom styles that you have used instead of using the default OFBiz theme. It should look like:

...