Access to add and change pages is restricted. See: https://cwiki.apache.org/confluence/display/OFBIZ/Wiki+access

This is a tutorial providing an introduction to OFBiz application development. It starts with the usual Hello World tutorial, then shows how to retrieve the names of the people stored in the OFBiz database and in a third example introduces entities and services. This example will store the names of guests and their hobbies in the database. Following that there are examples how to find, update and delete these persons in the database. Then it is demonstrated how to generate lists of these persons and export the data in various file formats. The first three examples are based on examples developed by Si Chen. These examples were modified for this tutorial and ported to run with OFBiz version 17.12.04. The tutorials are grouped here into one plugin called OI. These examples have been developed on a Windows PC. On Linux convert the backslashes to forward slashes in the path names.

To install OFBiz on your computer please refer to the appendix of this tutorial. There you will find instructions for Windows 10 and for Linux there. Plus information about the Eclipse IDE most developers use for OFBiz development.

Create a plugin

At first you have to create the new plugin or component called „OI“. OI is an abbreviation for OFBiz Introduction and will be used as the name of our plugin.

1.) Get into your OFBiz directory and enter:
gradlew createPlugin -PpluginId=OI

This command will create a new directory named „OI“ in the ofbiz\plugins directory. It will contain a sceleton of an OFBiz plugin which can be modified and extended as required. Details about the OFBiz plugin system can be found here: ofbiz-plugin-system. If you want to remove this plugin at a later time you can enter:
gradlew removePlugin -PpluginId=OI

2.) On Windows the plugin will not start with unsupported chinese characters in the $OFBIZ_HOME\plugins\OI\config\OIUiLabels.xml file. If you do not have support for chinese characters installed you need to remove all the entries for other languages but English in this file to get OFBiz to work. It will not work with "zh", "it" and "zh-tw" entries present.

3.) The a new component OI you created has some security data in the $OFBIZ_HOME\plugins\OI\data\OISecurityGroupDemoData.xml file. Therefore you need to restart OFBiz to get that data loaded by starting OFBiz with the command:
gradlew loadAll ofbiz

4.) From now on, you can start OFBiz with the command:
gradlew ofbiz

After entering https://localhost:8443/OI you can log in. Then you will get an empty page and the new plugin „OI“ is added to the main menu. However, you will have to click on the „+“ first to get a drop-down list which will have an entry named „OI“.

Overview

The examples which will be presented now consist of several files which work in concert to finally produce the desired output on your web browser's screen. This can be confusing at first, so here is a simplified overview of these files. A more detailed overview is provided in this image: docs\asciidoc\image\ofbiz-architecture.png in your OFBiz directory.
On the left side of the image the user requests an application or plugin using his browser. This application has its own menu definition in the *Menus.xml file (in this tutorial the OIMenus.xml file). The selected menu item then calls a request in the controller.xml file which again calls the matching screen definition in the *Screens.xml (OIScreens.xml) file. To define the screen layout the code in the *Screens.xml file will use a form in the *Forms.xml (OIForms.xml) file or call a FreeMarker file instead to finally define the screen to be dispayed on the web browser.

In some other documents, this chain is started at the right side. However, in this tutorial we follow the path as described above.

The Hello World example

At first we will extend our menu to be able to select the Hello example. For this open the $OFBIZ_HOME\plugins\OI\widget\OIMenus.xml file and add the following menu item:

Entry to add in OIMenus.xml
<menu-item name="hello" title="${uiLabelMap.Hello}"><link target="hello"/></menu-item>

The "link target" tag specifies the request-map name in the controller.xml file which will be discussed below.

If save your file and click on „Main“ button in the OI plugin again, the menu will refresh and show the additional „Hello“ button as defined with the „title=“ tag. The „uiLabelMap“ allows you to translate the button text into various languages using the OIUiLabels.xml file mentioned above.

This tutorial will demonstrate three different ways to display the phrase „Hello World“ on the screen. At first it is defined using a <label> command in the OIScreens.xml file. Then it is done using a field definition in the OIForms.xml file. Finally there is a FreeMarker file which will define HTML code to display „Hello World“ on the browser's screen.

The menu we just configured above will select a „request“ target in the $OFBIZ_HOME\plugins\OI\webapp\OI\WEB-INF\controller.xml file. Therefore we have to configure that request in this file by adding the following two lines which refer to „hello“:

Entry to add in controller.xml
<!-- Request Mappings -->
<request-map uri="hello"><security https="true" auth="true"/><response name="success" type="view" value="hello"/></request-map>

<!-- View Mappings -->
<view-map name="hello" type="screen" page="component://OI/widget/OIScreens.xml#hello"/>

The request-map entries define the URL of the screen for the browser. The uri=”hello” attribute defines the last part of the URL, so the complete URL to call this is http://localhost:8443/OI/control/hello. The request-map entries must be listed before the view-map entries in the controller.xml file. The “security” element with the attribute https=”true” sets OFBiz to offer the page using the https protocol. The auth=”true” attribute makes sure that access is granted only to authenticated users. If the user is not authenticated, he will be redirected to the user login screen. The ”response-name” tag will associate a request-map with the view-map specified in the value tag. In this example this is the view-map with the name ”hello”. Further details regarding this controller.xml file can be found here:
Control Servlet Guide

Using the view-map tag above, the controller.xml file will call the „hello“ screen in the $OFBIZ_HOME\plugins\OI\widget\OIScreens.xml file. So add the „hello“ screen into this file:

OIScreens.xml
<screen name="hello">
  <section>
     <actions>
        <set field="headerItem" value="hello"/>
     </actions>
     <widgets>
        <decorator-screen name="OICommonDecorator" location="${parameters.mainDecoratorLocation}">
        	<decorator-section name="body">
	    		<label text="Hello World!" style="h1"/>
	    		<horizontal-separator/>
	    		<include-form name="HelloW" location="component://OI/widget/OIForms.xml"/>
			    <horizontal-separator/>
	    		<platform-specific>
        	     <html>
        	       <html-template location="component://OI/webapp/OI/hello.ftl"/>
        	     </html>
        	   </platform-specific>
        	 </decorator-section>
         </decorator-screen>
      </widgets>
  </section>
</screen>

There is the label command in there which will output “Hello World!“ on the screen. The style tag specifies the size of “h1“ for this label which is the largest header size in HTML. The decorator-screen command specifies the screen decorator used to decorate the screen with all the application menus, the header and footer that are shared by all the screens. This default decorator is defined in the $OFBIZ_HOME\plugins\OI\widget\CommonScreens.xml file.

There are additional ways to do that though. This screen definition calls the HelloW form in the $OFBIZ_HOME\plugins\OI\widget\OIForms.xml file. So add this form into this file:

Entry to add in OIForms.xml
<form name="HelloW" type="single">
	<field name="Hello World!"><display/></field>
</form>

This form defines and displays a field with the name „Hello World“. In the screen definition the horizontal separator is used to separate this message from the one generated with the label command.

The screen definition then calls the hello.ftl FreeMarker file to display „Hello World“ on the screen. This hello.ftl file has to put at the location $OFBIZ_HOME\plugins\OI\webapp\OI\hello.ftl and looks like this:

hello.ftl
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h1>HELLO</h1>
<p>Hello world!</p>
</body>
</html>

Here is a link to details regarding the FreeMarker Template-Engine: Apache FreeMarker Manual

Now everything is set and when you click on „Hello“ in the menu, you will see the webpage generated by our hello example.

Retrieving persons from the database

This example uses a groovy script to retrieve the demo persons stored in the database and displays the names of the persons found on the screen. The screen is split into three FreeMarker files to demonstrate how to use several FreeMarker files.

