FreedomSoft - "the future is freedom"
Custom Code Tutorial - Print Version

Please Note: Before printing, you may need to adjust the Print Size within your browser to 100%.


Custom Code - Introduction

This tutorial introduces the custom code components of Freedombase; or, said another way, the places that custom code (subroutines or functions) can be tied in with Freedombase.

Freedombase contains many standard functions or features, that allow Web based information systems to be created and maintained rapidly and affordably. However, sometimes business rules require non standard functionality or a non standard approach, and under those circumstances custom code can be used.

Custom code is code that is created by a developer in order to apply complex business logic that cannot be applied using standard, predifined Freedombase calculations, or in order to incorporate business logic already defined in existing subroutines or functions.

This tutorial is intended to be worked through after the Introduction to the Freedombase Designer, and the Getting Started tutorials.

Please note that this tutorial does not cover calling Freedombase functions or subroutines from other systems; this tutorial only covers tying custom code in with Freedombase, ie how to make Freedombase call custom functions or subroutines.



Custom Code - Introduction - Custom Code Types

There are two places where custom code can be applied in Freedombase. They are:

- Templates, and
- Classes/Tables.

Templates can have Before, During, and After code applied. Classes/Tables can have Before and After code applied. Before and After are otherwise known as Pre and Post.

So, there are five combinations of custom code in Freedombase:

- Template Before Code
- Template During Code
- Template After Code
- Class Before Code
- Class After Code

The Class/Table (or Process) Before and After Code share the same parameter list as each other. The Template Before and After Code also share the same parameter list as each other. However, the custom code parameter list is different between Class/Table (or Process) and Template. Template During Code has it's own parameter list, that is not shared with any Before or After Code.

The five types of custom code serve and fullfill different purposes. Details of use and purpose for each type are described in the following pages.



Custom Code - Introduction - Creation

Before and After code is created in the same manner for both Classes and Templates, via the Class or Template page.

Before and After Code for Classes can be created from within the Freedombase Designer Class page, by displaying the Class that you want to add Before or After code to, and then selecting the Create hyperlink (1) beside the Before Code or After Code heading.

In the example provided below, by pressing the Before Code or After Code 'Create' hyperlink, the system would create the appropriate code, then open the code to be edited in the Code Maintenance page. In this example, the code would be created for the class CUSTOMER in the MAILBOX system.

Template Before and After Code can be created from withhin the Freedombase Designer Template page, by displaying the Template that you want to add Before or After code to, and then selecting the Create hyperlink (2) beside the Before Code or After Code heading.

In the example provided below, by pressing the Before Code or After Code 'Create' hyperlink, the system would create the appropriate code, then open the code to be edited in the Code Maintenance page. In this example, the code would be created for the OPENACCOUNTS_CLIENT_M.htm template.

Template During code, also known as Execute code, must be manually created, as there can be any number of During code references, with many different names, within one Template. During/Execute code can be created through any code editor that is compatible with the system that Freedombase is being run on; or, through the Code page of the Freedombase Designer.

During/Execute code requires the following parameter list: TemplateName, Command, User, DatabaseClassList, KeyList, InstanceList, ErrorMessageList, SequenceList, NoUpdateFlagList, iSearchFlag, DataIn, ExecuteResponse, iMetaErrorFlag.

For example: SELECT_IMAGE_SUBROUTINE(TemplateName, Command, User, DatabaseClassList, KeyList, InstanceList, ErrorMessageList, SequenceList, NoUpdateFlagList, iSearchFlag, DataIn, ExecuteResponse, iMetaErrorFlag)

Please see the During/Execute tutorial pages for further information on the parameter list.



Custom Code - Introduction - Editing

All custom code can be edited through the Freedombase Designer Code page, or through any code editor compatible with database engine that Freedombase is running on.

Freedombase will compile the custom code whenever the code is saved through the Freedombase Designer Code page.

To save the custom code, use the File Save hyperlink (1). In the example below, selecting Save will save and compile the Before code for the TIMESHEETLINE class in the OPENACCOUNTS system (2).

If the code is edited using any other means, you will need to compile and catalogue the code as appropriate for your database environment, after each modification.

For Before and After code, you can navigate from the Code page to either the associated Class (3) or Template.



Custom Code - Introduction - Integration

Just because Before or After Code exists, does not mean the code is integrated with the Process or Template that the Code belongs to.

