Openbiz Development Guide

Show openbiz views (pages)

- User URL string as controller.php?view=...&form=...&rule=...&mode=...&OtherInputs...

- Use client javascript GoToView(view, rule, loadPageTarget)

Input Description Sample
view bizview name shared.CalDetailView
form bizform name. This form must not depend on (a subform of) another bizform shared.FMCalDetail
rule the search rule of a bizform

syntax is "form.ctrl operator value".
operator can be =,>,>=,<,<=,!=. "LIKE %" SQL format is also valid rule. "AND", "OR" can be used to add more restriction.

FMSponsor.spr_exp>=12000 

FMSponsor.spr_name=\'Midas Auto\' OR FMSponsor.spr_id=\'SPSR_2\'

FMSponsor.spr_name LIKE \'S%\'

loadPageTarget the window or frame target to load the page  

Build view and form web templates

Smarty template engine is used in OpenBiz to render the HTML output. We provides some basic templates for render BizForm and BizView. You can create your own templates. Remember that an openbiz form must begin with a form tag <form id={$name} name={$name}>. For detail, please refer to http://smarty.php.net/docs.php 

Smarty engine recognizes the subdirectories under the main template directory. So you can create a fold under demoapp/templates/ and put template files there. When you give template file name in metadata file, just give Template="subdir/view.tpl".

- Draw BizForm as a html table

In BizForm xml file, you need give DataFormat as "block" and FormatStyle as table style which is given in css file.
<Mode ... TemplateFile="list.tpl" ... DataFormat="htmltable" FormatStyle="tbl"/>

In template file, {$fmttable}represents form whole html table.

Please refer to demo files FMEvent.xml and list.tpl

- Draw BizForm as a set of controls

Since the array dataformat output to smarty template is associated array, user can easily layout controls by their names. The associated array has format as fields[control_name][label] and fields[control_name][control].

Please refer to demo files FMEvent.xml and edit.tpl

Three DataFormats decide what data is passed to templates

DisplayMode DataFormat Data passed to template Controllability on template Example
array - Variable name to use in template is $fields
- associated array of single record
- array (control_name=>array ("label"=>label_text, "control"=>control_html))
Highly controllable in template edit.tpl
table - Variable name to use in template is $table
- 2D array of multiple records
- array (index=>array (control1_name=>value1, control2_name=>value2, ...))
  array[0] is column header
  array[1-n] are the 1st record to nth record
Highly controllable in template rptlist.tpl
block - Variable name to use in template is $block
- HTML string
lowly controllable in template list.tpl

Using BizDataObj functionalities

Only basic usage of BizDataObj is listed below. Please refer to the API doc for details.

Query and get results

1.x BizObj is renamed as BizDataObj in 2.0, because it mainly acts as business data unit. 

1.x use GetField and SetField to do get and set field values. In 2.0, no field level methods, but using GetRecord() and UpdateRecord() method to do record level operations. The idea behind is to make easier coding to retrieve query results and provide coarse-grained interfaces between 2 layers.

1.x way to query and get records

$bizobj->SetSearchRule("...");    // set search rule
$bizobj->RunSearch(-1);    // run query against the database tables. 
$bizobj->Home();    // reset the result record set
$hasVal = $bizobj->MoveFirst();    // move cursor to first record
while ($hasVal)
{
    foreach ($fieldlist as $field)
        $field_val[] = $bizobj->GetField($field);
   
// user logic code can be put here. 
    $hasVal = $bizobj->MoveNext();    // move cursor to next record
}

2.0 way to query and get records - option 1

$bizobj->SetSearchRule("...");    // set search rule
$bizobj->RunSearch(-1);    // run query against the database tables. query results can be cached
while ($recArray = $bizobj->GetRecord(1))    // get current record and move the cursor to the next record 
{
    // user logic code can be put here. recArray is an associated array whose key is "fieldname" and value is value of the field
}

2.0 way to query and get records - option 2

$bizobj->FetchRecords ($searchRule, $resultSet, $numRecords);    // get the whole recordset (list of recArray), query results won't be cached

Insert, Update and Delete a record 