At first we will extend our menu with a new button with the label „People“ for this example. Open the $OFBIZ_HOME\plugins\OI\widget\OIMenus.xml file and add the following menu item:

Entry to add in OIMenus.xml
<menu-item name="people" title="${uiLabelMap.People}">
<link target="people"/></menu-item>

Next we have to extend the $OFBIZ_HOME\plugins\OI\webapp\OI\WEB-INF\controller.xml file. Add the following two lines to configure the request-map and view-map:

Entry to add in controller.xml
<request-map uri="people"><security https="true" auth="true"/><response name="success" type="view" value="people"/></request-map>

<view-map name="people" type="screen" page="component://OI/widget/OIScreens.xml#people"/>

Then we have to add a „people“ screen in the $OFBIZ_HOME\plugins\OI\widget\OIScreens.xml file:

Entry to add in OIScreens.xml
<screen name="people">
	<section>
		<actions>
			<script location="component://OI/groovyscripts/people.groovy"/>
			<set field="headerItem" value="people"/>
		</actions>
		<widgets>
			<decorator-screen name="OICommonDecorator" location="${parameters.mainDecoratorLocation}">
			<decorator-section name="body">
				<platform-specific>
					<html>
					<html-template location="component://OI/webapp/OI/header.ftl"/>
					</html>
				</platform-specific>
				<platform-specific>
					<html>
					<html-template location="component://OI/webapp/OI/people.ftl"/>
					</html>
				</platform-specific>
				<platform-specific>
					<html>
					<html-template location="component://OI/webapp/OI/footer.ftl"/>
					</html>
				</platform-specific>
			</decorator-section>
			</decorator-screen>
		</widgets>
	</section>
</screen>

This screen definition uses three FreeMarker files: header.ftl, people.ftl and footer.ftl. The people.ftl file will show the list of the names of the persons retrieved. Now we have to place these files into the $OFBIZ_HOME\plugins\OI\webapp\OI\ directory.

header.ftl
<html>
<head>
<link rel="stylesheet" href="../images/main.css" type="text/css">
<title>List of the persons found in the database</title>
</head>
<body>
<p align = "left">
It is now ${Static["org.apache.ofbiz.base.util.UtilDateTime"].nowTimestamp().toString()} !</p>
<hr>


people.ftl
<#if persons?has_content>
<h2>These people have visited our site:</h2>
<br>
<ul>
<#list persons as person>
<li>${person.firstName?if_exists} ${person.lastName?if_exists}</li>
</#list>
</ul>
</#if>


footer.ftl
<hr>
Thank you for visiting the people application.
</body>
</html>

But we are not done yet. The screen definition had an „actions“ section which called a groovy script to retrieve all the persons and put the list into the person context. This is just a one-line script:

people.groovy
context.persons = delegator.findList("Person", null, null, null, null, false);

This line of groovy code has to be put into the $OFBIZ_HOME\plugins\OI\groovyscripts\people.groovy file so the screen definition will find this script there.

Details regarding the use of groovy for OFBiz can be found here: Groovy DSL for OFBiz business logic

The FreeMarker file people.ftl will read all the names of these persons from the context and list them on the screen based on this line:
<li>${person.firstName?if_exists} ${person.lastName?if_exists}</li>

Now click on the People button in the menu and a similar list will be displayed:

Build a database of guests and their hobbies

In this example we will store persons or guests and their hobbies in the database. For each person his or her hobbies can be added and looked up. So we need a database table or entity for the persons (HelloPerson) and one for the hobbies (HelloHobby). Then there will be a further entity to store all the links of each guest to one of the available hobbies. This entity is called HelloPersonHobby. These tables will be defined in the file $OFBIZ_HOME\plugins\OI\entitydef\entitymodel.xml. Put the following definitions into that file:

Entries to add to entitymodel.xml
<entity entity-name="HelloPerson" package-name="org.apache.ofbiz.OI" title="Persons Entity">
	<field name="helloPersonId" type="id"><description>primary sequenced ID</description></field>
	<field name="firstname" type="id"></field>
	<field name="lastname" type="id"></field>
	<field name="comments" type="comment"></field>
	<prim-key field="helloPersonId"/>
</entity>

<entity entity-name="HelloHobby" package-name="org.apache.ofbiz.OI" title="Hobbies Entity">
	<field name="helloHobbyId" type="id"><description>primary sequenced ID</description></field>
	<field name="description" type="description"></field>
	<prim-key field="helloHobbyId"/>
</entity>

<entity entity-name="HelloPersonHobby" package-name="org.apache.ofbiz.OI" title="PersonHobbies Entity">
	<field name="helloPersonId" type="id"><description>primary sequenced ID</description></field>
	<field name="helloHobbyId" type="id"><description>primary sequenced ID2</description></field>
	<prim-key field="helloPersonId"/>
	<prim-key field="helloHobbyId"/>
	<relation type="one" fk-name="HPRSN_PRSN" rel-entity-name="HelloPerson">
		<key-map field-name="helloPersonId"/>
	</relation>
	<relation type="one" fk-name="HPRSN_HBBY" rel-entity-name="HelloHobby">
		<key-map field-name="helloHobbyId"/>
	</relation>
</entity>

HelloPerson and HelloHobby each have a primary key (prim-key), and the linking entity HelloPersonHobby has two primary key fields, which it uses to link HelloPerson and HelloHobby. The relationships serve as foreign-key constraints but also allow you to go from one entity to another without having to remember yourself what their respective keys are (or change your code when keys change.) It is considered good practice to give your relations a foreign key name (fk-name) to make debugging easier and avoid accidental collision of foreign key names OFBiz generates for you.

This wiki page provides additional information about entities: General Entity Overview

When you get into webtools->entityengine->letter “H“, you will see the three entities HelloPerson, HelloHobby and HelloPersonHobby. If you click on "All" to the very right of the HelloPerson entity in the list, webtools will display its fields. Webtools allows you to create new HelloPerson records if you wish or update the existing ones. However, we will design our own screen in this tutorial to enter persons.

We are creating an application with a completely self-contained data model here for illustration. If we were building a real application, the best practice would be to reuse as many existing OFBiz entities as possible, so instead of creating our own HelloPerson entity, we would be using the existing OFBiz Party/Person/PartyGroup entities.

Now let's create some seed data for our HelloHobby entity. For that enter the following lines into the file $OFBIZ_HOME\plugins\OI\data\OIDemoData.xml:

OIDemoData.xml
<entity-engine-xml>
	<HelloHobby helloHobbyId="READING" description="Reading"/>
	<HelloHobby helloHobbyId="MOVIES" description="Movies"/>	
	<HelloHobby helloHobbyId="THEATER" description="Theater"/>
	<HelloHobby helloHobbyId="SKIING" description="Skiing"/>
	<HelloHobby helloHobbyId="SURFING" description="Surfing"/>
	<HelloHobby helloHobbyId="WINDSURFING" description="Windsurfing"/>
	<HelloHobby helloHobbyId="BASKETBALL" description="Basketball"/>
	<HelloHobby helloHobbyId="SOCCER" description="Soccer"/>
	<HelloHobby helloHobbyId="COOKING" description="Cooking"/>	
	<HelloHobby helloHobbyId="WINE" description="Wine"/>
</entity-engine-xml>

This specifies the available hobbies we can relate to the persons or guests. Now you are ready to load your seed data. Get into the Web Tools application's "Main" screen, and you will see a button for "XML Import/Export". Click on that and then on "XML Data Import". On the next screen, you can paste the lines above in the text entry box and click „Import Text“. If you are successful, the same screen will come back and tell you at the bottom how many values were added. In this case it will report: „Got 10 entities to write to the datasource.“ As an alternative you can enter the name of your file, OIDemoData.xml in this case, in the „Absolute Filename“ box and click on "Import file".