For the sakes of efficiency, Before and After Code is not integrated with the associated Process or Template every time the Before or After code is compiled. Instead, the Before and After code is only integrated the next time the associated Process or Template is rebuilt.

If you manually create a new Before or After subroutine, of either kind, you need to first compile and catalog that subroutine as appropriate, and then rebuild the template or processs that the subroutine belongs to.

However, creating Before or After code by selecting the Create hyperlink on the Class or Template pages (1), causes the code to be immediately integrated with the process or template. This is the one exception to the rule. For this reason, using the Create link to create Before or After code is the preferred method.

To rebuild a template or process, ensure any changes have been saved (2). If the message 'Waiting to be rebuilt' displays in the Rebuild Result textbox (3), the process or template needs to be rebuilt. For the final step, rebuilding the template or process, select the 'Recreate Template' or 'Rebuild Process' (4) utility, as appropriate.



Custom Code - Introduction - Includes

Every time an attribute is added or deleted from a Freedombase controlled class, Freedombase updates an include file, with the updated definition of that class.

These include files can be included within Before and After Code, as appropriate. These include files allow the developer to refer to attributes within instances by name instead of attribute position number, making code much more readable, and much easier to understand and maintain.

The "Include" statements are usually positioned at the top of the page code, basically next thing after the subroutine name and parameters.

The format of the include file name may differ depending on the database.

The command to include the file, for UniVerse, jBase, and QM, is:

$INCLUDE Process_BP SystemName_ClassName_Definition

For example:

$INCLUDE Process_BP DEMO_STATE_Definition

The format of the include file name, and the command to include the file, for OpenInsight, is:

$INSERT SystemName_ClassName_DEFINITION

For example:

$INSERT DEMO_STATE_DEFINITION



Custom Code - Introduction - Command

The Before and After Code is called every time the Process or Template is called, regardless of the Command (or function) that the Process or Template is being called for.

Command is the first parameter passed into all Before and After Code.

It is up to the developer to write code to check what Command is being performed, and then to behave appropriately.

For example, it would be appropriate to derive data within Process Before Code when the Command was 'Save' or 'Load', but it may not be appropriate to derive data when the Command was 'Get'.

A complete list of Command values that can be passed, is contained with the Freedombase Help pages. However, the Command values that will be checked for/acted on the most often, are Save, Load, Delete, and Get.

It is also possible to pass custom Command values, and for the Before or After Code to check for and to behave differently depending on what Command value was received. This allows custom functions to be added into a Freedombase controlled system with ease.



Custom Code - Introduction - Usage

Each Custom Code type is used to fulfill different purposes. The usual purpose of each type is explained against each type.



Custom Code - Introduction - User

One of the parameters passed into each Custom Code type, is the current User session id or number.

This is a string containing both the user id, and a random number providing a unique session id, in the format userid~randomnumber, eg Administrator~12323232.

To separate out the user id, use the FIELD(User,'~',1) statement, eg UserID = FIELD(User,'~',1)



Custom Code - Introduction - Table Access

It is possible to update or retrieve data to and from classes by reading, writing, or deleting directly to or from the classes within Before and After Code. However, to do so would bypass derivation, validation, audit, and cascade processing; so be very careful about accessing class data directly. It is usually preferable to call the Process to access data instead.

The code below uses the Freedombase Class SetPointer subroutine, instead of opening the class itself. Using the Freedombase Class SetPointer subroutine can provide performance advantages, as it holds class pointers in memory. For this reason, Freedombase Class SetPointer is the recommended method of opening classes for access on multi dimensional systems.



Custom Code - Process Before - Introduction

Process Before Code is usually used to perform the following:

1. To derive data, where that derived data needs to be updated onto file as the instance is saved, and the standard Freedombase derivation calculations cannot derive that data. Derivation should normally only be performed when Command is Save or Load.

2. To validate data, to record appropriate error messages, and to prevent the instance from being written to file, where the standard Freedombase type, relation, and validation calculations cannot perform the validation. Validation should normally only be performed when Command is Save or Load.

3. To prepare related data, that the data being saved is dependant on, when Creating and Creating Not Validated relationship types are insufficient. Preparing related data should normally only be performed when Command is Save or Load.



Custom Code - Process Before - Blank Attributes

Input fields from Web pages are returned over the Internet as a 'flattened' list of data, a string in the form of Name=Value&Name=Value etc.

Freedombase builds instances, including multi values on multi dimensional systems, from this string, and then passes the built instance to the Process.