// Insert Record
$recArr = $boUser->NewRecord();    // NewRecord returns an empty record array with a generate Id field
$recArr["LastName"] = "Test";
$recArr["FirstName"] = "Add";
$ok = $boUser->InsertRecord($recArr);
if (!$ok)     // handle error message
  print $boUser->GetErrorMessage();

// Update a record
$boUser->SetSearchRule("[Id]='USER-1006'");
$boUser->RunSearch();
$recArray = $bizobj->GetRecord(0);
$recArray["FirstName"] = "Update";
$ok = $boUser->UpdateRecord($recArray);
if (!$ok)     // handle error message
  print $boUser->GetErrorMessage();

// Delete a record
$boUser->SetSearchRule("[Id]='USER-1006'");
$boUser->RunSearch();
$recArray = $bizobj->GetRecord(0);
$ok = $boUser->DeleteRecord($recArray);
if (!$ok)     // handle error message
  print $boUser->GetErrorMessage();

Using BizForm functionalities

User can configure function in BizForm metadata file to invoke appropriate action on server side. The commonly used BizForm methods are listed below. Please refer to the API doc for details.

Query and show results

  • SearchRecord - show the query record mode
  • RearchRecord - Issue the query with user input in query mode and return to read mode

Insert a new record

  • NewRecord - show the new record mode
  • SaveRecord - Save current edited record with user input in query mode and return to read mode

Update a record

  • EditRecord - show the edit record mode on current focus record
  • SaveRecord - Save current edited record with user input in query mode and return to read mode

Delete a record

  • DeleteRecord - delete the current focus record

Copy a record

  • CopyRecord - copy the current focus record to new record

Page Navigation

  • MoveNext - show next page records
  • MovePrev - show previous page records

User input validation

According the topic at http://www.boringguys.com/?p=30, there are 3 different types of data validation checks one can do:

  • Syntactic validation - check data syntax 
  • Semantic validation - check one piece of data makes sense in regards to other incoming data
  • Domain or model validation - check data against another source of acceptable values

Syntactic and semantic validation can be implemented on client side and service side. Domain validation involves query data source, it is usually done in service code that connects to database.

Client side form validation