If you now select "Entity Maintenance" from the "Main" webtools page and click on "All" next to HelloHobby, you will see the hobbies that were just inserted. In addition to your fields and values, OFBiz also automatically creates timestamps of when your values were created and updated, for synchronizing data across multiple instances of OFBiz.

Now extend the menu with a new button for this example. Open the $OFBIZ_HOME\plugins\OI\widget\OIMenus.xml file and add the following menu item:

Entry to add in OIMenus.xml
<menu-item name="guestbook" title="${uiLabelMap.Guestbook}"><link target="guestbook"/></menu-item>

We will design two screens now. One to enter the persons or guests and display the guests already entered and one to add possible hobbies to these guests. Again, at first add the required request-maps and view-maps to the controller.xml:

Entries to add in controller.xml
<request-map uri="guestbook"><security https="true" auth="true"/><response name="success" type="view" value="guestbook"/></request-map>
<request-map uri="hobbies"><security https="true" auth="true"/><response name="success" type="view" value="hobbies"/></request-map>

<request-map uri="createPerson">
	<event type="service" invoke="createHelloPerson"/><response name="success" type="view" value="guestbook"/>
</request-map>

<request-map uri="createPersonHobby">
	<event type="service" invoke="createHelloPersonHobby"/><response name="success" type="view" value="hobbies"/>
</request-map>

<!-- View Mappings -->
<view-map name="guestbook" type="screen" page="component://OI/widget/OIScreens.xml#guestbook"/>
<view-map name="hobbies" type="screen" page="component://OI/widget/OIScreens.xml#hobbies"/>

You need to add the request maps for createPerson and createPersonHobby to the controller.xml file. These requests are called by the forms we will define below. Then the requests will call the specified services presented below.
An introduction for the Ofbiz widget toolkit which we will be using now can be found here: Understanding the OFBiz Widget Toolkit

Now add the two screens to the $OFBIZ_HOME\plugins\OI\widget\OIScreens.xml file:

Entries to add to OIScreens.xml
<screen name="guestbook">
	<section>
		<actions>
			<entity-condition entity-name="HelloPerson" list="allGuests">
				<order-by field-name="helloPersonId"/>
			</entity-condition>
			<set field="headerItem" value="guestbook"/>
		</actions>
		<widgets>
			<decorator-screen name="OICommonDecorator" location="${parameters.mainDecoratorLocation}">
				<decorator-section name="body">
					<label>Our Guests and what they've had to say:</label>
					<include-form name="GuestbookList" location="component://OI/widget/OIForms.xml"/>
					<label>Add another guest</label>
					<include-form name="AddGuest" location="component://OI/widget/OIForms.xml"/>
				</decorator-section>
			</decorator-screen>
		</widgets>
	</section>
</screen>

<screen name="hobbies">
	<section>
		<actions>
			<set field="helloPersonId" from-field="parameters.helloPersonId"/>
			<entity-and entity-name="HelloPersonHobby" list="allHobbies">
				<field-map field-name="helloPersonId"/>
				<order-by field-name="helloHobbyId"/>
			</entity-and>
			<entity-one entity-name="HelloPerson" value-field="person">
				<field-map field-name="helloPersonId"/>
			</entity-one>
			<set field="headerItem" value="guestbook"/>
		</actions>
		<widgets>
			<decorator-screen name="OICommonDecorator" location="${parameters.mainDecoratorLocation}">
				<decorator-section name="body">
					<label>Hobbies for ${person.firstname} ${person.lastname}</label>
					<include-form name="HobbiesList" location="component://OI/widget/OIForms.xml"/>
					<label>Add a hobby for ${person.firstname} ${person.lastname}</label>
					<include-form name="AddHobby" location="component://OI/widget/OIForms.xml"/>
				</decorator-section>
			
</decorator-screen>
		</widgets>
	</section>
</screen>

The first screen has an actions element where the the data retrieval logic is defined. The “entity-condition” command is used to retrieve all the records from the “HelloPerson” entity sorted by “helloPersonId”. The <set field="headerItem" value="guestbook"/> line is used to select the guestbook menu button after the screen is rendered.
The second screen shall display the hobbies linked with a particular person. In the actions element the helloPersonId of the person in question is moved from the parameters map or context to the field helloPersonId using the „from-field“ attribute. Then the „entity-and“ command is used to write all hobbies of the person with the selected helloPersonId into the allHobbies list. The „field-map“ element specifies the primary key as a filter for the allHobbies list. So the all Hobbies list will only contain entities matching the specified helloPersonId. Then the „order-by“ command sorts the entities in the allHobbies list to an ascending helloHobbyId field.

You can define the data retrieval logic either in the actions elements in the screen definition or in the forms action tag. This depends what's more conveniant for you. The screen specific content is defined in forms which are defined in the OIForms.xml file. A link to this file is specified in the OIScreens.xml file above. Using the “component://“ format the path a relative path to the OIForms.xml file can be used.

The commands used in the actions element are Minilang commands which are documented here: Mini Language(minilang) Reference

This screen definition does not use FreeMarker but refers to an OIForms.xml file instead. The form-widget allows you to define forms based on fields of an entity or parameters of a service, and you can define forms which display a single entity or show a list of data or submit single or multiple values to the controller. You can specify pull down menus and look up related entities, hide fields, place hyperlinks, even calendar buttons and other widgets. Finally, forms can automatically adjust when their related entity or service changes.

So enter these form definitions in the $OFBIZ_HOME\plugins\OI\widget\OIForms.xml file:

Entries to add in OIForms.xml
<form name="GuestbookList" type="list" list-name="allGuests">
	<auto-fields-entity entity-name="HelloPerson" default-field-type="display"/>
	<field name="hobbies">
		<hyperlink target="hobbies?helloPersonId=${helloPersonId}" description="See hobbies"/>
	</field>
</form>

<form name="AddGuest" type="single" target="createPerson">
	<auto-fields-entity entity-name="HelloPerson"/>
	<field name="helloPersonId"><hidden/></field>
	<field name="submitButton" title="Add a Guest" widget-style="standardSubmit"><submit button-type="button"/></field>
</form>

<form name="HobbiesList" type="list" list-name="allHobbies">
	<field name="helloHobbyId" title="Hobby">
		<display-entity entity-name="HelloHobby" description="${description}"/>
	</field>
</form>

<form name="AddHobby" type="single" target="createPersonHobby">
	<auto-fields-entity entity-name="HelloPersonHobby"/>
	<field name="helloPersonId"><hidden/></field>
	<field name="helloHobbyId">
		<drop-down>
			<entity-options entity-name="HelloHobby" description="${description} [${helloHobbyId}]" />

		</drop-down>
	</field>
	<field name="submitButton" title="Add a Hobby" widget-style="standardSubmit"><submit button-type="button"/></field>
</form>

The type of the HobbiesList form is “list” because we want to render its content as a list in tabular format. The “auto-fields-entity” command causes all the fields from the HelloPerson entity to be included as “display” fields which means they are read only. You can define different attributes for these fields by defining them separately following the “auto-fields-entity” command. If you now click on the Guestbook menu button you can see how the screens look like but they do not work yet. For this you need to define the following two services.

Creating services is a two step process. First, you define the service generically in an XML file, which tells the OFBiz service engine what parameters your service takes and where to find it (class and method or location of a script.) Then, you implement the service in Java, Groovy, the OFBiz Minilang, or another scripting language.
Here are additional details about services in OFBiz including a section about the attribute "entity-auto": Service Engine Guide
You now have to define the services in the $OFBIZ_HOME\plugins\OI\servicedef\services.xml file. Add the following lines in there:

services.xml
<service name="createHelloPerson" default-entity-name="HelloPerson" engine="entity-auto" invoke="create" auth="true">
	<description>Create a HelloPerson record</description>
	<auto-attributes include="pk" mode="OUT" optional="false"/>
	<auto-attributes include="nonpk" mode="IN" optional="false"/>
	<override name="comments" optional="true"/>
</service>

<service name="createHelloPersonHobby" engine="simple" location="component://OI/script/OIServices.xml" invoke="createHelloPersonHobby">
	<description>Create a HelloPersonHobby which links a person with a hobby</description>
	<auto-attributes mode="IN" entity-name="HelloPersonHobby"
	include="pk" optional="false"/>
</service>

Notice how our services.xml references the entities they work with directly with the <auto-attributes> tag, rather than specifying them outright. This saves you time and makes your application easier to maintain.

The createHelloPerson service is developed using the „entity-auto“ engine and the createHelloPersonHobby service is written using Minilang, the domain specific language (DSL) developed for OFBiz. The simple minilang service is stored in the OIServices.xml file inside a script/ directory and is an XML file with directives. Because it is designed especially for common OFBiz application tasks, such as lookup data, store data, check premissions, and work with existing entities and services, it can make those tasks very easy.

This createHelloPersonHobby service using Minilang has to be written into an $OFBIZ_HOME\plugins\OI\script\OIServices.xml file:

OIServices.xml
<?xml version="1.0" encoding="UTF-8"?>
<simple-methods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://ofbiz.apache.org/Simple-Method"
	xsi:schemaLocation="http://ofbiz.apache.org/Simple-Method http://ofbiz.apache.org/dtds/simple-methods.xsd">

<simple-method method-name="createHelloPersonHobby">
	<make-value entity-name="HelloPersonHobby" value-field="newEntity"/>
	<set-nonpk-fields map="parameters" value-field="newEntity"/>
	<set-pk-fields map="parameters" value-field="newEntity"/>
	<create-value value-field="newEntity"/>
</simple-method>

</simple-methods>

The make-value command creates a new entity in memory, called newEntity, from the HelloPersonHobby entity. Then the primary and non-primary key values are copied from the parameters map to the newEntity. Then the newEntity is written to the database with the create-value command. Finally, you have to re-start OFBiz (enter CTRL-C and then gradlew ofbiz on the command line) to load all the new definitions in the OIServices.xml and services.xml files.

If you now click on the new Guestbook menu button, you will get a screen where you can add guests which will appear in the list above the entry fields. If you click on the „See hobbies“ link in the table, the second screen on the right will be displayed. It shows a list of the hobbies entered for the selected person and also features a drop-down list from which you can select further hobbies to add to the existing list of hobbies of this person.

To get to know webtools further, open webtools->service engine->run script and enter the createHelloPersonHobby service to run that from webtools. After you click on submit you will see the following screen:

In this screen you could enter the HobbyId „WINE“ and the PersonId „10000“. If you click on submit, the hobby wine will be added to the list of hobbies of the person with the Id of 10000. Provided this person had been created before. You can use webtools to take a look at the HelloPersonHobby entity and you will see that the hobby wine is added to the PersonId 10000.
To do that select „Entity Engine“ in webtools and then select „Entity maintenance“. Specify the HelloPersonHobby entity and when you click the All button you will get a list of the records entered into this entity. If you click on the „view“ button in the left column of this list, you can edit or delete each record if required.

Add screens to edit and delete existing entities

The next step is to add screens to edit existing entities and delete entities. For that we enter two new lines into the OIMenus.xml file:

Entries to add to OIMenus.xml
<menu-item name="editguests" title="${uiLabelMap.EditGuests}"><link target="editguests"/></menu-item>
<menu-item name="deleteguests" title="${uiLabelMap.DeleteGuests}"><link target="deleteguests"/></menu-item>

Then we have to enter these targets into the controller.xml file:

Entries to add to controller.xml
<request-map uri="editguests"><security https="true" auth="true"/><response name="success" type="view" value="editguests"/></request-map>
<request-map uri="deleteguests"><security https="true" auth="true"/><response name="success" type="view" value="deleteguests"/></request-map>

<request-map uri="updateguest">
	<event type="service" invoke="updateguest"/><response name="success" type="view" value="guestbook"/>
</request-map>
<request-map uri="deleteguest">
	<event type="service" invoke="deleteHelloPersonandHobby"/><response name="success" type="view" value="deleteguests"/>
</request-map>

<view-map name="editguests" type="screen" page="component://OI/widget/OIScreens.xml#UpdateGuest"/>
<view-map name="deleteguests" type="screen" page="component://OI/widget/OIScreens.xml#DeleteGuest"/>

 We also added two request-maps for the two services updateguest and deleteguest which will be called by the forms UpdateGuest and DeleteGuest defined below.

Next we enter the UpdateGuest and DeleteGuest screens into OIScreens.xml:

Entries to add to OIScreens.xml
<screen name="UpdateGuest">
       <section>
           <actions>
               <set field="headerItem" value="editguests" />
           </actions>
           <widgets>
               <decorator-screen name="OICommonDecorator" location="${parameters.mainDecoratorLocation}">
                   <decorator-section name="body">
					<screenlet title="Update guest">
					<include-form name="Updateguest1" location="component://OI/widget/OIForms.xml"/>
						<horizontal-separator/>
						<include-form name="Updateguest2" location="component://OI/widget/OIForms.xml"/>
					</screenlet>
                   </decorator-section>
               </decorator-screen>
           </widgets>
       </section>
</screen>

<screen name="DeleteGuest">
	<section>
		<actions>
			<set field="headerItem" value="deleteguest"/>
		</actions>
		<widgets>
			<decorator-screen name="OICommonDecorator" location="${parameters.mainDecoratorLocation}">
				<decorator-section name="body">
					<screenlet title="Update guest">
						<include-form name="Deleteguest" location="component://OI/widget/OIForms.xml"/>
					</screenlet>
				</decorator-section>
			</decorator-screen>
		</widgets>
	</section>
</screen>

These two screens again include the forms UpdateGuest1, UpdateGuest2 and DeleteGuest in OIForms.xml:

Entries to add to OIForms.xml
<form name="Updateguest1" type="single" default-entity-name="HelloPerson"> 
       <field name="helloPersonId" title="${uiLabelMap.CommonType}">
           <drop-down allow-empty="false" current-description="">
               <entity-options description="[${helloPersonId}] ${firstname} ${lastname}" key-field-name="helloPersonId" entity-name="HelloPerson">
                   <entity-order-by field-name="helloPersonId"/>
               </entity-options>
           </drop-down>
       </field>
	<field name="submitButton" title="Select person entity" widget-style="standardSubmit"><submit button-type="button"/></field>
</form>

<form name="Updateguest2" type="single" target="updateguest" default-entity-name="HelloPerson">
	<actions>
		<entity-one entity-name="HelloPerson" value-field="person"/>
		<set field="firstname" value="${person.firstname}"/>
		<set field="lastname" value="${person.lastname}"/>
		<set field="comments" value="${person.comments}"/>
	</actions>
	<auto-fields-service service-name="updateguest" default-field-type="edit" /> 
	<field name="helloPersonId"><hidden/></field>
	<field name="submitButton" title="${uiLabelMap.CommonUpdate}"><submit button-type="button"/></field>
</form>

<form name="Deleteguest" type="single" target="deleteguest">
	<auto-fields-service service-name="deleteguest" default-field-type="edit"/>
	<field name="helloPersonId" title="${uiLabelMap.CommonType}">
		<drop-down allow-empty="false" current-description="">
			<entity-options description="${firstname} ${lastname} - [${helloPersonId}]"
				key-field-name="helloPersonId" entity-name="HelloPerson">
				<entity-order-by field-name="helloPersonId"/>
			</entity-options>
		</drop-down>
	</field>
	<field name="submitButton" title="${uiLabelMap.CommonDelete}"><submit button-type="button"/></field>