Under many circumstances, some attributes will not be returned from the browser - usually when they were not output to the browser in the first place.

In this circumstance, when saving data onto file, Freedombase will retrieve the missing attribute values from file, and will merge them into the instance in memory, before updating that merged instance back to file.

However, Freedombase also needs to know when an attribute was returned from the browser with a blank value.

In order for the Process to distinguish between an attribute that was not returned at all, and therefore should be loaded from file, and an attribute that was returned as a blank, and therefore should be left blank, Freedombase uses a string of characters set aside specifically for this purpose. This string of characters is included in the Freedombase_Definition include file.

When the Process Before Code is called, attributes not passed back from the browser have not yet been loaded from file.

It is necessary to distinguish between attributes not passed back from the browser, and attributes passed back as blank, within the Process Before.

In order to distinguish between the two, the Freedombase_Definition include must be included in the Process Before Code.

Attributes that were not passed back will have a null or empty value, and attributes that were passed back as blank will have a value equal to Freedombase_BlankAttribute_String. Comparing the values of attributes against Freedombase_BlankAttribute_String can be used to determine which attributes will be left blank when saved, and which will be populated from data currently on file.

An example is:

    SUBROUTINE MAILBOX_JOB_Before(Command,RawData,Instance,Instance_Key,ErrorMessage,Occurrence,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,CurrentSelect,CascadeRecurse,Cascade_Cascade_KeyList,Cascade_CascadeToKeyList,PreviousDatabaseeClassList,PreviousKeyList,PreviousDateTimeWrittenList,PreviousNoUpdateFlagList,User,MetaErrorFlag)

$INCLUDE FB_BP Freedombase_Definition
$INCLUDE Process_BP MAILBOX_JOB_Definition

     IF Command = 'Save' AND Instance NE '' THEN
        IF (Instance<MAILBOX_JOB_PAID>) AND Instance<MAILBOX_JOB_PAID> NE Freedombase_BlankAttribute_String THEN
           StartDate = Instance<MAILBOX_JOB_PAID>
           GOSUB GetPeriod
           Instance<MAILBOX_JOB_PERIOD> = Period
        END
     END

     RETURN

GetPeriod:

     IF NOT(StartDate) THEN
        StartDate = DATE()
     END

     StartDate = OCONV(StartDate,'D4')
     Period = FIELD(StartDate,' ',3)

     LOCATE FIELD(StartDate,' ',2) IN 'JAN':@AM:'FEB':@AM:'MAR':@AM:'APR':@AM:'MAY':@AM:'JUN':@AM:'JUL':@AM:'AUG':@AM:'SEP':@AM:'OCT':@AM:'NOV':@AM:'DEC' SETTING Position THEN
        Position = Position / 3
        IF Position NE INT(Position) THEN
           Position = INT(Position) + 1
        END
        Period := Position
     END

     RETURN

  END



Custom Code - Process Before - Derive

The following code is an example of deriving data, within Process Before Code. Usually this is only necessary where that data could not be derived using Freedombase Derivation calculations.

Please note that the Process will still apply all Derivation calculations before the data is written to file, allowing us to mix and match the two methods of deriving data (Freedombase Derivation calculations and custom code), and enables us to use the most efficient and effective methods for our particular circumstances.

The code is:

    SUBROUTINE MAILBOX_JOB_Before(Command,RawData,Instance,Instance_Key,ErrorMessage,Occurrence,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,CurrentSelect,CascadeRecurse,Cascade_Cascade_KeyList,Cascade_CascadeToKeyList,PreviousDatabaseeClassList,PreviousKeyList,PreviousDateTimeWrittenList,PreviousNoUpdateFlagList,User,MetaErrorFlag)

$INCLUDE FB_BP Freedombase_Definition
$INCLUDE Process_BP MAILBOX_JOB_Definition

     IF Command = 'Save' AND Instance NE '' THEN
        IF (Instance<MAILBOX_JOB_PAID>) AND Instance<MAILBOX_JOB_PAID> NE Freedombase_BlankAttribute_String THEN
           StartDate = Instance<MAILBOX_JOB_PAID>
           GOSUB GetPeriod
           Instance<MAILBOX_JOB_PERIOD> = Period
        END
     END

     RETURN