Openbiz does input validation using jsval library (LGPL, http://jsval.fantastic-bits.de/). To configure a validation rules for an openbiz HTMLControl, developers can use jsVal's "Inline initialization" to add validation attributes to HTMLAttr part of HTMLcontrol and FieldControl.

<Control Name="" Type="" HTMLAttr="jsval validation attributes here" ... /> 

Example: <BizCtr Name="email" Type="Text" HTMLAttr="regexp='JSVAL_RX_EMAIL' minlength=5..." ... /> 

In addition, HTMLControl has a Required (=Y|N) attribute. It is passed to pass as $fields['required'] (along with $fields['label'] and $fields['control']) to smarty template. In smarty template (edit.tpl), has code {if $item.required=="Y"} <span class='required'>*</span>{/if} to append a * to the label. Then the required control's label looks as "Email *"

Server side input validation

Openbiz mainly support validation on BizDataObj. In BizField, Required and Validator attributes are used for such purpose. Please see the BizDataObj configuration chapter for explanation of these two attributes.

Developers can use customer class to override DataObj::ValidateInput() method or Field::Validate() method achieve their own validation rules.

BizForm has an empty ValidateForm method which may be overridden by customer classes to implment special validation logic.

Extend OpenBiz classes

OpenBiz provide many functionalities to building a complicated web application. But different application has different requirement, so you may extend OpenBiz packages to build customer applications.

Extend server side classes

Because OpenBiz packages are based on object-oriented design, you can easily build up your own object by extending these packages and inherit all useful functions provided by them.

Extended classes are automatically loaded on demand. Extended class  must be included in a file with format as ClassName.php.

Use can specify class name for BizDataObj, BizField, BizView, BizForm, BizCtrl, Control and PluginService by filling the "Class" attribute of in metadata file

Base Class Where to specify the extend class
BizDataObj <BizDataObj ... Class="" ...>
BizField <BizField ... Class="" ...>
BizView <BizView ... Class="" ...>
BizForm <BizForm ... Class="" ...>
FieldControl <BizCtrl ... Class="" ...>
HTMLControl <Control ... Class="" ...>

Example: Extend BizDataObj and BizForm

/**
* class BOUser is the BizDataObj class to implement USER logic object
*/

class BOUser extends BizDataObj 
{
    function BOUser($xmlFile=null) {} 

      function my_special_function() {}    // new function

/**
* class FMUser is the BizForm class to implement USER UI object
*/

class FMUser extends BizForm 
{
    function FMUser($xmlFile=null) {} 

      function my_special_function() {}    // new function

      function Render() {}    // override Render()
}

Extend client side classes

BizForm is the main server side presentation class, it has its counterpart class on client browser side. In the BizForm metadata, users can specify client side class in jsClass attribute of BizForm element. For example.

<BizForm Name="FMEvent" ... Class="BizForm" jsClass="jbForm"...>

Openbiz provides two client side classes AjaxForm and jbForm that is a subclass of AjaxForm.

AjaxForm methods:

  • CallFunction(method, params_array) - convert function to request and send it to server. A server side BizForm method is invoked by this method if there's no specific method implemented in the client class. For example, a BizForm, whose jsClass=jbForm, has a method named "SelectRecord". Openbiz will check if "SelectRecord" method is defined in jbForm class. If yes, it calls jbForm's SelectRecord(params_array), otherwise it calls CallFunction(method, params_array).
  • CallbackFunction(retContent) - Ajax callback function. It passes the returned content to Show().
  • CollectFormData() - collect form data into a message in Ajax call
  • Show(retContent) - called by callback function to show Ajax returned content in the form

jbForm methods:

  • CollectFormData() - overriding AjaxForm CollectFromData(), add additional "selected row" in the form data
  • SelectRecord(params_array) - special logic on select record call
  • SortRecord(params_array) - special logic on sort record call
  • DeleteRecord(params_array) - special logic on delete record call

To implement UI logic on client side, developers need to create their own client side class like jbForm and give the class name to jsClass attribute of BizForm element. If you need special logic in some methods, add these methods in the client class, which can be subclass of either AjaxForm or jbForm. These methods can be pure client code or it can this.CallFunction(...) to send the request to server.

An example:

Form metadata file is like:

<BizForm ... Class="MyForm" jsClass="MyjbForm"...>
...
   <BizCtrl Name="mybutton" ...>
      <EventHandler Name="onclick" Event="onclick" Function="server_MyButtonClick"/>
      <EventHandler Name="onblur" Event="onblur" Function="js_MyButtonBlur"/>
...
</BizForm>

In server side MyForm class, server_MyButtonClick() needs to be defined.

In client side MyjbForm class, js_MyButtonBlur() needs to be defined. Of course, if server_MyButtonClick() is defined in client class, server_MyButtonClick() needs to call this.CallFunction("server_MyButtonClick", params_array) at the end of the method to send the request to server MyForm server_MyButtonClick() method. The client side code will be like:

function MyjbForm(name) {}
MyjbForm.inheritsFrom(jbForm); //set inheritance
MyjbForm.prototype.js_MyButtonBlur = function (params_array)
{
    // put client logic here
    ...
}
MyjbForm.prototype.server_MyButtonClick= function (params_array)
{
    // put client logic here
    ...
    // send request to server side at the end
    this.CallFunction("server_MyButtonClick", params_array);
}

Implement Plug-in Service

Openbiz customer can write their special logic by implement Plug-in Service. Plugin services are also metadata-driven objects. Service code is under bin/service and metadata is under /metadata/service. The plugin service metadata only gives the service name, package and implementing class. Any xml elements can be child of the root PluginService element. This is because different services may have different metadata configuration. Please refer to the Appendix to see the DTD of plugin service metadata xml file.

Openbiz core library includes services under openbiz/bin/service/ and their metadata files under openbiz/metadata/service/.

  • excelService.php - print Excel output with HTML or CSV formats
  • authService.php - authentication service, please refer to next chapter for usage
  • accessService.php - view access control service, please refer to next chapter for usage
  • profileService.php - user profile service, please refer to next chapter for usage
  • logService.php - write log to file
  • ioService - data winput/output service
  • pdfService.php -  prints PDF report
  • emailService.php - integrated with PHPMailer to send emails through smtp mail server.
  • doTriggerService - BizDataObj trigger service
  • auditService - audit trail service to trace data change

In the application, you can specify your own plugin service implementation with following methods.

  1. Change the service metadata content under your_app/metadata/service/, still use the core service. For example, you can write different accessService.xml to control the view access rule, but the implementing class is accessService.php in core library. The accessService.xml is like 
    <PluginService Name="accessService" Package="service" Class="accessService">
       application specific view access definition
    </PluginService>
  2. Specify the service implementing class. For example, different applications have different profile service. The profileService.xml is like 
    <PluginService Name="profileService" Package="service" Class="your_own_service_class">
    </PluginService>
  3. Combine the above 2 methods.

Write a plug-in service

Implement the class and method in the ClassName.php
A input argument of the method are the caller's object name (a BizForm name) and the input data string (a collection of form values from client browser). It implements the user-specific business logic and returns void. See the following code snippet.

class pdfService
{
 function pdfService() {}

 function renderView($viewName)
 {
  global $g_BizSystem;
  // get the view object and render the view to a html string
  $viewobj = $g_BizSystem->GetObjectFactory()->GetObject($viewName);
  if($viewobj) {
    $viewobj->SetConsoleOutput(false);
  $sHTML = $viewobj->Render();
  // convert HTML to PDF
  // ... customer code to do the convert 
 }
}

Call a plug-in service method

  • Call service in form. Define the caller function in BizForm metadata file
    <... Function="CallService(ClassName,MethodName)"...> is to call a method "MethodName" of the service "ClassName". ClassName is a class which defined in ClassName.php under bin/usrlib/ directory
  • Call service with url. Use url like bin/BizController.php?F=Invoke&P0=[servicename]&P1=[methodname]&P2=[parameter1]... The example is bin/BizController.php?F=Invoke&P0=[pdfService]&P1=[renderView]&P2=[demo.ReportView]
  • Call service in code. 
    global $g_BizSystem;
    $svcobj = $g_BizSystem->GetService($class);
    $svcobj->$method($this->m_Name);

Implement authentication, view and data access control

User authentication

Openbiz uses authentication service (usrlib/authService.php) to authenticate username and password

public function AuthenticateUser ($userid, $password) is called to authenticate user. 

  • AuthenticateUser by default querys on "User Id" and "Password" fields defined in metadata/shared/BOAuth.xml. If input user id and password is found in in BOAuth, AuthenticateUser returns true. AuthenticateUser method is to be modified to fit customer logic.
  • metadata/shared/SignupView.xml is the default login view. This view can be configured for change the login look and feel.

Role-based view access control

View access control depended on the AccessControl attribute in view metadata file in Openbiz 1.x. From Openbiz RC1, openbiz implements role-based view access control in its new security architecture. 

Openbiz uses profile service (usrlib/profileService.php) to get user profile that includes "role". Then match this role to access service (usrlib/accessService.php) to determine the users' accessibility to the view

public function GetProfile ($userid=null) is called to get user profile array which is an associated array with profile key and profile value pairs.

  • Customer must replace the default GetProfile method in openbiz package, because the default GetProfile method returns some randomly made array.

public function AllowViewAccess ($viewName, $role=null) is called to check the accessibility of a view

  • accessService has a configuration file (accessService.xml) that defines the view access permission. Please see an example below.

  • The xml configuration file is easy to understand. Customer needs to put their own logic in the accessService.xml.

Attribute-based data access control

Openbiz uses profile service (usrlib/profileService.php) to get user profile that includes attributes. Then based on these attributes to determine browse/update/delete permission of data record. The data access permissions are controlled in BizDataObj.

User can refer to attributes by @profile:attribute in BizDataObj metadata files. Profile attributes are all from profileService. When user first login, profileService returns a profile array that is saved in session. If an attribute is found in the profile array, the attribute value is returned. If the profile array doesn't contain such attribute, profileService method GetAttribute ($userid, $attr) is called to return the attribute value. Customer must implement GetAttribute method with their own logic.

- Data browse permission. A typical access control requirement is to limit the accessibility of table record to different users. This feature can be achieved by configuring AccessRule in BizDataObj metadata file. We do the configuration with the following two scenario.

Scenario  Configuration
Event can be only accessed by the owner

(personal access control)

<BizDataObj Name="Event" ... AccessRule="[OwnerId]='@profile:ORGID'" ...>

<BizField Name="OwnerId" Column="OWNER_ID"/>

Event can be accessed by all invited attendees 

(if the login user is one of the attendees, he can access the event)

<BizDataObj Name="Event" ... AccessRule="[AttendeeId]='@profile:USERID'" ...>

<BizField Name="AttendeeId" Column="ATTD_ID" Join="Attendee"/>

<Join Name="Attendee" Table="evts_attds" Column="EVT_ID" ColumnRef="SYSID" JoinType="LEFT JOIN"/>

- Data update permission. UpdateCondition is to control the record update permission. UpdateCondition expects true or false. A sample is UpdateCondition = "[OrgId]=={@profile:ORGID}". {} is the evaluated as simple expression.

- Data delete permission. DeleteCondition is to control the record delete permission. DeleteCondition expects true or false. A sample is DeleteCondition = "'admin'=={@profile:ROLEID}"

Build a multi-step wizard view

Wizard is a sequence of forms (questionnaires) which guide users to complete a complicated task. An example is filling an expense report which include different types (hotel, airfare, other activities...) of expenses.

Openbiz support wizard by easy metadata configuration. Openbiz wizard has following features:

  • Each form has "Next", "Back", "Cancel" and "Finish" navigation buttons.
  • If user clicks cancel button, user input data is not saved to database.
  • User input data won't to commit to database until user clicks finish button.

To see sample of wizards, go to Test view, click on "Wizard tests +" submenus. As you can see from the demo, wizard can be accessed with an url. 

As shown in the 2nd link, developer can give the parameter on the fly to run the wizard against given record

Configuration of wizard view

Openbiz wizard is a regular view that contains several wizard forms. So configuring a wizard in openbiz is no harder than  configuring a BizView. The difference between a wizard view and regular view is wizard view show the wizard forms one by one based on the their sequence in view metadata file, but regular view show all forms in one page. Below is an example of a wizard view metadata file.

<?xml version="1.0" standalone="no"?>
<BizView Name="EventWizardView" Description="" Package="demo" Class="BizViewWizard" Template="view.tpl">
   <ControlList>
      <Control Name="wizardForm1" Form="FMEventWzd1"/>
      <Control Name="wizardForm2" Form="FMEventWzd2"/>
   </ControlList>
   <Parameters>
      <Parameter Name="Evt_Id" Value="" />
   </Parameters>
</BizView>

Configuration of wizard forms

A wizard form is a regular form with class as BizFormWizard or BizFormNewWizard (or their subclasses).

  • BizFormWizard - to edit a record
  • BizFormNewWizard - to create a record

This is a typical wizard form metadata file.

<?xml version="1.0" standalone="no"?>
<BizForm Name="FMNewEventWzd1" Package="demo" Class="BizFormNewWizard" jsClass="jbForm" Title="Enter event information:" SearchRule="" Description="Event BizForm" BizDataObj="BOEvent">
  <DisplayModes>
    <Mode Name="EDIT" TemplateFile="edit.tpl" DataFormat="array" FormatStyle="" />
  </DisplayModes>
  <BizCtrlList>
    <BizCtrl Name="evt_id" FieldName="Id" DisplayName="Event Id" />
    <BizCtrl Name="evt_name" FieldName="Name" DisplayName="Name" Type="" Width="250" />
    <BizCtrl Name="evt_desc" FieldName="Description" DisplayName="Description" Type="Textarea" />
    <BizCtrl Name="evt_host" FieldName="Host" DisplayName="Host" />
  </BizCtrlList>
  <Toolbar>
    <Control Name="btn_back" Image="" Caption="&lt; Back" Type="Button" Function="GoPrev()" />
    <Control Name="btn_next" Image="" Caption="Next &gt;" Type="Button" Function="GoNext()" />
    <Control Name="btn_cancel" Image="" Caption="Cancel" Type="Button" Function="DoCancel()" PostAction="view:demo.EventView"/>
    <Control Name="btn_finish" Image="" Caption="Finish" Type="Button" Function="DoFinish()" PostAction="view:demo.EventView"/>
  </Toolbar> 
  <Navbar>
  </Navbar> 
</BizForm>

Extend wizard with customer wizard class

If application has speical logic, Openbiz suggest developers to write their own wizard form class drived from BizWizardForm. By overriding GoPrev(), GoNext(), DoCancel() and DoFinish() methods, developers can do different handling on navigation buttons events.

Openbiz wizard view play the role as a form controller where wizard form can set/get input data, cancel/finish the whole wizard process and render wizard forms. Please see the API document for details.

Implement dataobject events trigger

Upon dataobjects update/delete operations, openbiz allows triggering different alerts and action requests based on boolean results from search criteria for specific object. Briefly it is called DO Trigger which executes in a response to a change in the values stored in the database. DO trigger has two parts - trigger events and trigger actions. These information are defined in DOTrigger plugin service metadata files. At runtime when user update/delete a BizDataObj record, openbiz searches for this dataobj's trigger by looking for its trigger metadata file with name DataObjName_trigger.xml under the same directory. For example, demo/BOEvent's dataobj trigger metadata file is demo/BOEvent_trigger.xml.

Define a dataobject trigger metadata

<PluginService Name="BOEvent_Trigger" Description="" Package="demo" Class="service.doTriggerService" DataObjectName="BOEvent">
<DOTrigger TriggerType="UPDATE|DELETE">*
  <TriggerCondition Expression="" ExtraSearchRule="" />
  <TriggerActions>
    <TriggerAction Action="Method_Name" Immediate="Y|N" DelayMinutes="" RepeatMinutes="">
      <ActionArgument Name="" Value="" /> *
    </TriggerAction>
  </TriggerActions>
</DOTrigger>
</PluginService>

Trigger conditions Description Sample
trigger type Update or Delete record  
expression any expression supported by openbiz {[Expense]}>100. Check if current record's Expense field > 100
extra search rule search rule added on the current dataobj search rules {[AlertFlag]}='Y'. Check if there's at least one record whose AlertFlag is 'Y'
Trigger action methods Description Parameters
ExecuteSQL ExecuteSQL method executes SQL statement Name="DBName" Value="Default"
Name="SQL" Value="select * from regist where EVENT_ID='{[Id]}'"
ExecuteShell ExecuteShell method executes external application Name="Script" Value="dir"
Name="Inputs" Value=" > d:\temp\out.txt"
SendEmail SenEmail method sends outbound emails. 

It calls emailService's sendEmail method

Name="EmailService" Value="service.emailService"
Name="Account" Value="MyPhpopenbiz"
Name="TOs" Value="rockyswen@gmail.com; rockyswen@phpopenbiz.org"
Name="CCs" Value=""
Name="BCCs" Value=""
Name="Subject" Value="alert message"
Name="Body" Value="This is an alert message. \nPlease notice that the record with {[Id]} was updated."
Name="Attachments" Value="" 
AuditTrail Trace record field change

It calls auditService Audit method

Name="AuditService" Value="service.auditService"
Name="DataObjectName" Value="{@:Name}"
any method in doTriggerService The method of doTriggerService is called  Parameters needed to the method

Make customer specific UI components

Due to the extensibility nature of metadata xml files, developers can create their own metadata file, which does not comply with openbiz metadata DTD, to describe the behavior of objects. Openbiz converts xml file to a php array and pass it to the class constructor to save developers' parsing work. Developers need to write their own code to extend from MetaObject class and read in the array by overriding ReadMetadata(&$xmlArr) method.

Tabs component

This is the good example of using customer metadata xml file. Openbiz 2.1 supports tabs UI component. The metadata file (demo/tabs.xml) is like

<?xml version="1.0" standalone="no"?>
<BizForm Name="Tabs" Package="demo" Class="HTMLTabs">
  <TabViews>
    <View Name="demo.AttendeeView" URL="" Caption="Attendees"/>
    <View Name="demo.EventView" URL="" Caption="Events"/>
  </TabViews>
</BizForm>

The class code is under openbiz/bin/HTMLTabs.php. Its css file is in openbiz.css.

Developer can draw more UI widgets like tree, menu using the same approach. 

Menu component 

Openbiz 2.1 supports menu UI component. The metadata file of a menu (i.e. demo/Menus.xml) is like

<?xml version="1.0" standalone="no"?>
<Menu Name="Menus" Package="demo" Class="HTMLMenus">
  <MenuItem URL="" Caption="" Target=""> *
    <MenuItem URL="" Caption="" Target=""/> *

The class code is under openbiz/bin/HTMLMenus.php. Its css file is /css/menu.css

The menu provided in the demo application can be seen in "Test" view. This menu is a horizontal dropdown menu. It is a pure css menu. The idea is copied from http://solardreamstudios.com/learn/css/cssmenus. The output html (<ul><li>...) has same format of the output of well-known DynarchMenu http://www.dynarch.com/products/dhtml-menu/. So integration with DynarchMenu is an easy job. Of course, developers are free to modify the HTMLMenus class to work with other menu libraries. Same principle will apply to other customer UI components.

Tree component

Openbiz 2.1 supports tree UI component. The metadata file of a tree (i.e. demo/Tree.xml) is like

<?xml version="1.0" standalone="no"?>
<Tree Name="Tree" Package="demo" Class="HTMLTree">
  <Node URL="" Caption="" Target=""> *
    <Node URL="" Caption="" Target=""/> *

The class code is under openbiz/bin/HTMLTree.php. Its css file is in openbiz.css

The tree provided in the demo application can be seen in "Test" view. The technique of drawing tree is same as the tree of Eclipse help system http://help.eclipse.org/help30/index.jsp. Again, developers are free to modify the HTMLTree class to work with other tree libraries.

Control the look and feel with css files

The openbiz look and feel is controlled by stylesheet css files. The main css file is /css/openbiz.css

Control BizForm table style

In case of using Format="block" in the BizForm's displayMode, users can modify the following section in css/openbiz.css file.

/* -------- table style -------- */
.tbl {...}
.tbl .head {...}
.tbl .rowodd {...}
.tbl .roweven {...}
.tbl .rowsel {...}
.tbl .cell {...}

Control tabs style

In order to give user specific tab styles, users can modify the following section in css/openbiz.css file.

/* -------- tabs style -------- */
.tabmenu {...}
.tabmenu li {...}
.tabmenu a, a.active {...}
.tabmenu a.active {...}
.tabmenu a:hover {...}
.tabmenu a:visited {...}
.tabmenu a.active:hover {...}

Control menu style

In order to give user specific tab styles, users can modify the following section in css/menu.css file.

Control tree style

In order to give user specific tree styles, users can modify the following section in css/openbiz.css file.

/* ----- tree style ----- */
UL.expanded {...}
UL.collapsed {...}
LI.tree {...}

Control rich text editor (RTE) style

In order to give user specific RTE styles, users can modify the following section in pages/rte/rte.css file.

Date and Datetime picker

The DHTML calendar is well documented at  http://www.dynarch.com/demos/jscalendar/doc/html/reference.html. The javascript file is under demoapp/js/jscalendar.

Debug strategies

- Logging. BizSystem::log() method  can be called to log 4 priority levels LOG_EMERG, LOG_ERR, LOG_WARNING and LOG_DEBUG. Also a subject can be specified to give log messages different categories. The error is logged under /log/log_error.html, open it and you may find out what's wrong

- Debugging. Other than the DEBUG flag in 1.1.x is still valid, developers can turn on other 2 debug flags.
  1) Open the sysheader.inc under /bin, turn on debug log by changing define("DEBUG", 1); Then you'll see some debug information in /log/log_debug.html. This debug file records mainly the database calls and other operations
  2) Turn on SQL debug flag. Uncomment //$this->m_DbConnection[$rDBName]->debug = true; in BizSystem::GetDBConnection().
  3) Turn on RPC debug flag, each RPC response is printed in a separate window. Set var RPC_DEBUG = true; in clientUtil.js.