</form>

Both forms have a drop-down list defined which allows to select the guest to update or delete. When the submit button is clicked, the request-map for the services updateguest and deleteguest are called which are described next.

Finally we extend the services.xml file in the servicedef directory:

Entries to add to services.xml
<service name="updateguest" default-entity-name="HelloPerson" engine="entity-auto" invoke="update" auth="true">
	<description>Update a HelloPerson record</description>
	<auto-attributes include="pk" mode="IN" optional="false"/>
	<auto-attributes include="nonpk" mode="IN" optional="true"/>
</service>

<service name="deleteguest" default-entity-name="HelloPerson" engine="entity-auto"
	invoke="delete" auth="true">
        <description>Delete a HelloPerson record</description>
		<auto-attributes include="pk" mode="IN" optional="false"/>
</service>

<service name="deleteHelloPersonandHobby" engine="simple"
	location="component://hobbies/script/HobbiesServices.xml" invoke="deleteHelloPersonandHobby">
	<description>Delete a HelloPerson record plus HelloPersonHobby which links a person with a hobby</description>
	<auto-attributes mode="IN" entity-name="HelloPerson" include="pk" optional="false"/>
</service>

Here we use the engine "entity-auto" for updateguest as a simple way to create the required methods and use auto-attributes to generate the fields on our screen. We do not need to define a service script when we select "entity-auto". However, when we delete the record, we also have to remove the relations to the hobbies of the guest. Therefore we have to add the following Minilang script (engine=“simple“) to the OIServices.xml file, which removes the relations to the hobbies too:

Entry to add to OIServices.xml
<simple-method method-name="deleteHelloPersonandHobby">
	<make-value entity-name="HelloPerson" value-field="newEntity"/>
	<set-nonpk-fields map="parameters" value-field="newEntity"/>
	<set-pk-fields map="parameters" value-field="newEntity"/>
	<!-- delete the hobby-links of the guest -->
	<remove-related relation-name="HelloPersonHobby" value-field="newEntity"/>
	<!-- delete the guest -->
	<remove-value value-field="newEntity"/>
</simple-method>

Finally, you have to re-start OFBiz (enter CTRL-C and then gradlew ofbiz on the command line) to load all the new definitions in the OIServices.xml and services.xml files. If you now click on the new EditGuests and DeleteGuests menu buttons, you will get screens where you can update or edit guests and delete guests.

A Find screen to select and edit guests

In the forms above the primary keys were selected from a drop-down list. If you have a large amount of entities this no longer practical. Therefore we will define a „Find“ screen now to get the primary key of the entity we are looking for. This screen displays a list of the guests matching the entered search criteria. The number in the first column of this list is a link you can click for an edit screen for this guest in the database.

First add a new button in our menu:

Entry to add to OIMenus.xml
<menu-item name="findguests" title="${uiLabelMap.FindGuests}"><link target="findguests"/></menu-item>

Then add a request map and view map into the controller.xml file:

Entries to add to controller.xml
<request-map uri="findguests"><security https="true" auth="true"/><response name="success" type="view" value="findguests"/></request-map>
<request-map uri="editguests2"><security https="true" auth="true"/><response name="success" type="view" value="editguests2"/></request-map>
<view-map name="findguests" type="screen" page="component://OI/widget/OIScreens.xml#FindGuest"/>
<view-map name="editguests2" type="screen" page="component://OI/widget/OIScreens.xml#UpdateGuest2"/>

The FindGuest screen has to be added to the OIScreens.xml file:

Entries to add to OIScreens.xml
<screen name="FindGuest">
    <section>
        <actions>
            <set field="headerItem" value="findguests"/>
            <set field="titleProperty" value="FindGuests"/>
            <set field="FindGuestCtx" from-field="parameters"/>
			<set field="viewSize" value="20"/>
			<set field="viewIndex" value="0"/>
        </actions>
        <widgets>
            <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                <decorator-section name="body">
                    <section>
                        <widgets>
                            <decorator-screen name="FindScreenDecorator" location="component://common/widget/CommonScreens.xml">
                                <decorator-section name="search-options">
                                    <include-form name="FindGuests" location="component://OI/widget/OIForms.xml"/>
                                </decorator-section>
                                <decorator-section name="search-results">						
                                    <include-form name="FindGuestsList" location="component://OI/widget/OIForms.xml"/>
                                </decorator-section>
                            </decorator-screen>
                        </widgets>
                    </section>
                </decorator-section>
            </decorator-screen>
        </widgets>
    </section>
</screen>

<screen name="UpdateGuest2">
    <section>
            <actions>
                <set field="headerItem" value="findguests" />
            </actions>
            <widgets>
                <decorator-screen name="OICommonDecorator" location="${parameters.mainDecoratorLocation}">
                    <decorator-section name="body">
					<screenlet title="Update guest">
						<include-form name="Updateguest2" location="component://OI/widget/OIForms.xml"/>
					</screenlet>
                    </decorator-section>
                </decorator-screen>
            </widgets>
    </section>
</screen>

The fields FindGuestCtx, viewSize and viewIndex are used by the FindGuestList form and will be discussed there. This screen definition includes the FindGuests and FindGuestsList forms which have to be defined in the OIForms.xml file:

Entries to add to OIForms.xml
<form name="FindGuests" type="single" target="findguests" default-entity-name="HelloPerson">
	<field name="noConditionFind"><hidden value="Y"/>
	<!-- the line above enables a query although all fields are empty -->
	</field>
	<field name="helloPersonId" title="${uiLabelMap.HelloPersonId}"><text-find/></field>
	<field name="firstname" title="${uiLabelMap.FirstName}"><text-find/></field>
	<field name="lastname" title="${uiLabelMap.LastName}"><text-find/></field>
	<field name="searchButton" title="${uiLabelMap.CommonFind}" widget-style="smallSubmit"><submit button-type="button"/></field>
</form>

<form name="FindGuestsList" type="list" list-name="listIt" paginate-target="findguests" default-entity-name="HelloPerson" separate-columns="true"
	odd-row-style="alternate-row" header-row-style="header-row-2" default-table-style="basic-table hover-bar">
	<actions>
	<!-- Preparing search results for user query by using OFBiz stock service to perform find operations on a single entity or view entity -->
	  <service service-name="performFind" result-map="result" result-map-list="listIt">
		<field-map field-name="inputFields" from-field="FindGuestCtx"/>
		<field-map field-name="entityName" value="HelloPerson"/>
		<field-map field-name="orderBy" from-field="parameters.sortField"/>
		<field-map field-name="viewIndex" from-field="viewIndex"/>
		<field-map field-name="viewSize" from-field="viewSize"/>
	  </service>
	</actions>
	<field name="helloPersonId" title="${uiLabelMap.HelloPersonId}" widget-style="buttontext">
		<hyperlink target="editguests2" description="${helloPersonId}">
			<parameter param-name="helloPersonId"/>
		</hyperlink>
	</field>
	<field name="firstname" title="${uiLabelMap.FirstName}" sort-field="true"><display/></field>
	<field name="lastname" title="${uiLabelMap.LastName}" sort-field="true"><display/></field>
	<field name="comments" title="${uiLabelMap.Comment}"><display/></field>
</form>

The FindGuests form defines a search screen for guests in the data base. The FindGuestsList form displays the guests found according to the selected criteria.