GetPeriod:

     IF NOT(StartDate) THEN
        StartDate = DATE()
     END

     StartDate = OCONV(StartDate,'D4')
     Period = FIELD(StartDate,' ',3)

     LOCATE FIELD(StartDate,' ',2) IN 'JAN':@AM:'FEB':@AM:'MAR':@AM:'APR':@AM:'MAY':@AM:'JUN':@AM:'JUL':@AM:'AUG':@AM:'SEP':@AM:'OCT':@AM:'NOV':@AM:'DEC' SETTING Position THEN
        Position = Position / 3
        IF Position NE INT(Position) THEN
           Position = INT(Position) + 1
        END
        Period := Position
     END

     RETURN

  END



Custom Code - Process Before - Validate

The following code is an example of validating data, within Process Before Code. Usually this is only necessary where that data could not be validated using data type, relational integrity, or Freedombase Validation calculations.

Please note that if the Process Before Code sets ErrorMessage, then the Process itself will not write the data to file.

Please note that the Process will still apply all type, relational, and Validation calculations before the data is written to file, and will not write the data if either the Process Before Code set ErrorMessage, or the Process istelf found a data fault. This allows us to mix and match the four methods of validating data (data type, relational, Freedombase validation calculations, and custom code), and enables us to use the most efficient and effective methods for out particular circumstances.

Sample code is:

     SUBROUTINE Freedombase_Cascade_Before(Command,RawData,Instance,Instance_Key,ErrorMessage,Occurrence,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,CurrentSelect,CascadeRecurse,Cascade_Cascade_KeyList,Cascade_CascadeToKeyList,PreviousDatabaseeClassList,PreviousKeyList,PreviousDateTimeWrittenList,PreviousNoUpdateFlagList,User,MetaErrorFlag)

$INCLUDE Process_BP Freedombase_Cascade_Definition
$INCLUDE Process_BP Freedombase_Class_Definition

     IF (Command = 'Save' OR Command = 'Load') AND (Instance) THEN
        CALL Freedombase_Class_SetPointer('Freedombase','Class',Fredombase_Class,MetaErrorFlag)
        IF MetaErrorFlag > 0 THEN
           RETURN
        END
        Class_Key = Instance<Cascade_PointerDatabasee>:Delimiter_Key:Instance<Cascade_PointerClass>
        READ Class_Instance FROM Freedombase_Class,Class_Key ELSE
           Class_Instance = ''
        END
        IF Class_Instance = '' THEN
           ErrorMessage = Class_Key:' is not a valid Cascade System and Cascade Class combination'
           RETURN
        END
     END

     RETURN

  END



Custom Code - Process Before - Related

Under rare circumstances, it may be necessary to manually create or modify related data, before data currently being updated is validated and actually updated.

Please note that the Creating and Creating Not Validated relationship types can probably be used instead of this method, and may be more efficient. However, the Creating and Creating Not Validated relationship types require that the Cascade function be operating before the actual creation occurs, and this may place limitations on the usage of Creating and Creating Not Validated relationship types.

Please see the Help documentation for the Freedombase Designer for more information on the Creating and Creating Not Validated relationship types.

Sample code is:

     SUBROUTINE CIS_COURSE_After(Command,RawData,Instance,Instance_Key,ErrorMessage,Occurrence,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,CurrentSelect,CascadeRecurse,Cascade_Cascade_KeyList,Cascade_CascadeToKeyList,PreviousDatabaseeClassList,PreviousKeyList,PreviousDateTimeWrittenList,PreviousNoUpdateFlagList,User,MetaErrorFlag)

$INCLUDE FB_BP Freedombase_Definition
$INCLUDE Process_BP CIS_CALENDAR_Definition
$INCLUDE Process_BP CIS_COURSE_Definition

     IF (Command = 'Save' OR Command = 'Load') AND ErrorMessage = '' THEN
        * Get the first Calendar instance for this Course.
        CALENDAR_Instance = ''
        CALENDAR_Instance<CIS_CALENDAR_COURSE> = Instance<CIS_COURSE_COURSE>
        CALENDAR_Instance<CIS_CALENDAR_DAY> = 1
        CALL CIS_CALENDAR_Process('Get','',CALENDAR_Instance,'','','','','','','','','','','','','','','','','',User,MetaErrorFlag)
        IF (MetaErrorFlag) THEN
           RETURN
        END
        * Set the Date of the first Calendar instance for this Course.
        CALENDAR_Instance<CIS_CALENDAR_DATE> = Instance<CIS_COURSE_STARTDATE>
        CALENDAR_ErrorMessage = ''
        CALL CIS_CALENDAR_Process(Command,'',CALENDAR_Instance,'',CALENDAR_ErrorMessage,'','','','','','','','','','','','','','','',User,MetaErrorFlag)
        IF (MetaErrorFlag) THEN
           RETURN
        END

     END

     RETURN

  END