The form FindGuestList is defined with the type list, because it shall display a list and the list-name listIt. This name is required to use the performFind script which is called in the form. The paginate-target="findguests" attribute points to the screen which displays this form and is required to enable pagination for lists which are displayed on several pages. Then there are a number of attributes which define the features of the list. The attribute odd-row-style="alternate-row" will provide a grey background for every second line, header-row-style="header-row-2" will provide a dark list header which allows to sort the columns FirstName and LastName, default-table-style="basic-table hover-bar" will draw a dark bar when the mouse is positioned over a line in the list. The separate-columns="true" attribute will put the fields into separate columns. For fields defined as „display“ this is the default and this attribute is not required.

Then the performFind service in the actions element of the form is called. The performFind service is defined in the $OFBIZ_HOME\framework\common\servicedef\services.xml file and the $OFBIZ_HOME\src\main\java\org\apache\ofbiz\common\FindServices.java file.

The performFind service fetches the data from the HelloPerson entity using the parameters in the FindGuestCtx context which was defined in the screen definition. This data is put into the listIt list. As long as you have only one list, you can use the value „parameters“ for the field-name input-fields directly. However, if you have multiple lists, pagination will only work when different contexts are used. The field-name orderBy allows to specify the sort order of the list. Here it is used when the list is sorted by clicking on the column headings. Using the viewSize field which is defined in the screen definition you can specify how many lines shall be displayed on a page. When there are more lines available, a bar will be displayed which lets you scroll through forward and backwards through the list. You e.g. set the value for viewSize to two and then you will see this bar with just a few guests entered in your database. The field viewIndex specifies the first page to be displayed on the screen if this shall not be the first one.

The form also defines the helloPersonId field as a hyperlink to call the Updateguests2 screen via the request-map in the controller.xml file to edit the selected guest.

In the FindGuestsList form, in the hyperlink, the “description” attribute defines the text displayed as the hyperlink. The term ${helloPersonId}inserts the “helloPersonId” variable. The “target” attribute defines the request map in the controller file for the hyperlink. The “parameter” element passes helloPersonId as a parameter to the Updateguest2 screen.
For the firstname and lastname fields the attribute sort-field="true" is specified. Therefore the list can be sorted by clicking on the column heading for these names.

Internationalization

At the very beginning of this tutorial we have removed the Chinese characters from the $OFBIZ_HOME\plugins\OI\config\OIUiLabels.xml file. Now we will extend this file to translate messages in the FindGuests form to German. This form contains e.g. the value ${uiLabelMap.OIFirstName}. OFBiz will look into the OIUiLabels.xml file and use the specified translation in there. This can make sense for English as well, you can convert the fieldname into a more user-friendly text this way. Add the following lines to the OIUiLabels.xml file within the resource:


OIUiLabels.xml
<property key="HelloPersonId">
	<value xml:lang="en">Guest-Id</value>
	<value xml:lang="de">Gäste-Nummer</value>
</property>
<property key="FirstName">
	<value xml:lang="en">First Name</value>
	<value xml:lang="de">Vorname</value>
</property>
<property key="LastName">
	<value xml:lang="en">Last Name</value>
	<value xml:lang="de">Familienname</value>
</property>

If you now restart OFBiz and then click on the FindGuests menu button, the fields will be named „First Name“ and „Last Name“. If you set your browser to German as the prefered language, the fields will be named in German instead. You could also easily translate the labels in the menu buttons as well because these were defined using the "uiLabelMap." prefix already.

Generate lists of the guests and export the data in various file formats

With OFBiz you can generate lists in various file formats, HTML, CSV, XLS and PDF for example. We will now see how you can write list definitions and output these in these file formats.

First we need additional entries in the OIMenus.xml file:

Entries to add in OIMenus.xml
<menu-item name="listguests" title="${uiLabelMap.ListGuests}">
<link target="listguests"/></menu-item>
<menu-item name="listguests2" title="${uiLabelMap.ListGuests2}">
<link target="listguests2"/></menu-item>
<menu-item name="listguestscsv" title="${uiLabelMap.ListGuestsCSV}">
<link target="listguestsCSV"/></menu-item>
<menu-item name="listguestsxls" title="${uiLabelMap.ListGuestsXLS}">
<link target="listguestsXLS"/></menu-item>
<menu-item name="listguestsPDF" title="${uiLabelMap.ListGuestsPDF}">
<link target="listguestsPDF"/></menu-item>

We have defined two different list forms, called ListGuests and ListGuests2, for demonstration purposes. Then we add targets for the screens calling these forms in the controller.xml file:

Entries to add in controller.xml
<request-map uri="listguests"><security https="true" auth="true"/><response name="success" type="view" value="listguests"/></request-map>
<request-map uri="listguests2"><security https="true" auth="true"/><response name="success" type="view" value="listguests2"/></request-map>
<request-map uri="listguestsCSV"><security https="true" auth="true"/><response name="success" type="view" value="listguestsCSV"/></request-map>
<request-map uri="listguestsXLS"><security https="true" auth="true"/><response name="success" type="view" value="listguestsXLS"/></request-map>
<request-map uri="listguestsPDF"><security https="true" auth="true"/><response name="success" type="view" value="listguestsPDF"/></request-map>

<view-map name="listguests" type="screen" page="component://OI/widget/OIScreens.xml#Listguest"/>
<view-map name="listguests2" type="screen" page="component://OI/widget/OIScreens.xml#Listguest2"/>
<view-map name="listguestsCSV" type="screencsv" content-type="text/csv" page="component://OI/widget/OIScreens.xml#Listguestcsv"/>
<view-map name="listguestsXLS" type="screenxls" content-type="application/vnd.ms-excel" page="component://OI/widget/OIScreens.xml#Listguestxls"/>
<view-map name="listguestsPDF" type="screenfop" content-type="application/pdf" page="component://OI/widget/OIScreens.xml#ListguestPDF"/>

For the Listguest and Listguest2 screens we use the type „screen“. For generating a CSV file we use the attributes type="screencsv" content-type="text/csv", for the Excel file we use type="screenxls" content-type="application/vnd.ms-excel" and for a PDF file we use type="screenfop" content-type="application/pdf".

This time we need no scripts and therefore have no request-maps for these. Then we define the screens for these lists in the OIScreens.xml file:

Entry to add in OIScreens.xml
<screen name="Listguest">
	<section>
		<actions>
			<set field="headerItem" value="Listguest"/>
		</actions>
		<widgets>
			<decorator-screen name="OICommonDecorator" location="component://OI/widget/CommonScreens.xml">
			  <decorator-section name="body">
				<include-form name="ListGuests" location="component://OI/widget/OIForms.xml"/>
			  </decorator-section>
			</decorator-screen>
		</widgets>
	</section>
</screen>

<screen name="Listguest2">
	<section>
		<actions>
			<set field="headerItem" value="Listguest"/>
		</actions>
		<widgets>
			<decorator-screen name="OICommonDecorator" location="component://OI/widget/CommonScreens.xml">
				<decorator-section name="body">
					<include-form name="ListGuests2" location="component://OI/widget/OIForms.xml"/>
				</decorator-section>
			</decorator-screen>
		</widgets>
	</section>
</screen>

<screen name="Listguestcsv">
	<section>
		<actions>
			<set field="headerItem" value="Listguestcsv"/>
			<set field="viewSize" value="10000"/>
		</actions>
		<widgets>
			<include-form name="ListGuests2" location="component://OI/widget/OIForms.xml"/>
		</widgets>
	</section>
</screen>

<screen name="Listguestxls">
	<section>
		<actions>
			<set field="headerItem" value="Listguestxls"/>
			<set field="viewSize" value="10000"/>
		</actions>
		<widgets>
			<include-form name="ListGuests" location="component://OI/widget/OIForms.xml"/>
		</widgets>
	</section>
</screen>