Custom Code - Process After - Introduction

Process After Code is usually used to perform the following:

1. To modify the data being returned, so that data not actually held on file, or held in a different shape on file, can be returned. This could be to default data as appropriate. Modifying data being returned should normally only be performed when Command is Get.

2. To provide instance or attribute level security, for example should this user see this instance but not others within this class, or should this user see some attributes but not others within this instance. Security checking should normally only be performed when Command is Get.

3. To update related data, that is dependant on the data that was just saved. Updating related data should normally only be performed when Command is Save or Load, and when the ErrorMessage parameter is blank or null.



Custom Code - Process After - Modify

The following code is an example of modifying the data being returned, so that data not actually held on file, or that is held but in a different format, can be returned as though it is held on file.

Please note that this method is particularly useful when business rules change after data has been written to file, and you do not want to create or run a conversion program over the existing data. This method allows you to convert the data into the new format as and when the older data is needed. Consider, however, that a one off conversion program may be more efficient.

This method can also be used to modify the data depending on the user, for example if one user needed data in one format, and another user needed data in a different format, the Process After Code could check what format this user required, and could modify the data accordingly.

Sample code is:

     SUBROUTINE Freedombase_Code_After(Command,RawData,Instance,Instance_Key,ErrorMessage,Occurrence,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,CurrentSelect,CascadeRecurse,Cascade_Cascade_KeyList,Cascade_CascadeToKeyList,PreviousDatabaseeClassList,PreviousKeyList,PreviousDateTimeWrittenList,PreviousNoUpdateFlagList,User,MetaErrorFlag)

$INCLUDE FB_BP Freedombase_Definition

     IF FIELD(Command,' ',1) NE 'Delete' AND (Instance<Code_File>) THEN
        GOSUB GetCode
     END

     RETURN

GetCode:

     Code = ''
     OPENSEQ Instance<Code_Location>,Instance<Code_File> TO CodeFile ELSE
        RETURN
     END

     LOOP
        READSEQ Line FROM CodeFile ELSE
           EXIT
        END
        Code := Line:@AM
     REPEAT

     CLOSESEQ CodeFile
     Instance<Code_Code> = Code

     * If there has been an update error, then Freedombase will not get this
     * Instance from file before outputting it. Instead, if there has been an update error,
     * Freedombase will use the Instance in memory, ie the Instance in InstanceList.
     * So we will update the Instance in InstanceList if there has been an error.

     IF (ErrorMessage) OR (ErrorMessageList) THEN
        DatabaseeClass = 'Freedombase':Delimiter_Name:'Code'
        LOCATE DatabaseeClass IN DatabaseeClassList SETTING DatabaseeClassPosition THEN
           LOCATE Instance<Code_File> IN KeyList<DatabaseeClassPosition> SETTING KeyPosition THEN
              InstanceList<DatabaseeClassPosition,KeyPosition> = LOWER(LOWER(Instance))
           END
        END
     END

     RETURN

  END



Custom Code - Process After - Security

The following code is an example of applying security by modifying the data being returned, as appropriate for the user who is retrieving the data.

Please note that attribute level security can be applied to some extent at the template level.

Please note that the Process Before Code may need to 'reverse out' this security; for example, if the value of an attribute was not passed back from the Process After Code due to security restrictions, the Process Before Code would need to take that into account when that attribute is passed back as blank from the browser. Ie, in this circumstance, the Process Before Code may need to determine that the user was not allowed to see this data, and to reset that attribute, in order to prevent the value of the attribute being lost. Resetting the value of the attribute to null or blank in the Process Before Code, as appropriate, would achieve this.

Sample code is:

     SUBROUTINE MAILBOX_CUSTOMER_After(Command,RawData,Instance,Instance_Key,ErrorMessage,Occurrence,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,CurrentSelect,CascadeRecurse,Cascade_Cascade_KeyList,Cascade_CascadeToKeyList,PreviousDatabaseeClassList,PreviousKeyList,PreviousDateTimeWrittenList,PreviousNoUpdateFlagList,User,MetaErrorFlag)

$INCLUDE FB_BP Freedombase_Definition
$INCLUDE Process_BP MAILBOX_CUSTOMER_Definition

     IF FIELD(User,'~',1) = 'guest' AND Instance<MAILBOX_CUSTOMER_BALANCE> NE '' THEN
        Instance<MAILBOX_CUSTOMER_BALANCE> = '(hidden)'
     END

     RETURN

  END



Custom Code - Process After - Related

The following code is an example of updating related, dependant data, after the current instance has been updated successfully.

     SUBROUTINE CIS_COURSE_After(Command,RawData,Instance,Instance_Key,ErrorMessage,Occurrence,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,CurrentSelect,CascadeRecurse,Cascade_Cascade_KeyList,Cascade_CascadeToKeyList,PreviousDatabaseeClassList,PreviousKeyList,PreviousDateTimeWrittenList,PreviousNoUpdateFlagList,User,MetaErrorFlag)

$INCLUDE FB_BP Freedombase_Definition
$INCLUDE Process_BP CIS_CALENDAR_Definition
$INCLUDE Process_BP CIS_COURSE_Definition

     IF (Command = 'Save' OR Command = 'Load') AND ErrorMessage = '' THEN
        CALENDAR_Instance = ''
        CALENDAR_Instance<CIS_CALENDAR_COURSE> = Instance<CIS_COURSE_COURSE>
        CALENDAR_Instance<CIS_CALENDAR_DAY> = 1
        CALL CIS_CALENDAR_Process('Get','',CALENDAR_Instance,'','','','','','','','','','','','','','','','','',User,MetaErrorFlag)
        IF (MetaErrorFlag) THEN
           RETURN
        END
        CALENDAR_Instance<CIS_CALENDAR_DATE> = Instance<CIS_COURSE_STARTDATE>
        CALENDAR_ErrorMessage = ''
        CALL CIS_CALENDAR_Process(Command,'',CALENDAR_Instance,'',CALENDAR_ErrorMessage,'','','','','','','','','','','','','','','',User,MetaErrorFlag)
        IF (MetaErrorFlag) THEN
           RETURN
        END

     END

     RETURN

  END



Custom Code - Template Before - Introduction

Template Before Code is usually used to perform the following:

1. To control what data will be output onto the Template. This could be to default data appropriately, or to provide instance level security.

2. To redirect the user to another page instead, if appropriate.

3. To replace the HTML that would have been output from the template, with HTML created completely from custom code.



Custom Code - Template Before - Data Output

The data to be output on the Template, is controlled via the Command, and via the DatabaseClassList, KeyList, InstanceList, ErrorMessageList, and SequenceList parameters.

The Command parameter can override whatever is contained within the various Lists passed into the Template. The types of output that Command can cause, are:

1. No data will be output.

2. One Instance.

3. All of the data referenced within DatabaseClassList, KeyList, InstanceList, ErrorMessageList, and SequenceList will be output.

4. All of the data referenced within the current Search result will be output.

5. All of the data within a particular Class will be output.

In all of the above, data related to any data output, where the related data is referenced on the Template, will also be output.

To force any of the above five types of output, use the following settings of Command:

1. No data will be output - use Command = 'New'

2. One Instance - use Command = 'Select':InstanceKey

3. All of the data referenced within DatabaseClassList, KeyList, InstanceList, ErrorMessageList, and SequenceList will be output - use Command = 'Go'

4. All of the data referenced within the current Search result will be output - user Command = 'ViewSearch'

5. All of the data within a particular Class will be output - use Command = 'View All'

DatabaseClassList, KeyList, InstanceList, ErrorMessageList, and SequenceList are all related lists. DatabaseClassList is an Attribute or Field marker delimited list, containing a list of Database_Classes or System_Classes. KeyList, InstanceList, ErrorMessageList, and SequenceList are all Attribute or Field and Value marker delimited lists. These four lists are all controlled at the Attribute or Field level by DatabaseClassList. At the Value level, KeyList, InstanceList, ErrorMessageList, and SequenceList contain a list of keys, instances, error messages, and output sequence information for each Database_Class or System_Class.

The data within InstanceList is only used, if there is no related data within KeyList, or there is an error message within the related data in ErrorMessageList. Normally, Freedombase will use the instance key to retrieve the current version of the Instance, as the page is being output. This ensures that the most recent version of the data possible is output. However, when the user has tried to update or delete an instance, and that update or deletion has failed validation, that udpate will not have occurred, and we need to output the invalid data back to the user, instead of what is on file. For that reason, if ErrorMessageList contains related data for an instance in InstanceList, Freedombase will use the instance in InstanceList instead of re-reading the data from file.