<screen name="ListguestPDF">
	<section>
		<actions>
			<set field="viewSize" value="10000"/>
			<set field="pageLayoutName" value="main-page-landscape"/>
			<entity-condition entity-name="HelloPerson" list="hellopersons">
				<order-by field-name="helloPersonId"/>
			</entity-condition>
		</actions>
		<widgets>
			<decorator-screen name="FoReportDecorator" location="component://common/widget/CommonScreens.xml">
			<decorator-section name="body">
				<container>
					<label text="Guests and comments" style="h1"/>
				</container>
				<include-form name="ListGuests2" location="component://OI/widget/OIForms.xml"/>	
			</decorator-section>
			</decorator-screen>
		</widgets>
	</section>
</screen>

As you can see the Listguestcsv and ListguestPDF screens include the form ListGuests2.  "viewSize" is set to 10000 to avoid pagination. Here are the required forms which have to be added to the OIForms.xml file:

Entries to add in OIForms.xml
<form name="ListGuests" type="list">
	<actions>
		<entity-condition entity-name="HelloPerson">
			<order-by field-name="helloPersonId"/>
		</entity-condition>
	</actions>
	<auto-fields-entity entity-name="HelloPerson" default-field-type="display"/>
</form>

<form name="ListGuests2" type="list" list-name="hellopersons">
	<actions>
		<entity-condition entity-name="HelloPerson">
			<order-by field-name="helloPersonId"/>
		</entity-condition>
	</actions>
	<field name="helloPersonId"><hidden/></field>
	<field name="firstname"><display/></field>
	<field name="lastname"><display/></field>
	<field name="comments"><display/></field>
</form>

The ListGuest2 form does not use auto-fields-entity but specifies the fields separately. This way you can display only a selection of the fields of an entity if required. For demonstration we have removed the helloPersonId field in this form.

That's all there is to it. You can now click on the different buttons in the menu and see the results. Here are screenshots of the ListGuest form, a generated CSV file and a PDF output.

Printing Barcodes

In the example above we have used the attributes: type="screenfop" and content-type="application/pdf" in the controller.xml file. FOP specifies the Apache FOP (Formatting Objects Processor) extension. This extension is a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter. It is a Java application that reads a formatting object (FO) tree and renders the resulting pages to a specified output. Details are described in this document: https://xmlgraphics.apache.org/fop/
So in the example above we have used screenfop to generate a PDF document and now we will use screenfop to generate a barcode.

First open the file framework\webapp\config\fop.xconf and modify the line 34 to: <base>http://localhost:8443to use the current port number for our installation.

fop.xconf
<!-- Base URL for resolving relative URLs -->
   <base>http://localhost:8443</base>  

Then define a new menu button for our example:

Entries to add in OIMenus.xml
<menu-item name="barcode" title="${uiLabelMap.Barcode}"><link target="barcode"/></menu-item>

Next add the following request-maps into the controller.xml file:

Entries to add in controller.xml
<request-map uri="barcode"><security https="true" auth="true"/><response name="success" type="view" value="PdfBarcode"/></request-map>
<view-map name="PdfBarcode" type="screenfop" content-type="application/pdf" encoding="none" page="component://OI/widget/OIScreens.xml#PDFBarcode"/>

This is the barcode screen for the screens.xml file:

Entry to add in OIScreens.xml
<screen name="PDFBarcode">
     <section>
          <actions>
                <set field="headerItem" value="barcode"/>				
				<set field="helloPersonId" value="123456"/>
				<set field="firstname" value="Hans"/>
				<set field="lastname" value="Dampf"/> 
				<set field="comments" value="This is the barcode of the Id of:"/>
          </actions>
          <widgets>
				<platform-specific>
					<xsl-fo>
                        <html-template location="component://OI/webapp/OI/OIBarCode.fo.ftl"/>
					</xsl-fo>
                </platform-specific>
          </widgets>
     </section>
</screen>

The screen above calls the OIBarCode.fo.ftl file which generates the barcode:

OIBarCode.fo.ftl
<#escape x as x?xml>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

    <fo:layout-master-set>
        <fo:simple-page-master master-name="main" page-height="4in" page-width="8in"
                               margin-top="0.5in" margin-bottom="0.25in" margin-left="0.25in" margin-right="0.25in">
            <fo:region-body margin-top="0in"/>
            <fo:region-before extent="0in"/>
            <fo:region-after extent="0in"/>
        </fo:simple-page-master>
    </fo:layout-master-set>

    <fo:page-sequence master-reference="main">
        <fo:flow flow-name="xsl-region-body" font-family="Helvetica">
		<fo:block text-align="center">
		<#-- This text is displayed above the barcode -->
        ${firstname!} ${lastname!}
        </fo:block>
            <fo:block text-align="center" font-size="48pt">
			123
                <fo:instream-foreign-object>
                    <barcode:barcode xmlns:barcode="http://barcode4j.krysalis.org/ns"
						message="${helloPersonId!}">    
						<#-- message is the number in the barcode -->
                        <barcode:code128>
                            <barcode:height>0.5in</barcode:height>
                            <barcode:module-width>.375mm</barcode:module-width>
                        </barcode:code128>
                        <barcode:human-readable>
                            <barcode:placement>bottom</barcode:placement>
                            <barcode:font-name>Helvetica</barcode:font-name>
                            <barcode:font-size>18pt</barcode:font-size>
                            <barcode:display-start-stop>false</barcode:display-start-stop>
                            <barcode:display-checksum>false</barcode:display-checksum>
                        </barcode:human-readable>
                    </barcode:barcode>
                </fo:instream-foreign-object>
				456
            </fo:block>
            <fo:block><fo:leader/></fo:block>			
			<fo:block text-align="center">
				${comments!} ${firstname!} ${lastname!} in the helloPerson entity.
			</fo:block>
        </fo:flow>
    </fo:page-sequence>
</fo:root>
</#escape>

The tag fo:instream-foreign-object defines the barcode to print. In there there is the message variable into which you move the value of the barcode. The size of the barcode is defined with the tags:

<barcode:height>0.5in</barcode:height>

<barcode:module-width>.375mm</barcode:module-width>

Text that shall be printed to the left or right of the barcode has to be put into the same block the tag fo:instream-foreign-object is in. Text above or below the barcode has to be specified in separate blocks above or below this block. When you click on the barcode menu button, the following PDF document will be generated:

See these links for further information regarding the barcode implementation in OFBiz:
Instructions for the Apache FOP extension
The Barcode XML format
The Apache FOP Project
Extensible Stylesheet Language (XSL)

Security

In the controller.xml file we have defined the request-maps with this tag: <security https="true" auth="true"/> This defines that the application can only be accessed using the HTTPS protocol and you need to login to use the requested application. The controller.xml file includes the component://common/webcommon/WEB-INF/common-controller.xml file. This file contains definitions for the request-maps uri="checkLogin", "login", "logout", "forgotPassword", and "passwordChange" to handle these processes.

The permissions required to use the OI plugin are set in the $OFBIZ_HOME\plugins\OI\ofbiz-component.xml file. In the webapp name="OI" tag you find the definition: base-permission="OFBTOOLS,OI". So the user has to have the permissions for OFBTOOLS or OI to use the OI plugin. You usually log in as "admin" and this user has the permissions for OFBTOOLS. So you have no problem using your plugin. To edit the user access rights, you select the party application from the top menu. Then you click find to get a list of the registered users and click on the Party Id to edit the party. The profile of the selected party will be presented and you can see how many and what User Ids are defined for this party.

As an example click on admin, which is the person 'THE PRIVILEGED ADMINISTRATOR'. In the profile you can see that admin has multiple login Ids listed in the User Name(s) form. Click on the 'Security Groups' button of the admin user login. This will then show that the user login 'admin' is part of the 'FULLADMIN' and the 'MYPORTAL_EMPLOYEE' group. If you click on the 'FULLADMIN' group and then on the Permissions button, you will see all the permissions for the FULLADMIN security group. Navigate between the permissions till you find the OFBTOOLS_VIEW permissions. 'OFBTOOLS_VIEW Permission to access the Stock OFBiz Manager Applications.' This confirms that the userlogin 'admin' has the permission 'OFBTOOLS'. If you do not click on the 'Security Groups' button mentioned above but the 'Edit' button in the User Name(s) form, you can change the password for the user admin.

Additional information about OFBiz security can be found here:
OFBiz Security Permissions
Security Services – Developer
Hotwax - How to Setup User Permissions
Keeping OFBiz secure from external exploits

Appendix

Install the Eclipse IDE

The Eclipse IDE provides excellent support for developing OFBiz XML files. These files have a xsi:schemaLocation attribute which points to an XSD file. There is an XSD file for e.g. widget-screen, widget-form or Minilang/simple-methods. This file contains the commands and their descriptions relevant for the particular file you are editing. Eclipse will e.g. display a tooltip window when you move your mouse on an tag in your source file and provide autocompletion.

Download the Eclipse IDE for Java from here: https://www.eclipse.org/downloads/packages/ There are packages for Windows and Linux. Linux distributions usually offer Eclipse in their repositories. You may also get into Help → Eclipse Marketplace and e.g. install the Eclipse XML Editors and Tools.

Here is further information about using Eclipse with OFBiz: Eclipse+Tips

Install OFBiz on Microsoft Windows 10

1. Download the Java SE JDK

For OFBiz you need the JDK version 8. You can download this from here:

https://adoptopenjdk.net/releases.html?variant=openjdk8&jvmVariant=hotspot

For Windows 10 in 64 bit select the matching "msi" file, this is the exact link:
https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u265-b01/OpenJDK8U-jdk_x64_windows_hotspot_8u265b01.msi

2. Execute the downloaded file and install the Java JDK and JRE. Do not modify the default file paths.
During the installation there will be a window called "User defined Setup" where you can select the components to install. In there you can specify that the JAVA_HOME environment variable shall be set during the install process. Enable this and select the local disk as the location where you will install the Java JDK.

3. Download the OFBiz ZIP archive from this link:
https://ofbiz.apache.org/download.html

4. Unpack the directory apache-ofbiz-17.12.04 in the zip file. You can rename this directory to e.g. c:\ofbiz

5. Set the environment variables
You can skip this step, if you selected that these variables shall be set during the installation. If you missed this, you have to set them manually:

Get into Control panel – extended system settings – environment variables, set a new JAVA_HOME variable:
JAVA_HOME to C:\Program Files\Java\jdk1.8.0_265

Then test whether this variable has been set successfully in a command window:
echo %JAVA_HOME%

After that, add this path to the system path variable:
control panel – extended system settings – environment variables, existing path variable
%JAVA_HOME\bin% (=C:\Program Files\Java\jdk1.8.0_265\bin)
Close these windows again to get Windows to enable this path variable.

Now test the path variable enter in a new command window:
java -version
javac -version
If these commands output java version "1.8.0_261" and javac 1.8.0_261, your are ok.

6. Install OFBiz
The INSTALL file provides detailed instructions. Follow these steps:

a) open a command window in administrator mode
For that, right-click on the start button, open search, enter "cmd" and then select cmd to execute as administrator. Now get into the ofbiz directory you created, e.g. c:\ofbiz.

b) enter this command:
init-gradle-wrapper (=init-gradle-wrapper.bat)
If this does not work, you have to set the script policy in this batch file to bypass. You can either edit the batch file or enter on the command line:
type init-gradle-wrapper.bat
Then enter the last line manually using bypass:
Powershell.exe -executionpolicy bypass -File gradle\init-gradle-wrapper.ps1

c) run gradlew.bat:
gradlew cleanAll loadAll (=gradlew.bat cleanAll loadAll)
Allow this batch file to download files when queried by Windows

If you do not want to include the demo data use this command instead:
gradlew cleanAll
gradlew "ofbiz --load-data readers=seed,seed-initial" loadAdminUserLogin -PuserLoginId=admin
This command will build OFBiz and load the seed data and create the admin user that you can use to login.

d) Finally start OFBiz by entering:
gradlew ofbiz
You can make a startofbiz.bat file with this command and generate a link to that from your desktop to start it conveniantly when required.

OFBiz will compile and start. After the message "OFBiz is started and ready" appears, followed by several further messages, it will stop with the prompt :ofbiz. This means you can use OFBiz now.
Ignore the % progress indicator because this task does not end as long as OFBiz is running.

7. Run OFBiz
Open a browser window and enter https://localhost:8443/webtools (or ecommerce, or catalog). If you selected webtools, on the next screen there is a small login link you have to click for the login prompt. Otherwise you will get the login prompt immediately.
The default login is admin, password ofbiz

9. Stopping OFBiz

You may use Ctrl+C in the terminal where you started OFBiz.

Install OFBiz on a Linux system

1. Download the Java SE JDK

For OFBiz you need the JDK version 8. You can download this from here:
https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot

This link will direct you to a screen where you can select OpenJDK 8 (LTS) and JVM HotSpot. Then click on "Latest relase" and download the tar.gz file.
Unpack this file and, using sudo, move the unpacked directory, e.g. jdk8u275-b01, to your "/opt" directory.

2. Download the OFBiz ZIP archive from this link:
https://ofbiz.apache.org/download.html

Then unpack the directory apache-ofbiz-17.12.04 from the zip file into your home directory. You can rename this directory to e.g. c:\ofbiz.

3. Set the JAVA_HOME, JRE_HOME, OFBIZ_HOME and PATH environment variables.

To do that you can execute the following commands from the commandline or set up a script containing these commands:

export JAVA_HOME=/opt/jdk8u275-b01
export JRE_HOME=/opt/jdk8u275-b01/jre
export PATH=$PATH:/opt/jdk8u275-b01/bin:/opt/jdk8u275-b01/jre/bin
export OFBIZ_HOME=/home/(your name)/apache-ofbiz-17.12.04

If you name the script e.g. "setpaths.sh" you have to execute it with the dot command, as
". ./setpaths.sh".

You can also add these commands to your ~/.bashrc file so that they are always set when your system starts.
Enter echo $PATH on the command line and check if the variables are set ok.

4. Install OFBiz

The INSTALL file provides detailed instructions. Follow these steps:

Get into the ofbiz directory in your home directory and execute the following scripts:
./gradlew init-gradle-wrapper
./gradlew cleanAll loadAll

If you do not want to include the demo data use the commands below instead of ./gradlew cleanAll loadAll:
./gradlew cleanAll
./gradlew "ofbiz --load-data readers=seed,seed-initial" loadAdminUserLogin -PuserLoginId=admin
This command will build OFBiz and load the seed data and create the admin user that you can use to login.

This will install OFBiz.

5. Finally start OFBiz by entering:

./gradlew ofbiz

OFBiz will compile and start. After the message "OFBiz is started and ready" appears, followed by several further messages, it will stop with the prompt :ofbiz. This means you can use OFBiz now.
Ignore the % progress indicator because this task does not end as long as OFBiz is running.

6. Run OFBiz
Open a browser window and enter https://localhost:8443/webtools (or ecommerce, or catalog) If you selected webtools, on the next screen there is a small login link you have to click for the login prompt. Otherwise you will get the login prompt immediately.
The default login is admin, password ofbiz

7. Stopping OFBiz

You may use Ctrl+C in the command window where you started OFBiz.




  • No labels