When outputting data using these Lists, SequenceList must be set correctly to allow Freedombase to drill to related data. Essentially, SequenceList must contain a list of string values of increasing value. (Numbers are easier to use than strings, and numbers are usually used; but they must be right justified.) Please see the Example below for the correct setting and usage of SequenceList.

To output data for multiple different classes on the one page, without Freedombase drilling to the related classes, SequenceList must be set so that the sequences of the dependant class instances fall between the sequences of the controlling class instances.

For example, if we had two classes - CUSTOMER and ORDER - the sequence values for the CUSTOMER instances could be '1','2', and '3', and the sequence values for the ORDER instances could be '1 1','1 2','2 1','3 1', and '3 2'. In this case, Freedombase would interpret that the first two ORDER instances were related to the first CUSTOMER, as '1 1' and '1 2' both fall between '1' and '2'; the third ORDER would be related to the second CUSTOMER, as '2 1' falls between '2' and '3'; and the fourth and fifth ORDER instances would be related to the third CUSTOMER, as '3 1' and '3 2' is greater than '3'.

By convention all numbers used within SequenceList are right justified, and take six characters per number. So, by convention, the SequenceList entries for the CUSTOMER class used in this example would actually be '     1','     2', and '     3', and the SequenceList entries for the ORDERS class used in this example would actually be '     1      1','     1     2','     2     1','     3     1', and '     3     2'. Six characters are used by convention, as it is unlikely that any application would ever need to exceeed a six digit sequence number when outputting data to a Template.

Please note that the example below was taken from Freedombase running on OpenInsight. Most of the examples within this tutorial are taken from Freedombase running on UniVerse. The BASIC used, and the way some commands are executed, varies slightly between the two systems. However, the logic should still be clear enough to be understood, regardless of which version of multi dimensional BASIC you are used to working with.

Sample code is:

     SUBROUTINE TEAM_REPORT_M_HTM_BEFORE(Command,User,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,SearchFlag,Response,Error,MetaErrorFlag)

$INSERT FREEDOMBASE_DEFINITION

    ThisCompany = FIELD(User,'/',1)

    ProjectOccurrence = 0
    ProjectList = ''
    ProjectOccurrenceList = ''
    EXECUTE 'SELECT TEAMS_PROJECT WITH COMPANY = "':ThisCompany:'" BY SEQUENCE'
    ExitFlag = 0
    LOOP
       READNEXT Key ELSE
          ExitFlag = 1
       END
    UNTIL (ExitFlag) DO
       ProjectOccurrence += 1
       ProjectList<1,ProjectOccurrence> = Key
       ProjectOccurrenceList<1,ProjectOccurrence> = SPACE(6 - LEN(ProjectOccurrence)):ProjectOccurrence
    REPEAT

    * Only output the Project List onto the page. Overwrite any other data that may be in the memory lists.

    DatabaseClassList = 'TEAMS':Delimiter.Name:'PROJECT'
    KeyList = ProjectList
    InstanceList = ''
    ErrorMessageList = ''
    SequenceList = ProjectOccurrenceList

    RETURN

  END



Custom Code - Template Before - Another Page

On occassion, it may be necessary to direct the user to a different Template; for example if the current user should not be allowed to see the current Template, or if data to be output needs to be displayed on a different Template.

Please note, however, that security can be applied on Templates, and this is the preferred method for preventing users from seeing Templates that they should not see. Under normal circumstances, the Another Page option would only be used when the Custom Code is able to determine that the data to be displayed is better displayed via another Template.

To output another page, the Template Before Code can all the other Template Code directly, which will return the new page in the Response parameter. The Template Before Code can then set the last six characters of the data returned in Response to RETURN.

Freedombase will check if the last six characters of Response are RETURN, and if so, will remove those last six characters, and will return the remaining contents of Response to the browser, bypassing the Template, and bypassing any Template After Code.

Sample code is:

     SUBROUTINE Login_HTM_BEFORE(Command,User,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,SearchFlag,Response,Error,MetaErrorFlag)

$INSERT FREEDOMBASE_DEFINITION

     BEGIN CASE
        CASE Command = 'Go Contactus.htm'
           CALL CONTACTUS_HTM(Command,User,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,SearchFlag,Response,Error,MetaErrorFlag)
           Response := 'RETURN'
        CASE Command = 'Go Supportlink.htm'
           CALL SUPPORTLINK_HTM
Command,User,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,SearchFlag,Response,Error,MetaErrorFlag)
           Response := 'RETURN'
     END CASE

     RETURN

  END



Custom Code - Template Before - Replace HTML

On occassion, it may not be workable to use a Template to output the HTML that is required; some pages may need to be so variable, depending on the data being output, or depending on the user, that the only workable solution is to output the HTML from custom code.

To tell Freedombase to ignore the Template itself, and to only return to the browser whatever was returned by the custom code, pass the HTML to be returned to be browser in the Response parameter, and make the last six characters of the data returned in Response RETURN.

Freedombase will check if the last six characters of Response are RETURN, and if so, will remove those last six characters, and will return the remaining contents of Response to the browser, bypassing the Template, and bypassing any Template After Code.

Sample code is:

     SUBROUTINE Login_HTM_BEFORE(Command,User,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,SearchFlag,Response,Error,MetaErrorFlag)

$INSERT FREEDOMBASE_DEFINITION

     Response = 'The HTML to be output on the page, instead of the HTML that would normally come from the Template'

     Response := 'RETURN'

     RETURN

  END



Custom Code - Template During - Introduction

Template During/Execute Code is usually used to perform the following:

1. To insert HTML output from a program into the page.

Please note that Template After code can also be used to achieve a similar result, by placing tags within the template (for example %ReplaceMe%) and then swapping %ReplaceMe% with the new results, in the Response variable passed into the Template After code.

However, when the same custom code needs to be run from multiple different templates, it may make more sense to use During/Execute code than Template After code; for example, if a banner image needs to be displayed on a number of pages, and a different image needs to be displayed depending on the user who is logged in, using During/Execute code would probably be more practical. One During/Execute subroutine could be created that can be referenced from all Templates that need it, instead of having to create After code for every single template that requires the different image displayed.



Custom Code - Template During - Insert

The usual use of Template During/Execute code, is to insert HTML or data directly into the body of a page.

The contents of the page, between the opening and closing During/Execute tags, will be passed into the During/Execute subroutine. This allows the subroutine to behave differently for different data or different circumstances.

The contents of the template, between the opening and closing tags (%X SubroutineName% and %/X%) can contain any other Freedombase tag. Any other Freedombase tag will be evaluated before the During/Execute subroutine is executed. This allows data from the database to be passed into the During/Execute subroutine.

For example:

The Customer's Preferred Image, Based On Customer Type, Is: <img src="%X GETIMAGE_SUBROUTINE%EXAMPLES_CUSTOMER_TYPE%/X%">

The During/Execute subroutine could be:

   SUBROUTINE GETIMAGE_SUBROUTINE(TemplateName,Command,User,DatabaseClassList,KeyList,InstanceList, ErrorMessageList,SequenceList,NoUpdateFlagList,iSearchFlag,DataIn,ExecuteResponse,iMetaErrorFlag)

   ExecuteResponse = ''
   BEGIN CASE
      CASE DataIn = 'P' ; * Customer Type is Private
         ExecuteResponse = 'privateimage.gif'
      CASE DataIn = 'B' ; * Customer Type is Business
         ExecuteResponse = 'businessimage.gif'
      CASE 1
         ExecuteResponse = 'unknowntype.gif'
   END CASE

   RETURN



Custom Code - Template After - Introduction

Template After Code is usually used to perform the following:

1. To replace specific HTML on the page, with HTML output by a program.



Custom Code - Template After - Remove Or Change

The usual use of this technique, is to insert tags into the page - for example %SCRIPT% - that the Template After Code can look for, and replace with appropriate values, usually HTML or scripts.

This technique is particularly useful when outputting data or scripts onto the page that varies from user to user, or varies depending on current system or class settings. It can also be used to output HTML containing integrated data, when the rules for integration are more complex than can be easily handled using standard templates.

Sample code is:

     SUBROUTINE CIS_COURSE_M_htm_After(Command,User,DatabaseeClassList,KeyList,InstanceList,ErrorMessageList,SequenceList,NoUpdateFlagList,SearchFlag,Response,Error,MetaErrorFlag)

$INCLUDE FB_BP Freedombase_Definition

     CreateCourseDate = ''
     IF FIELD(Command,' ',1) = 'CreateCourse' THEN
        CreateCourseDate = OCONV(FIELD(Command,' ',2),'D')
     END
     Response = CHANGE(Response,'%CREATECOURSEDATE%',CreateCourseDate)

     RETURN

  END



    navigate