Rapid Application Development toolkit for building Administrative Web Applications

RADICORE for PHP - Functions, Methods and Variables

By Tony Marston

10th March 2006
Amended 1st July 2014

As of 10th April 2006 the software discussed in this article can be downloaded from www.radicore.org

Introduction
Abstract Table Class
- Sequence of Events
- Notes on Public Methods
- Notes on Internal (private) Methods
- Notes on Customisable Methods
- Class Properties
Singleton class
Non OO functions
Session Variables
Global Variables
Amendment History

Introduction

The RADICORE library consists of functions, methods and variables which exist in different 'include' files, such as the following:

dml.mysql.class.inc This is the Data Access Object for MySQL version 4.0 or lower.
dml.mysqli.class.inc This is the Data Access Object for MySQL version 4.1 or higher.
dml.oracle.php4.class.inc This is the Data Access Object for Oracle using the OCI8 extension for PHP 4.
dml.oracle.php5.class.inc This is the Data Access Object for Oracle using the OCI8 extension for PHP 5.
dml.pgsql.class.inc This is the Data Access Object for PostgreSQL version 8.0 or higher.
dml.sqlsrv.class.inc This is the Data Access Object for SQL Server 2008 R2 or higher.
error.inc This contains the default error handler.
include.general.inc This contains general purpose functions.
include.jump.inc This contains addJump, clearJump and findJump.
include.session.inc This contains functions to do with session handling.
include.subsystem.inc There is a separate copy for each subsystem. It is referenced at the start of include.general.inc to provide the opportunity to execute custom code whenever a script within that subsystem is run and before a database object is created. With this it is possible to amend the include_path setting, or include another file containing more functions.
include.xml.php4.inc This contains the XML and XSL functions for PHP 4.
include.xml.php5.inc This contains the XML and XSL functions for PHP 5.
std.csv.class.inc This is for the functions which produce CSV output (OUTPUT1 and OUTPUT4).
std.datevalidation.class.inc This contains functions for validating and formatting all dates.
std.encryption.class.inc This contains functions for password encryption and decryption.
std.pdf.class.inc This is for the functions which produce PDF output.
std.singleton.php4.inc An implementation of the singleton design pattern for PHP 4.
std.singleton.php5.inc An implementation of the singleton design pattern for PHP 5.
std.table.class.inc This is the abstract table class from which each database table class is extended.
std.validation.class.inc This contains the processing behind _validateInsert and _validateUpdate.

In the following sections there are some elements which you can reference in your code while there are others which should only be referenced by the framework. The "framework only" elements have been given a different background colour, as follows:

This element is only to be referenced by the framework, not user code

If you try to use any of these elements in your code the results may be unexpected.


Abstract Table Class

Each database table within a RADICORE application has its own table class which contains specific information regarding that particular database table. This class inherits a great deal of common code from the abstract table class. It is the abstract table class that contains the methods which are accessed from all the controllers, and it is the abstract table class which communicates which the database through the data access object (DAO).

Each operation, which starts from the controller and may end in the database, contains a series of distinct steps or stages. Some of these steps in the abstract table class are in fact nothing more than empty methods, and as they contain no code they have no effect on that operation. If any of these empty methods is copied from the abstract table class (superclass) into an application table class (subclass) it will override the method in the superclass. This means that any method found in the subclass will be executed instead of a method with the same name that may be defined in the superclass. This then provides an opportunity to insert code into that empty method so that it can be executed at the appropriate stage of the operation.

These empty methods are known as customisable methods in RADICORE as they are predefined and waiting to be filled with custom code. The signature (API) and the return parameter cannot be changed, only what happens in between. Note that within any subclass it is possible to create additional methods other than those mentioned below, but these additional methods will not be executed unless they are called from one of the pre-defined customisable methods.

The following naming conventions are used for methods within the abstract table class:

PrefixMeaning
(none)A public method which can be accessed from outside.
'_'A private method which should only be accessed from inside.
'_cm_'A method which is defined within the abstract table class but is empty of any code. It is therefore called within a sequence of operations but does nothing. A copy can be inserted into any subclass in order to define code that needs to be executed at that point in the sequence.
'_dml_'Internal methods which pass control to the Data Access Object (DAO) in order to send Data Manipulation Language (DML) statements to the database.

In the following sections certain function/method arguments are defined as follows:

  1. $rowdata/$fieldarray - an associative array of 'name=value' pairs containing fields from a single database occurrence. Note that this may also contain fields which have been obtained from other sources (e.g. a sql JOIN).
  2. $rows - an indexed array containing multiple database occurrences, where the index is treated as the row/occurrence number, and each row contains $rowdata for that occurrence.
  3. $where - a string that contains selection criteria in a format which can be used as the WHERE clause in an SQL query, such as:
    field1='value1' AND field2='value2' .....
    

Sequence of Events

In the following table the column labelled 'Entry Point' indicates the method which may be called from outside, either from a controller or another table class. The column labelled 'Sequence' shows other methods which may be called internally, and in what sequence.

There is also a small set of UML diagrams which may help you to visualise the program flow.

Entry PointSequence
cascadeDelete ($where)
  1. $rows = getData_raw ($where)
  2. for each row: deleteRecord ($rowdata)
cascadeNullify ($update_array, $where)
  1. $rows = getData_raw ($where)
  2. for each row: updateRecord ($rowdata)
clearScrollArray ()  
commit ()
  1. _examineWorkflow ($this->fieldarray)
  2. $DML->commit ($this->dbname)
customButton ($fieldarray, $button)
  1. _cm_custombutton ($fieldarray, $button)
  2. getExtraData ($fieldarray)
deleteMultiple ($rows)
  1. for each row: deleteRecord ($rowdata)
deleteRecord ($rowdata)
  1. isPkeyComplete($rowdata, $this->PkeyNames)
  2. _cm_pre_deleteRecord ($rowdata)
  3. deleteRelations ($rowdata)
  4. _dml_deleteRecord ($rowdata)
  5. _cm_post_deleteRecord ($rowdata)
deleteRelations ($rowdata)
  1. $child->cascadeNullify ($update_array, $where)
  2. $child->cascadeDelete ($where)
deleteScrollItem ($index)  
deleteSelection ($selection)
  1. _cm_deleteSelection ($selection)
eraseRecord ($rowdata)
  1. isPkeyComplete($rowdata, $this->PkeyNames)
  2. _cm_pre_eraseRecord ($rowdata)
  3. eraseRelations ($rowdata)
  4. _dml_deleteRecord ($rowdata)
  5. _cm_post_eraseRecord ($rowdata)
eraseRelations ($rowdata)
  1. $child->cascadeNullify ($update_array, $where)
  2. $childrows = $child->getData_raw ($where)
  3. for each row: $child->eraseRecord ($childrow)
executeQuery ($query)  
fetchRow ($resource)
  1. $rowdata = _dml_fetchRow ($resource)
  2. $rowdata = _cm_post_fetchRow ($rowdata)
  3. $rowdata = _cm_post_lastRow ($rowdata)
fetchRowChild ($row)
  1. $keys = _cm_getNodeKeys ($keys)
  2. $resource = getData_serial ($where)
  3. $row = fetchRow ($resource)
filePickerSelect ($file_name)
  1. $file_name = _cm_filePickerSelect ($file_name)
fileUpload ($input_name, $temp_file)
  1. $output_name = _cm_fileUpload ($input_name, $temp_file, $wherearray)
formatData ($rowdata)
  1. _cm_formatData ($rowdata)
free_result ($resource)
  1. $resource = $DML->_dml_free_result ($resource)
getChildData ()  
getClassName ()  
getColumnNames ($where)
  1. $fieldarray = $this->_cm_getColumnNames($fieldarray)
getCount ($where)
  1. _dml_getCount ($where)
getData ($where)
  1. $where = _cm_pre_getData ($where, $where_array, $fieldarray)
  2. isPkeyComplete ($where_array, $this->PkeyNames)
  3. $where_str = _sqlAssembleWhere ($where, $where_array)
  4. $data_raw = _dml_getData ($where)
  5. _processInstruction ($data_raw)
  6. _cm_post_getData ($data_raw, $where)
getData_raw ($where)
  1. $data_raw = _dml_getData ($where)
getData_serial ($where)
  1. $where = _cm_pre_getData ($where, $where_array)
  2. $where_str = _sqlAssembleWhere ($where, $where_array)
  3. $resource = getData_serial ($where)
getExpanded ()
  1. return $this->expanded
getExtraData ($rowdata, $where=null)
  1. getForeignData ($rowdata)
  2. _cm_getExtraData ($where, $firstrow)
  3. _cm_changeConfig ($where, $firstrow)
getFieldSpec ()
  1. return $this->fieldspec
getFieldSpec_original ()
  1. return $this->fieldspec
getForeignData ($rowdata)
  1. _cm_getForeignData ($rowdata)
  2. $parent->getData ($where)
getInitialData ($where)
  1. _cm_getInitialData ($where_array)
getInitialDataMultiple ($where)
  1. _cm_getInitialDataMultiple ($where_array)
getLastIndex ()  
getNodeData ($expanded, $where)
  1. _cm_getNodeData ($expanded, $where, $wherearray)
  2. _processInstruction ($rowdata)
getParentData ()  
getPkeyArray ($rowdata, $next_task)
  1. getPkeyNames ()
  2. _cm_getPkeyNames ($pkeynames, $task_id, $pattern_id)
getPkeyNames ()
  1. return $this->primary_key
getPkeyNamesAdjusted()
  1. getPkeyNames ()
  2. _cm_getPkeyNames ()
getScrollIndex ()  
getScrollItem ($index)
  1. findJump ($next_index, $curr_index)
  2. array2where ($scrollItem, $pkeyNames)
getScrollSize ()  
getValRep ($item, $where)
  1. _cm_getValRep ($item, $where)
getWhere ()
  1. _cm_getWhere ()
initialise ($where, $selection, $search)
  1. _getInitialValues()
  2. _getInitialWhere($this->sql_where)
  3. _cm_initialise ($where, $selection, $search)
  4. _cm_changeConfig ($where)
  5. _cm_filterWhere()
  6. _cm_setJavaScript ($javascript)
  7. _examineWorkflowInstance ($where)
initialiseFileDownload ($where)
  1. $fieldarray = getData_raw ($where)
  2. _cm_initialiseFileDownload ($fieldarray)
initialiseFilePicker ()
  1. _cm_initialiseFilePicker()
initialiseFileUpload ($where)
  1. _cm_initialiseFileUpload ($wherearray)
insertMultiple ($rows)
  1. _cm_pre_insertMultiple ($rows)
  2. for each row: insertRecord ($rowdata)
  3. _cm_post_insertMultiple ($rows)
insertOrUpdate ($rowdata)
  1. $rowdata = _cm_pre_insertOrUpdate ()
  2. $pkeynames = getPkeyNames ()
  3. either: $rowdata = insertRecord ($rowdata)
  4. or: $rowdata = updateRecord ($rowdata)
  5. $rowdata = _cm_post_insertOrUpdate ()
insertRecord ($rowdata)
  1. _cm_getInitialData ($rowdata)
  2. _cm_pre_insertRecord ($rowdata)
  3. $insertarray = _validateInsert ($rowdata)
  4. _cm_commonValidation ($insertarray, $rowdata)
  5. _cm_validateInsert ($insertarray)
  6. _dml_insertRecord ($insertarray)
  7. _cm_post_insertRecord ($insertarray)
multiQuery ($query)  
$csv->outputCSV ($resource)
  1. $string = _cm_pre_output ($filename)
  2. $fieldarray = fetchRow ($resource)
  3. $fieldarray = formatData ($fieldarray)
  4. $string = _cm_post_output ($string, $filename)
$pdf->outputPDF_DetailView ($resource)
  1. $filename = _cm_pre_output ($filename)
  2. $string = $PDF->detailView ($resource)
  3. $string = $PDF->output ($name, $destination)
  4. startTransaction ()
  5. $string = _cm_post_output ($string, $filename)
  6. commit() or rollback()
$pdf->outputPDF_LabelView ($resource)
  1. $filename = _cm_pre_output ($filename)
  2. $string = $PDF->labelView ($resource)
  3. $string = $PDF->output ($name, $destination)
  4. startTransaction ()
  5. $string = _cm_post_output ($string, $filename)
  6. commit() or rollback()
$pdf->outputPDF_ListView ($resource)
  1. $filename = _cm_pre_output ($filename)
  2. $string = $PDF->listView ($resource)
  3. $string = $PDF->output ($filename, $destination)
  4. startTransaction ()
  5. $string = _cm_post_output ($string, $filename)
  6. commit() or rollback()
popupCall ($popupname, $where, $script_vars, $fieldarray, $settings)
  1. _cm_popupCall ($popupname, $where, $fieldarray, $settings)
popupReturn ($fieldarray, $return_from, $select_array)
  1. _cm_popupReturn ($fieldarray, $return_from, $select_array)
  2. _cm_getInitialData ($fieldarray)
  3. getForeignData ($fieldarray)
  4. _cm_post_popupReturn ($fieldarray, $return_from, $select_array)
  5. getExtraData ($fieldarray)
post_fileUpload ($filename, $filesize)
  1. _cm_post_fileUpload ($filename, $filesize)
post_search ($search, $selection)
  1. _cm_post_search ($search, $selection)
reset ($where)
  1. $this->setSqlSearch(null)
  2. $this->setOrderBy(null)
  3. $this->setOrderBySeq(null)
  4. $this->fieldarray = array()
  5. $this->initialise($where)
  6. $this->_cm_reset($where)
restart ($return_from, $return_action, $return_string)
  1. $this->_cm_restart($pattern_id, $zone, $return_from, $return_action, $return_string)
rollback ()
  1. $DML->rollback ($this->dbname)
setInstruction ($instruction)
  1. $this->instruction = $instruction
setChildData ($child_data)
setChildObject ($child_object)
setLookupData ($input)
  1. _cm_changeConfig ($where, $fieldarray)
  2. _cm_getExtraData ($where, $fieldarray)
setParentData ($parent_data)
setParentData ($parent_object)
setScrollArray ($where)
  1. _cm_setScrollArray ($where, $where_array)
setSqlSearch ($search)
  1. $this->sql_search = $search
  2. $this->sql_search_orig = $search
startTransaction ()
  1. _cm_getDatabaseLock ()
  2. $DML->startTransaction ($this->dbname)
unFormatData ($fieldarray)
  1. _cm_unFormatData ($fieldarray)
updateFieldArray ($fieldarray, $_POST)
  1. _cm_updateFieldArray ($fieldarray, $postarray, $rownum)
  2. getExtraData ($fieldarray)
updateLinkData ($rows, $postarray)
  1. _cm_pre_updateLinkData ($rows, $postarray)
  2. for each row, one of:
    1. insertRecord ($rowdata)
    2. updateRecord ($rowdata)
    3. deleteRecord ($rowdata)
  3. _cm_post_updateLinkData ($rows, $postarray)
updateMultiple ($rows, $postarray)
  1. _cm_pre_updateMultiple ($rows)
  2. for each row: updateRecord ($rowdata)
  3. _cm_post_updateMultiple ($rows)
updateRecord ($rowdata)
  1. _cm_pre_updateRecord ($rowdata)
  2. $updatearray = _validateUpdate ($rowdata)
  3. $originaldata = _dml_ReadBeforeUpdate ($where, $resuse_previous_select)
  4. _cm_commonValidation ($updatearray, $originaldata)
  5. _cm_validateUpdate ($updatearray, $originaldata)
  6. _dml_updateRecord ($updatearray, $originaldata)
  7. _cm_post_updateRecord ($fieldarray, $originaldata)
updateSelection ($selection, $replace)
  1. _cm_updateSelection ($selection, $replace)
validateDelete ($rowdata, $parent_table)
  1. _cm_validateDelete ($rowdata, $parent_table)
  2. $child->getCount($where)
  3. $rows = $child->getdata ($where)
  4. for each row: $child->validateDelete ($pkey, $parent_table)
validateSearch ($postarray)
  1. _cm_validateSearch ($postarray)

Notes on Public Methods

These methods are called from the controller scripts, and may also be called if you create a table object within any of your custom code.

boolean = $object->cascadeDelete ($where)
$where is a string in the format of a WHERE clause in an SQL query.

The steps in this operation are as follows:

$this->errors will contain any error messages.

boolean = $object->cascadeNullify ($update_array, $where)
$update_array is an associative array which identifies the field(s) to be updated (usually 'foreign_key=NULL').
$where is a string in the format of a WHERE clause in an SQL query.

The steps in this operation are as follows:

$this->errors will contain any error messages.

$fieldarray = $object->clearEditableData (fieldarray)
$fieldarray (IN) is is an associative array containing the details of a single database occurrence.
$fieldarray (OUT) will be the input array with any amendments.

This will examine each field if $fieldarray, and if it is editable (i.e. the noedit attribute is not set), the current value will be nullified.

void $object->clearScrollArray ()

This is used to delete the contents of $this->scrollarray.

$errors = $object->commit ()
$errors will be an array of zero or more error messages.

This will call _examineWorkflow() and either issue a commit or rollback depending on the result. Note that this has no effect on tables which do not support database transactions, such as MyISAM tables.

$fieldarray = $object->customButton ($fieldarray, $button)
$fieldarray (IN) is an associative array containing the details of a single database occurrence.
$button is the name of the button field which was pressed.
$fieldarray (OUT) will be the same array, but after any changes have been made.

This function will perform the following steps:

Please see FAQ137 for details on how to define and use custom buttons in the data area.

$rows = $object->deleteMultiple ($rows)
$rows (IN) is an indexed array of database occurrences, and each occurrence is an associative array of name=value pairs.
$rows (OUT) is the same array.

For each record identified in $rows it will call the deleteRecord() method.

$this->errors will contain any error messages.

$this->numrows will contain the count of deleted records.

$this->messages will contain a message in the format 'N records were deleted from TABLENAME'.

$rowdata = $object->deleteRecord ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) is the same array.

The steps in this operation are as follows:

$this->errors will contain any error messages.

Note that the deletion may have been verified beforehand by a call to validateDelete().

void $object->deleteRelations ($rowdata)
$rowdata is an associative array containing the details of a single database occurrence.

This will examine the contents of $this->child_relations for the database occurrence identified in $rowdata, and for each child record found belonging to that relationship it will perform the action indicated by its type:

$this->errors will contain any error messages.

The current value of $this->audit_logging in the parent class is passed down to each of the child classes so that all deletes are either logged (or not logged) together. This may temporarily change the value in a child class for this delete operation.

$where = $object->deleteScrollItem ($index)
$index is the index number an entry in $this->scrollarray.
$where will contain a string which identifies the next available occurrence.

This is used to delete an item from $this->scrollarray, and to return the primary key of the next available entry.

$msg = $object->deleteSelection ($selection)
$selection is a string in the format of a WHERE clause in an SQL query and may identify one or more database occurrences.
$msg will contain a message in the format 'N rows were updated'.

This passes control to _cm_deleteSelection() to perform the relevant processing for those records which match the selection criteria.

$this->numrows will contain the count of records which were deleted.

$rowdata = $object->eraseRecord ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) is the same array.

The sequence of events is as follows:

This will effectively delete the selected record, its children, the children of its children, and so on all the way down to the bottom of the relationship hierarchy.

$this->errors will contain any error messages.

void $object->eraseRelations ($rowdata)
$rowdata is an associative array containing the details of a single database occurrence.

This will iterate through the contents of $this->child_relations and perform the following on any related occurrence:

$this->errors will contain any error messages.

$result = $object->executeQuery ($query)
$query is an arbitrary query, or several queries separated by ';'(semi-colon) or an array of queries.
$result will contain the actual result if only a single query is supplied, or TRUE/FALSE if several queries are supplied.

This is used to execute a number of arbitrary SQL queries "as-is" without the overhead and complexity of supplying query fragments which are then combined and executed one query at a time. Here is an example:

    // step 1: create a copy of the PRODUCT table
    $query[] = "DROP TEMPORARY TABLE IF EXISTS temp_product;";
    $query[] = "CREATE TEMPORARY TABLE IF NOT EXISTS `temp_product` LIKE {$productDB}product;";
    $query[] = "INSERT INTO temp_product (product_id, product_name)
SELECT product_id, product_name
FROM {$productDB}product
WHERE date_intro <= '$date_to 23:59:59' AND end_date_sales >= '$date_from 00:00:00'
  AND NOT EXISTS(SELECT 1 FROM {$productDB}prod_cat_class 
	               WHERE product_id=product.product_id 
					   AND prod_cat_id LIKE 'NOTFORSALE%' 
					   AND start_date <= '$date_to 23:59:59' AND end_date >= '$date_to 00:00:00');";

    // step 2: create a second table containing items which have been ordered during this period
    $query[] = "DROP TEMPORARY TABLE IF EXISTS temp_ordered;";
    $query[] = "CREATE TEMPORARY TABLE IF NOT EXISTS `temp_ordered` LIKE {$productDB}product;";
    $query[] = "INSERT INTO temp_ordered (product_id, product_name)
SELECT product_id, 'name' 
FROM order_item
WHERE order_item.order_type='S'
  AND order_item.created_date >= '$date_from' AND order_item.created_date <= '$date_to'
  AND order_item.order_item_status_type_id NOT IN ('PEND','CNCL','CNRG','HOLD','SAM1','SAM2','SAM3','SAM4')
GROUP BY product_id;";

    // step 3: remove from TEMP_PRODUCT anything which exists in TEMP_ORDERED
    $query[] = "DELETE FROM temp_product WHERE product_id IN (SELECT product_id FROM temp_ordered);";

    $result = $this->executeQuery($query);
$rowdata = $object->fetchRow ($resource)
$resource is a database resource created by getData_serial().
$rowdata will contain an associative array of all the fields from a single database occurrence.

This is used to return database records one at a time instead of in multiples. After each iteration the resource pointer will be advanced to the next occurrence, and $rowdata will return FALSE when end-of-file is reached.

The actual steps are as follows:

$rowdata = $object->fetchRowChild ($rowdata)
$rowdata (IN) is a database row which represents the parent node in a hierarchy.
$rowdata (OUT) will contain the next available child row in that hierarchy.

This is used in an OUTPUT 6 pattern to extract child nodes from a hierarchy so that they can be added to a CSV file.

For each parent node the steps are as follows:

Note that each child record which is returned may have children of its own, so this function will go down as many levels as possible.

$file_name = $object->filePickerSelect ($file_name)
$file_name (IN) is the name of the selected file.
$file_name (OUT) is the name of the selected file.

This is used to process the file selected by the user in the filepicker screen. This is not called if the hyperlink_direct option has been used as the hyperlink will cause the selected file to be processed directly in the browser instead of being passed back to this task.

This is a wrapper for _cm_filePickerSelect() which may or may not change the default behaviour.

$output_name = $object->fileUpload ($input_name, $temp_file)
$input_name is the name of the file being uploaded from the client.
$temp_file is a copy of the file in the temporary directory.
$output_name is the file name to be used on the server.

This is used to deal with the file being uploaded from the client before it is written to the server, for example changing it's name into something more meaningful. A temporary copy of the file is available in $temp_file so that its contents may be checked, such as verifying that an image file has the correct dimensions.

This is a wrapper for _cm_fileUpload().

$rowdata = $object->formatData ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) is the same array after any values have been formatted.

This is called when the data is being transferred to the XML file for output to the user. It will take the contents of $rowdata and, using the field specifications in the $this->fieldspec array, will format any values, such as dates and numbers, before they are displayed to the user. For example, a date such as '2006-01-02' will be converted into '02 Jan 2006'.

It will also call _cm_formatData() to perform any custom formatting.

This is the opposite of unformatData().

$result = $object->free_result ($resource)
$resource is a database resource created by getData_serial().
$result will return either TRUE or FALSE.

This is used to free a resource created by getData_serial() and processed by fetchRow().

$child_data = $this->getChildData ()
$child_data is the data extracted from the child object.

This method is only valid in a task which uses more than one object in a parent-child or parent-child-grandchild relationship.

It is used to obtain the contents of $fieldarray from the child object.

$name = $object->getClassName ()
$name will contain the name, in lower case.

This will return the name of the current class so that it can be used as the identity of the current database table when adding data to an XML document. Where the current class is a subclass with a numerical suffix, as in _s01, this will be removed. For example:

A numerical suffix is used when the purpose of the subclass is to contain some different business logic. A non-numeric suffix is used when data from the same database table needs to be inserted into multiple zones within the same XSL stylesheet, in which case the data for each zone comes from an object with a different class name, such as mnu_task_snr and mnu_task_jnr. This is described in Using subclasses to provide alias names

$fieldarray = $object->getColumnNames ($where)
$where is a string in the format of a WHERE clause in an SQL query and may identify one or more database occurrences. fieldarray will contain the default value for each field.

This is used in the OUTPUT4 pattern to read a single record from the database and look at the result to obtain a list of field names. Each of these fields will be added to $this->fieldspec with the following properties:

$fieldspec[$column]  = array('type' => 'string',
                             'control' => 'dropdown',
                             'optionlist' => 'selected');

All values will initially be set to 'Y' (selected), but these can be changed to 'N' in the $this->_cm_getColumnNames() method before the screen is displayed.

$count = $object->getCount ($where)
$where is a string which identifies what needs to be counted.
$count will contain the count of entries.

If the $where string is in the format column='value' the following statement will be constructed and executed:

$count = $this->getCount("column='X'");
  will execute:
SELECT count(*) from $this->tablename WHERE column='X'

If the $where string begins with 'SELECT ' it will be assumed to be a complete SQL statement and will be executed without any modification, as with the following examples:

$count = $this->getCount("SELECT MAX(seq_no) FROM table27 WHERE column='X'");
$count = $this->getCount("SELECT SUM(quantity) FROM order_item WHERE order_id=42");
$rows = $object->getData ($where)
$where is a string in the format of a WHERE clause in an SQL query and may identify one or more database occurrences.
$rows will be an indexed array of different database rows (occurrences) and each row will be an associative array of field names and values.

This is called from within each page controller to process any selection or search criteria passed down by the parent screen, or from a child SEARCH screen, before constructing an SQL query to retrieve data from the database. Because this uses strings in the $where clause which are constructed by the framework it may not be suitable for $where clauses which are constructed manually inside any object. In these cases the getData_raw() method may be more appropriate.

The following optional variables are also used to construct the SQL query:

The steps in this operation are as follows:

$this->numrows will contain the count of records retrieved from the database.

$rows = $object->getData_raw ($where)
$where is a string in the format of a WHERE clause in an SQL query and may identify one or more database occurrences.
$rows will be an indexed array of different database rows (occurrences) and each row will be an associative array of field names and values.

This calls _dml_getData() without adjusting any related object properties beforehand, and without formatting the data afterwards.

When retrieving another table's data from within a table object this method should be used instead of getData() where the reformatting of the $this->sql_select, $this->sql_from and $where variables inside _sqlAssembleWhere() may cause problems, especially with complex combinations of AND, OR and NOT.

The following optional variables are also used to construct the SQL query:

$this->numrows will contain the count of records retrieved from the database.

$resource = $object->getData_serial ($where)
$where is a string in the format of a WHERE clause in an SQL query and may identify one or more database occurrences.
$resource will be a resource that can be processed by the fetchRow() method, and subsequently released by the free_result() method.

This is similar to getData(), but instead of returning an array of occurrences it returns a resource from which individual occurrences can be obtained one at a time using fetchRow(). This is useful for batch jobs in which an unknown number of database occurrences need to be processed as it could cause problems if all those occurrences were retrieved and loaded into an array before they were processed. With this method it is possible to fetch and process a row before fetching the next row.

The following optional variables are also used to construct the SQL query:

The steps in this operation are as follows:

$this->numrows will contain the count of records retrieved from the database.

Here is some sample code:

$where = "workitem_status='EN' AND deadline <= '$now'";
$workitem_result = $workitem->getData_serial($where);
while ($row = $workitem->fetchRow($workitem_result)) {
    ... process contents of $row ...
} // while
$result = $workitem->free_result($workitem_result)
$array = getExpanded ()
$array will contain a list of nodes which have been expanded.

When viewing a tree structure it is possible to expand a node so that its children will appear in the display. This will identify those nodes which have been expanded so that the system will retrieve its children.

$array = getExtraData ($input, $where=null)
$input can be either a string (in the format of a WHERE clause) or an array (associative or indexed). A string will be converted into an array.
$array will be the same as $input, but after any changes have been made.

This retrieves data that may be of use during the transaction which would not otherwise be made available, such as for INSERTS. The contents of $input may be a WHERE string, or it may be an array of database records. The sequence of steps is as follows:

$array = $object->getFieldSpec ()
$array will be an associative array of field names and their specifications.

This will return the current contents of the $this->fieldspec. Note that it may have been amended during the processing of the current task.

$array = $object->getFieldSpec_original ()
$array will be an associative array of field names and their specifications.

This will return the contents of the $this->fieldspec array after it has been reloaded from the <tablename>.dict.inc file which was exported from the Data Dictionary. This is done immediately prior to any INSERT or UPDATE operations to remove any changes made in custom code as these changes may be out of step with the physical database structure and could cause a fatal error.

$rowdata = $object->getForeignData ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) will be the same array, but after any changes have been made.

This will first call _cm_getForeignData() to perform any custom processing.

Then it will examine the contents of $this->parent_relations. For each relationship it will perform the following:

Here is an example of the code:

    foreach ($this->parent_relations as $reldata) {
        if (isset($reldata['parent_field'])) {
            // may be more than one parent_field, so turn it into an array of separate field names
            $parent_fields = extractFieldNamesIndexed($reldata['parent_field']);
            // construct WHERE clause to read from parent table
            $where = '';
            foreach ($reldata['fields'] as $fldchild => $fldparent) {
                if (strlen($fldchild) < 1) {
                    // 'Name of child field missing in relationship with $tblchild'
                    $this->errors[] = getLanguageText('sys0110', strtoupper($tblchild));
                    break;
                } // if
                if (!isset($fieldarray[$fldchild]) or strlen($fieldarray[$fldchild]) == 0) {
                    // foreign key field is missing, so stop further processing
                    $where = '';
                    break;
                } // if
                if (empty($where)) {
                    $where = "$fldparent='" .addslashes($fieldarray[$fldchild]) ."'";
                } else {
                    $where .= " AND $fldparent='" .addslashes($fieldarray[$fldchild]) ."'";
                } // if
            } // foreach
            if (empty($where)) {
                // $where is empty, so set foreign field(s) to empty
                foreach ($parent_fields as $ix => $parent_field) {
                    $fieldarray[$parent_field] = null;
                } // foreach
            } else {
                if (isset($fieldarray[$parent_fields[$ix]]) AND !empty($fieldarray[$parent_fields[$ix]])) {
                    // (last) parent field is already there, so do nothing
                } else {
                    $tblparent = $reldata['parent'];
                    // instantiate an object for this table
                    if (array_key_exists('subsys_dir', $reldata)) {
                        $dir = $_SERVER['DOCUMENT_ROOT'] .getParentDIR() .'/' .$reldata['subsys_dir'] .'/';
                    } else {
                        $dir = NULL;
                    } // if
                    if (!class_exists($tblparent)) {
                        require_once $dir ."classes/$tblparent.class.inc";
                    } // end
                    $parentobj = new $tblparent;
                    $parentobj->sql_select = $reldata['parent_field'];
                    $parent_data = $parentobj->getData($where);
                    if (count($parent_data) > 0) {
                        // copy specified parent field(s) into $fieldarray
                        foreach ($parent_fields as $ix => $parent_field) {
                            $fieldarray[$parent_field] = $parent_data[0][$parent_field];
                        } // foreach
                    } else {
                        // not found, so set foreign key(s) to empty
                        foreach ($reldata['fields'] as $fldchild => $fldparent) {
                            $fieldarray[$fldchild] = null;
                        } // foreach
                    } // if
                } // if
            } // if
        } // if
    } // foreach

Click on the following hyperlink for a description of getLanguageText().

$array = $object->getInitialData ($input)
$input is either a string in the format of a WHERE clause, or an associative array.
$array will be an associative array containing initial values.

This is used with INPUT tasks to supply any initial values before the screen is displayed to the user. Without it all the fields would be blank.

The steps in this operation are as follows:

$array = $object->getInitialDataMultiple ($input)
$input can be either a string (in the format of a WHERE clause) or an associative array. A string will be converted into an array.
$array will be the same as input, but after any initial values have been added.

This is a wrapper for _cm_getInitialDataMultiple().

This is used in the ADD4 transaction pattern to create the records that will be inserted by calling insertMultiple().

This is also used in the ADD5 transaction pattern to create a number of blank records that will be displayed in the screen into which the user can then enter data.

$index = $object->getLastIndex ()
$index is the highest index number in $this->scrollarray.
$array = $object->getNodeData ($expanded, $where)
$expanded is either an indexed array which identifies the nodes which are to be expanded, or a string containing 'ALL' to signify all available nodes.
$where is a string which is used as selection criteria.
$array will be an indexed array of different database occurrences, and each occurrence will be an associative array of field names and values.

The steps in this operation are as follows:

$parent_data = $this->getParentData ()
$parent_data is the data extracted from the parent object.

This method is only valid in a task which uses more than one object in a parent-child or parent-child-grandchild relationship.

It is used to obtain the contents of $fieldarray from the parent object.

$array = $object->getPkeyArray ($rowdata=null, $next_task=null)
$rowdata is an optional associative array of name=value pairs for a single database occurrence.
$next_task is as associative array with details of the next task
$array will be the same as the input array, but will only contain those fields which exist within the primary key.

If $rowdata is not supplied then $this->fieldarray will be used instead.

The processing sequence is as follows:

The remaining fields will then be used to construct a string in the format of the WHERE clause of an sql SELECT statement.

$array = $object->getPkeyNames ()
$array will be an indexed array of field names.

This is used to obtain a list of all the fields which comprise the primary key in the current table. It will obtain the relevant information from $this->primary_key.

$array = $object->getPkeyNamesAdjusted ()
$array will be an indexed array of field names.

This is used to obtain a list of field names which will subsequently be used in the construction of a string in the format of the WHERE clause of an sql SELECT statement.

The processing sequence is as follows:

$index = $object->getScrollIndex ()
$index is the current pointer into $this->scrollarray.
$where = $object->getScrollItem (&$index)
$index (IN) is a key to an entry in $this->scrollarray.
$index (OUT) will contain an index number which may be different.
$where will contain the primary key of the entry indicated by $index.

The processing sequence is as follows:

$size = $object->getScrollSize ()
$size is the current count of items in $this->scrollarray.
$output = $object->getWhere ($next_task)
$output is the $where string from the object.
$next_task is an associative array which contains details of the child task.

This is called when a navigation button is pressed in order to extract the $where string from the main object in the current task so that it can be passed to the next task.

The _cm_getWhere() method is called in case this string needs to be customised.

See also: Appendix I: Passing context between objects.

$array = $object->getValRep ($item, $where)
$item is the name of the list to be returned as there may be more than one.
$where is an optional string containing selection criteria.
$array will contain the output as an associative array.

This is used to obtain the contents of a dropdown list or radio group. The term "ValRep" stands for "Value/Representation" where "Value" is what is held internally and "Representation" is what is shown to the user. The list may either be hard-coded, or extracted from a database table. The implementation details are defined in _cm_getValRep().

$where = $object->initialise ($where, $selection, $search)
$where (IN) is a string in the format of a WHERE clause in an SQL query.
$selection is an optional string similar to $where
$search is an optional string similar to $where
$where (OUT) is the version of $where (IN) after any amendments have been applied.

This is used in all forms to perform any initialisation when the form is first activated. It does the following:

If the task was activated from a navigation button then both $where and $selection will be passed down from the calling task. Both values are made available so that the application code can decide which one to use. The $selection string will come from the entity containing multiple rows which were marked as selected, such as the main entity in a LIST1 or the inner/child entity in a LIST2 or LIST3. The $where string will contain whatever was passed into that entity as $where.

Note the following processing rules regarding $where and $selection:

Note that the $search string is only available with tasks of type OUTPUT which are called from a SEARCH1 task.

All tasks whose pattern is in the ADD group will also use the getInitialData() method.

If $mode is equal to "search" then the following actions will be taken:

void $object->initialiseFileDownload ($where)
$where is a string in the format of the WHERE clause in an SQL query.

This is used in a file download transaction to specify default values for the following:

The processing sequence is as follows:

void $object->initialiseFilePicker ()

This is used in a file picker transaction to specify default values for the following:

The _cm_initialiseFilePicker() method is called to perform any custom processing.

void $object->initialiseFileUpload ($where)
$where is a string in the format of a WHERE clause in an SQL query, as passed down by the previous screen.

This is used in a file upload transaction to specify default values for the following:

The _cm_initialiseFileUpload() method is called to perform any custom processing.

Once the user identifies the file to be uploaded and presses the SUBMIT button control is passed to the fileUpload() method.

$rows = $object->insertMultiple ($rows)
$rows (IN) is an indexed array containing the details of each row to be inserted.
$rows (OUT) is the same array after it has been processed, but may contain changes.

This uses data which is typically created with the getInitialDataMultiple() method.

The processing sequence is as follows:

$this->errors will contain any error messages.

$this->numrows will contain the count of inserted records.

$this->messages will contain a message in the format 'N records were inserted into TABLENAME'.

$rowdata = $object->insertRecord ($rowdata)
$rowdata (IN) is an associative array containing the details of a single row to be inserted.
$rowdata (OUT) is the same array after it has been processed, but may contain changes.

The steps in this operation are as follows:

$this->errors will contain any error messages.

$this->numrows will be set to 1 if the record is inserted, or 0 if it is not.

Just before a record is inserted a lookup will be made using the given primary key. If a record already exists then $this->errors will be loaded with an appropriate message UNLESS $this->no_duplicate_error is set to TRUE, in which case $this->numrows will be set to 0 and no error message will be produced.

$array = $object->insertOrUpdate ($array)
$array (IN) may be either an indexed array containing data for several database rows, or an associative array containing data for a single database row.
$array (OUT) is the same array after it has been processed, but may contain changes.

The steps in this operation are as follows:

$this->errors will contain any error messages.

$result = $object->multiQuery ($query)
This is a synonym for $object->executeQuery ($query)
$output = $object->outputCSV ($resource)
$resource is a database resource created by getData_serial().
$output will be a string containing all the retrieved data in CSV (comma separated values) format.

The steps in this operation are as follows:

$string = $object->outputPDF_DetailView ($resource)
$resource is a database resource created by getData_serial().
$string is the PDF output if 'destination' is set to 'string'.

This method is used to produce output in PDF format (detail view) which follows the layout defined in the report structure file. It has the following steps:

$string = $object->outputPDF_LabelView ($resource)
$resource is a database resource created by getData_serial().
$string is the PDF output if 'destination' is set to 'string'.

This method is used to produce output in PDF format (label view) which follows the layout defined in the report structure file. It has the following steps:

$string = $object->outputPDF_ListView ($resource)
$resource is a database resource created by getData_serial().
$string is the PDF output if 'destination' is set to 'string'.

This method is used to produce output in PDF format (list view) which follows the layout defined in the report structure file. It has the following steps:

$where = $object->popupCall ($popupname, $where, $script_vars, $fieldarray, $settings)
$popupname identifies the popup screen which is to be processed.
$where (IN) is the WHERE clause being processed by the current screen.
$script_vars is the set of variables for the current screen that will be saved in the $_SESSION array.
$fieldarray is an associative array containing the data for the current database occurrence.
$settings is a string containing optional settings for the popup screen.
$where (OUT) is the WHERE clause that will be passed to the popup screen.

If any screen contains a popup button, and the user presses this button, this method will be called to define the parameters which are to be passed to the popup form, which will then be activated. The current form will be suspended while the popup form has control, and will be automatically reactivated as soon as the popup form terminates (refer to popupReturn()).

Although a value may be brought forward in $where it may not be applicable in the popup form, so by default the output value will be empty. This behaviour may be customised in _cm_popupCall().

Currently the default value in $settings is select_one=true which will cause the SELECT column to be displayed as a radio group so that only one entry can be selected at any one time. This can be reversed by specifying select_one=false which will then cause the SELECT column to be displayed as a series of checkboxes.

$rowdata = $object->popupReturn ($rowdata, $return_from, $selection)
$rowdata (IN) is an associative array containing the current data for a database occurrence.
$return_from is the name of the popup form which has just been processed.
$selection is a string which identifies what was selected in the popup or filepicker form.
$rowdata (OUT) will contain the input array adjusted to include whatever was selected.

If any screen contains a popup button, and the user presses this button, this method will be called after the user makes a selection in the popup form and presses the CHOOSE button to return to the current form. This is so that the selected item can be merged with the existing data for the current form.

If the selection was a filename then this name will be added to $rowdata, _cm_popupReturn() will be called in case any custom processing is required, and the function will exit.

If the selection was a database occurrence then $selection will contain the primary key (which may be a compound key) of that occurrence. The selection will be passed to _cm_popupReturn() for any custom processing before it is added to $rowdata, then passed to _cm_getInitialData(), getForeignData() and _cm_post_popupReturn().

This function will perform the following steps:

$filename = $object->post_fileUpload ($filename, $filesize)
$filename (IN) is the name of the file which has just been uploaded.
$filesize is the size of the file which has just been uploaded.
$filename (OUT) is the filename in case it needs to be changed.

This is a wrapper for the _cm_post_fileUpload() method.

void = $object->post_search ($search, $selection)
$search is a string in the format of the WHERE clause in an SQL query using the values which were submitted in the search task.
$selection is a string containing any selection criteria passed down from the parent task.

This is a wrapper for the _cm_post_search() method.

void = $object->reset ($where)
$where is a string in the format of a WHERE clause in an SQL query.

This is used when the RESET button is pressed. This button is available in those transactions where the user can modify the selection and sorting criteria to alter how the current set of data is displayed. By pressing this button the user will remove any additional selection and sorting criteria and return the transaction to its original state. This is done by performing the following:

void = $object->restart ($return_from, $return_action, $return_string)
$return_from is the identity of the task from which the system is returning.
$return_action is a string which indicates the completion status of that task.
$return_string is a string which is returned from the $return_from task.

This is used when a task is restarted after being suspended for the processing of a child task. It is also used in a MULTI2 task when the $search values are changed.

It is a wrapper for the _cm_restart() method.

boolean = $object->rollback ()

If a database update fails this will rollback (undo) any changes made since the previous call to startTransaction(). Note that this has no effect on tables which do not support database transactions, such as MyISAM tables.

void = $object->scriptNext ($task_id, $where=null, $selection=null, $task_array=null)

If there is an open database transaction then this will call $this->commit(), after which it will call the scriptNext() function to transfer control to the script identified in $task_id.

void = $object->scriptPrevious ($errors=null, $messages=null, $action=null, $instruction=null)

If there is an open database transaction then this will call $this->commit(), after which it will call the scriptPrevious() function to transfer control to the previous script in the current stack.

void = $this->setChildData ($child_data)
$child_data is the data to be inserted into the child object.

This method is only valid in a task which uses more than one object in a parent-child or parent-child-grandchild relationship.

It is used to replace the contents of $fieldarray in the child object which has been updated following a call to getChildData().

void = $object->setChildObject (&$child_object)
$child_object is a reference to the child object which is to be inserted into its parent object.

This method is used in a page controller which uses more than one object in a parent-child or parent-child-grandchild relationship. It then allows the getChildData() and setChildData() methods, as well as other methods, to operate on that object.

$array = $object->setFieldArray ($fieldarray)
$fieldarray is an array which can either contain a single associative array representing a single database row, or an indexed array of associative arrays representing several database rows.

This inserts the array into the $this->fieldarray of the specified object, replacing any array which currently exists.

$array = $object->setInstruction ($instruction)
$instruction is an array which identifies particular rows and the action to be taken on each of those rows.

This takes any instructions passed back by a child form which has just terminated, and loads them into $this->instruction for the current object so that they may be processed in a subsequent call to $this->_processInstruction().

$array = $object->setLookupData ($input)
$input is either an associative array, or a WHERE string which will be converted into an array.
$array will contain the input data, but as an associative array.

This is used to re-obtain any lookup data, such as will be used as the contents of any dropdown lists or radio groups.

The steps in this operation are as follows:

void = $object->setOrderBy ($orderby)
$orderby is a string in the format of the ORDER BY clause of an SQL query.

This sets the value for $this->sql_orderby.

void = $object->setOrderBySeq ($orderbyseq)
$orderbyseq is a string which indicates either ASC(ending) or DESC(ending).

This sets the value for $this->sql_orderby_seq.

void = $this->setParentData ($parent_data)
$parent_data is the data to be inserted into the parent object.

This method is only valid in a task which uses more than one object in a parent-child or parent-child-grandchild relationship.

It is used to replace the contents of $fieldarray in the parent object which has been updated following a call to getParentData().

void = $object->setParentObject (&$parent_object)
$parent_object is a reference to the parent object which is to be inserted into its child object.

This method is used in a page controller which uses more than one object in a parent-child or parent-child-grandchild relationship. It then allows the getParentData() and setParentData() methods, as well as other methods, to operate on that object.

$where = $object->setScrollArray ($where)
$where (IN) is a string in the format of a WHERE clause in an SQL query.
$where (OUT) will contain the primary key of the first entry in the newly-constructed array.

This is used in a MULTI1 transaction pattern to create $this->scrollarray, which is an array of primary keys for records which may or may not currently exist in the database. After the array has been created using _cm_setScrollArray() the display will start at the first occurrence, and the user will be able to step through all the occurrences using the standard scrolling mechanism. An UPDATE operation will perform an INSERT if the selected occurrence does not currently exist in the database.

void = $object->setSqlSearch ($search)
$search is a string in the format of a WHERE clause in an SQL query.

This is used to set/reset the search criteria which will be used in the next call to $this->getData(). It will affect the following variables:

void $object->sqlSelectDefault ()

This is used to set the components of the subsequent sql SELECT statement to their default values. It performs the _sqlForeignJoin() method to include the contents of the $parent_relations array. After this is called and before the getData() method is called the following variables are available for further customisation:

boolean = $object->startTransaction ()

This will call _cm_getDatabaseLock() to issue any database locks before telling the database that a new transaction has started. This will terminate with the next commit or rollback.

$rowdata = $object->unFormatData ($rowdata)
$rowdata (IN) is an associative array containing the data from a single database occurrence.
$rowdata (OUT) will contain the input data after any reformatting has been performed.

This will take any data which has been formatted for the user, such as dates and numbers, and convert them back into internal format. For example, a date such as '02 Jan 2006' will be converted into '2006-01-02'.

It will also call _cm_unformatdata() to perform any custom unformatting.

This is the opposite of formatData().

$fieldarray = $object->updateFieldArray ($fieldarray, $_POST)
$fieldarray (IN) is the current contents of the object before any amendments are applied.
$_POST is the array of data POSTed from the current form.
$fieldarray (OUT) is contents of the object after it has been amended by the contents of $_POST.

This method will only be used when the form is submitted without the use of a SUBMIT button, such as a javascript submit() function. It will merge the $_POST array with the object's current $fieldarray and output the result.

This function will perform the following steps:

Note that this method does NOT automatically update the database. This is only done when a SUBMIT button is pressed, in which case a method such as $object->insertRecord() or $object->updateRecord() will be used instead.

$rows = $object->updateLinkData ($rows, $postarray)
$rows (IN) is an indexed array which contains the current data for several database occurrences.
$postarray is an array which shows which occurrences in $rows were SELECTED or NOT SELECTED.
$rows (OUT) will contain the updated data for those database occurrences.

This is used in the LINK1 transaction pattern to update the link table in a many-to-many relationship. The contents of $postarray will be processed to ensure that a database occurrence exists where SELECTED=TRUE and does not exist where SELECTED=FALSE.

The steps in this operation are as follows:

$this->errors will contain any error messages.

$rows = $object->updateMultiple ($rows, $postarray)
$rows (IN) is an indexed array containing the data for several database occurrences.
$postarray (optional) contains any changes.
$rows (OUT) will contain the updated data for those database occurrences.

The steps in this operation are as follows:

$this->errors will contain any error messages.

$this->messages will contain a message in the format 'N records were updated on TABLENAME'.

$this->numrows will contain a count of the records which were updated.

$rowdata = $object->updateRecord ($rowdata)
$rowdata (IN) is an associative array containing the data for a single database occurrence.
$rowdata (OUT) will contain the updated data for that database occurrence.

The steps in this operation are as follows:

$this->errors will contain any error messages.

$msg = $object->updateSelection ($selection, $replace)
$selection a string in the format of a WHERE clause in an SQL query.
$replace a string in the format of a SET clause in an SQL query.
$msg will contain a message in the format 'N records were updated on TABLENAME'.

This is intended to issue an SQL query in the format:

UPDATE <tablename> SET $replace WHERE $selection

The actual processing will be performed within _cm_updateSelection() to allow customisation.

$this->numrows will contain a count of the records which were updated.

$rowdata = $object->validateDelete ($rowdata, $parent_table=null)
$rowdata (IN) is an associative array containing the data for a single database occurrence.
$parent_table is empty by default, but during cascade deletes it identifies the table from which the delete has been initiated.
$rowdata (OUT) will contain the updated data for that database occurrence.

This will check that the database record identified in $rowdata can be deleted without causing problems.

The steps in this operation are as follows:

$this->errors will contain any error messages.

$postarray = $object->validateSearch ($postarray)
$postarray (IN) an associative array containing input data.
$postarray (OUT) the same array after any changes have been made.

This will allow the input data from a SEARCH screen to be validated before it is passed back to the previous task for use in the WHERE clause of an sql SELECT statement.

This will check that any required field is not empty, then call _cm_validateSearch() to perform any custom processing.


Notes on Internal (private) Methods

These methods should not be called in any user code.

void $PDF->detailView ($this, $resource)
$this is the database object from which this method was called.
$resource is a database resource created by getData_serial().

This is called from outputPDF_DetailView(). It will perform the following steps:

void $PDF->labelView ($this, $resource)
$this is the database object from which this method was called.
$resource is a database resource created by getData_serial().

This is called from outputPDF_LabelView(). It will call fetchRow() on all the records provided in $resource and perform the following steps:

void $PDF->listView ($this, $resource)
$this is the database object from which this method was called.
$resource is a database resource created by getData_serial().

This is called from outputPDF_ListView(). It will perform the following steps:

$string $PDF->output ($name, $destination)
$name is the name of the PDF file.
$destination is the place where the document will be sent.
$string will contain the output if $destination = 'S'.

If $name is empty then the output will be sent to the browser ($destination = 'I') with the name doc.pdf.

$destination can have one of the following values:

void $this->_dml_deleteRecord ($rowdata)
$rowdata is an associative array containing the data for a single database occurrence.

This will use the contents of $rowdata and the list of primary keys in the $this->fieldspec array to build a WHERE clause, then construct and execute a query in the following format:

DELETE FROM $tablename WHERE $where

If the audit flag is turned on for this table it will also write the details out to the audit database.

$count = $this->_dml_deleteSelection ($selection, $from=null, $using=null)
$selection is a string in the format of a WHERE clause in an SQL query, and may identify multiple database occurrences.
$from is only used in a multi-table delete.
$using is only used in a multi-table delete.
$count will identify how many records were actually processed.

This will construct and execute a query in the following format:

DELETE FROM $tablename WHERE $selection

If the audit flag is turned on for this table it will also write the details out to the audit database.

If values are supplied for both $from and $using the query will be constructed as follows:

DELETE FROM $from USING $using WHERE $selection

Note that this variation will not write out details to the audit database as the records are not read before they are deleted.

$rowdata = $this->_dml_fetchRow ($resource)
$resource is a database resource created by _dml_getData_serial().
$rowdata will contain an associative array of data from the next available row, or FALSE if no more rows exist.

This is used after a call to getData_serial() to return the database records one at a time.

$rows = $this->_dml_getData ($where)
$where is a string in the format of a WHERE clause in an SQL query.
$rows will contain a two-level array of retrieved data. The first level will be indexed by row number, and each row will contain an associative array of name=value pairs.

As well as using the $where string this method will also use the following optional strings:

  1. $sql_select
  2. $sql_from
  3. $sql_groupby
  4. $sql_having
  5. $sql_orderby

The first step is to construct and execute a query in the following format:

SELECT count(*) FROM $from_str $where_str $group_str $having_str

If the returned count is zero then the function immediately exits, otherwise the value is used with the $pageno and $rows_per_page variables to construct the LIMIT and OFFSET portions of the subsequent SELECT statement.

If $lock_rows is set then $lock_str will be set as follows:

A complete SQL query will be constructed from the various component parts, some of which may be NULL, as follows:

$this->query = "SELECT $select_str FROM $from_str
                       $where_str
                       $group_str 
                       $having_str
                       $sort_str 
                       $limit_str 
                       $lock_str"
if (!$result = <DBMS>_query($this->query)) {
    trigger_error($this, E_USER_ERROR);
} // if     

If you wish to view the SQL statements that are constructed and issued take a look at $GLOBALS['log_sql_query'].

All resulting data will be converted into a two-level array - the first level will be indexed by row number, and each row will contain an associative array of name=value pairs. All column names will be converted into lower case. This is done using code similar to the following:

$array = array();
while ($row = DBMS_fetch_assoc($result)) {
    $array[] = array_change_key_case($row, CASE_LOWER);
} // while
return $array;
$resource = $this->_dml_getData_serial ($where)
$where is a string in the format of the WHERE clause in an SQL query.
$resource will be a database resource which can be used to access all the retrieved rows.

This is similar to _dml_getData(), but instead of returning an array of occurrences it returns a resource from which individual occurrences can be obtained using fetchRow().

$rowdata = $this->_dml_insertRecord ($rowdata)
$rowdata (IN) is an associative array containing the data for a single database occurrence which is be be inserted.
$rowdata (OUT) is the same data after it has been inserted, which may now include any auto-generated values.

This will remove any field from the array which does not exist in the table, such as the SUBMIT button which exists in the $_POST array created by the web server.

If any field has the autoinsert option set it will insert a value according to the data type:

If any field in the primary key has the auto_increment option set then ensure that is has no value in $rowdata otherwise no new value will be generated. If there is no auto_increment option then perform a lookup with the supplied primary key to see if a duplicate record already exists. If it does then generate an error message and terminate the insert.

If the table contains any candidate (unique) keys then perform a lookup on each one to check for duplicates. If any is found then generate an error message and terminate the insert.

It will then construct and execute a query in the following format:

INSERT INTO $tablename SET field1='value1', field2='value2',...

If any field in the primary key has the auto_increment option set then obtain the value which has just been generated and insert it into the output array.

If the audit flag is turned on for this table it will also write the details out to the audit database.

$rowdata = $this->_dml_ReadBeforeUpdate ($where, $reuse_previous_select)
$where is a string in the format of the WHERE clause of an SQL query.
$reuse_previous_select is boolean, taken from $this->reuse_previous_select.
$rowdata will contain a two-level array of retrieved occurrences - the first is indexed by row number, and each row will contain an associative array of name=value pairs.

If $reuse_previous_select is FALSE (the default setting) this will construct and execute a query in the following format:

SELECT * FROM $tablename WHERE $where FOR UPDATE

If $reuse_previous_select is TRUE this will construct and execute a query using the current contents of $this->sql_select, $this->sql_from, et cetera. This is for those cases where the current SELECT statement contains a field from a JOINed table, and this field must be included in the re-read so that it is available in the $originaldata arguments of the _cm_commonValidation() and _cm_validateUpdate() methods.

This read is done for the following reasons:

In theory the contents of $where should identify the primary key of an existing occurrence, therefore $rowdata should contain no more than one record. If $fieldarray contains a field called 'rdcversion' (Radicore Version Number) then this will be included in the $where string. This special field (an INTEGER with AUTO-UPDATE set) will be incremented with each update, so can be used to prevent simultaneous updates of the same record. Please refer to FAQ 70 for more details.

$rowdata = $this->_dml_updateRecord ($updatearray, $originaldata)
$updatearray contains an associative array of updated details for a single database occurrence.
$originaldata contains an associative array of original details for the same database occurrence, as provided by _dml_ReadBeforeUpdate().
$rowdata will contain the data after it has been updated.

This will remove any field from the array which does not exist in the table, such as the SUBMIT button which exists in the $_POST array created by the web server.

If any field has the autoupdate option set it will insert a value according to the data type:

This will use the contents of $updatearray and the list of candidate keys in the $this->fieldspec array to perform a lookup to check for duplicates. If any is found then it will generate an error message and terminate the update.

This will use the contents of $updatearray and the list of primary keys in the $this->fieldspec array to build a WHERE clause, then construct and execute a query in the following format:

UPDATE $tablename SET field1='value1', field2='value2',... WHERE $where
$count = $this->_dml_updateSelection ($selection, $replace)
$selection is a string in the format of a WHERE clause in an SQL query, and may identify multiple database occurrences.
$replace is a string in the format of a SET clause in an sql UPDATE statement.
$count will identify the number of records which were processed.

This will construct and execute a query in the following format:

UPDATE $tablename SET $replace WHERE $selection
$errors = $this->_examineWorkflow ($input)
$input may be an associative array containing the details of a single database occurrence, or a WHERE string which will be converted into an associative array.
$errors will contain an array of error messages, and will be empty if no errors are detected.

This is performed whenever a task (user transaction) completes successfully in order to find out one of the following:

For further details please consult An activity based Workflow Engine for PHP.

$fieldarray = $this->_getInitialValues ()
$fieldarray is an associative array of name=value pairs.

This function will look for records on the INITIAL_VALUE_USER table using the current task and user identity. If none are found it will look on the INITIAL_VALUE_ROLE table using the current task and role identity. These records provide initial values for fields within the current task.

If any are found the details are added to $fieldarray. This is loaded into $this->initial_values during the initialise() method, and how it is handled after that depends on the behaviour of the current task:

$where = $this->_getInitialWhere ($where)
$where (IN) is a string in the form of a WHERE clause in an SQL query.
$where (OUT) is the input string after it has been amended.

This will turn the $where string into an array, merge it with the contents of $this->initial_values (without overwriting any non-null values), then convert the result back into a string.

void $this->_examineWorkflowInstance ($where)
$where is a string in the format of the WHERE clause in an SQL query, and should contain the primary key of a single database occurrence.

This is performed at the start of a task (user transaction) to find out if it is a workitem within a workflow instance. If it is it will perform the following:

For further details please consult An activity based Workflow Engine for PHP.

$rows = $this->_processInstruction ($rows)
$rowdata (IN) is an indexed array containing the data for a number of database occurrences.
$rowdata (OUT) is the same array after any modifications have been made.

This looks for any instructions set by the previous task and adjusts the current set of data accordingly. At present the only instructions available are:

  1. The setting of the SELECT checkbox in a LIST or TREE VIEW screen.
  2. The expanding of a node in a TREE VIEW screen.

For examples of how to set values please refer to $this->instruction.

$where = $this->_sqlAssembleWhere ($where, $where_array)
$where (IN) is a string in the format of the WHERE clause in an SQL query.
$where_array is the same as $where (IN) but in the form of an associative array.
$where (OUT) will be a string containing any adjustments.

This will also use the following class variables:

This will look at the various components that will be used in the construction of the sql SELECT statement to see if they need adjustment, as follows:

$where = $this->_sqlAssembleWhereLink ($where, $where_array)
$where (IN) is a string in the format of the WHERE clause in an SQL query.
$where_array is the same as $where (IN) but in the form of an associative array.
$where (OUT) will be a string containing any adjustments.

In a LINK1 transaction three table names are provided:

  1. OUTER - from which a single occurrence will be read using the primary key passed down from the previous transaction.
  2. INNER - from which all occurrences will be read.
  3. LINK - which lies between the OUTER and INNER tables.

This information will be used to construct an sql SELECT statement similar to the following:

SELECT outer.outer_id, inner.inner_id, inner_desc, 
       CASE WHEN link.inner_id IS NULL THEN 'F' ELSE 'T' END AS selected
FROM outer 
CROSS JOIN inner 
LEFT JOIN link ON (outer.outer_id=link.outer_id AND inner.inner_id=link.inner_id)
WHERE outer.outer_id='x' 
[AND link.inner_id IS NULL/IS NOT NULL]
$sql_from = $this->_sqlForeignJoin (&$sql_select, $sql_from, $parent_relations)
$sql_select (IN) is a list of field names in the same format as the SELECT clause in an sql query.
$sql_from (IN) is a list of table names in the same format as the FROM clause in an sql query.
$parent_relations is an array from $parent_relations.
$sql_select (OUT) will contain any amendments.
$sql_from (OUT) will contain any amendments.

This will use the information contained in $parent_relations to amend the contents of $sql_select and $sql_from. The processing sequence is as follows:

For more details please refer to Using Parent Relations to construct sql JOINs.

$sql_from = $this->_sqlProcessJoin (&$sql_select, $sql_from, $parent_relations, &$new_relations)
$sql_select (IN) is a list of field names in the same format as the SELECT clause in an sql query.
$sql_from (IN) is a list of table names in the same format as the FROM clause in an sql query.
$parent_relations is an array of relationship details from $parent_relations.
$new_relations (IN) is an array which gives details of every table which has text available from an alternative language table.
$sql_select (OUT) will contain any amendments.
$sql_from (OUT) will contain any amendments.
$new_relations (OUT) will contain any details of text which is available from alternative language tables.

This will use the contents of $parent_relations to construct JOIN statements to retrieve data from any parent table(s).

If variable $this->sql_no_foreign_db is set to TRUE then any table which belongs in a different database will be excluded.

This will amend both the SELECT and FROM strings to produce an sql SELECT statement similar to the following:

SELECT child.*, parent.parent_field
FROM child
LEFT JOIN "dbparent".parent ON (parent.parent_id = child.parent_id)
WHERE child.child_id='x'

If any parent table has any text which resides on an alternative language table the details will be added to $new_relations.

For more details please refer to Using Parent Relations to construct sql JOINs.

$sql_select = $this->_sqlSelectAlternateLanguage ($sql_select, $new_relations)
$sql_select (IN) is a list of field names in the same format as the SELECT clause in an sql query.
$new_relations is an array which gives details of every table which has text available from an alternative language table.
$sql_select (OUT) will contain any amendments.

This will use the contents of $new_relations to amend the contents of the SELECT string to produce an sql SELECT statement similar to the following:

SELECT child.field1, child.field2, 
       COALESCE((SELECT fieldname 
                 FROM alt_language_table 
                 WHERE alt_language_table.foreign_key=base_table.primary_key 
                   AND alt_language_table.language_id='??')
                , base_table.fieldname) AS fieldname 
FROM child
LEFT JOIN parent ON (parent.parent_id = child.parent_id)
WHERE child.child_id='x'

For more details please refer to Internationalisation and the Radicore Development Infrastructure (Part 2).

$rowdata = $this->_validateInsert ($rowdata)
$rowdata (IN) is an associative array which contains the data to be validated.
$rowdata (OUT) will contain the same data which may have been changed during the validation process.

This will compare the field specifications in the $this->fieldspec array with the contents of $rowdata to verify that each value conforms to the specifications. The checks it performs are as follows:

$this->errors will contain any error messages.

All fields will be checked, which may mean that multiple error messages will be generated. When the screen is redisplayed any error message will be displayed immediately under/adjacent the field to which it relates. If the field name does not exist on the current screen then the error message will appear in the general message area at the bottom of the screen.

$rowdata = $this->_validateUpdate ($rowdata)
$rowdata (IN) is an associative array which contains the data to be validated.
$rowdata (OUT) will contain the same data which may have been changed during the validation process.

This is similar to _validateInsert() but will only examine those fields actually supplied in $rowdata.


Notes on Customisable Methods

These methods are not defined in any generated table class by default. They are defined in the abstract table class as empty methods with a prefix of _cm_. Each of these methods is called at a predetermined point in the processing sequence, but as the default method does not contain any code then nothing will happen. To insert your custom code you first have to copy the empty method from the abstract table class to your database table class, then you can add your custom code to alter the outcome of any task which uses that class and that method.

$fieldarray = $this->_cm_changeConfig ($where, $fieldarray)
$where is a string in the format of the WHERE clause in an SQL query.
$fieldarray (IN) is the same data, but converted to an associative array.
$fieldarray (OUT) will be the input array, plus any changes.

This is to make changes to the $this->fieldspec array which governs how each field will be displayed on the user interface. It is possible to add new fields as well as change any existing fields.

Here is an example of adding a new field called selected:

$this->fieldspec['selected'] = array('type' => 'boolean',
                                     'true' => 'Y',
                                     'false' => 'N');

If a field that is added in this manner is accidentally treated as if it belonged in that table, such as being used as a valid sort item, it can be identified as a non-database field by adding in the specification 'nondb' => 'y'.

Here is how to make a field read-only or invisible:

$this->fieldspec['field1']['noedit'] = 'y';    // make read-only
$this->fieldspec['field2']['nodisplay'] = 'y'; // do not display

Note that where an option like the above is defined with a 'y' value it cannot be reversed simply by changing the value to 'n' - it must be completely removed, as in the following:

unset($this->fieldspec['field1']['noedit']);
unset($this->fieldspec['field2']['nodisplay']);

By default whenever a field with subtype=image is displayed it will show both the image and the file name. If the text is not to be displayed it can be removed with the following:

$this->fieldspec['image_field']['notext'] = 'y';

Modifications to the $this->fieldspec array may depend on certain conditions, as shown in the following example:

if ($GLOBALS['mode'] == 'search') {
    $prev_script = getPreviousScript();
    if (basename($prev_script) == 'foobar.php') {
        ... do stuff ...
    } // if
} // if

Note that any changes made here will be reversed before any database INSERT or UPDATE is performed. This is so that any data validation is performed against the proper specifications.

$rowdata = $this->_cm_commonValidation ($rowdata, $originaldata)
$rowdata (IN) is an associative array containing the contents of a single database occurrence.
$originaldata for an UPDATE is the original data as it currently exists on the database, while for an INSERT it is the same as $fieldarray (IN).
$rowdata (OUT) will contain the input data, plus any changes.

This is used to specify secondary validation that is to be performed for both insertRecord() and updateRecord() operations. To generate an error message for a particular field simply add the message to the $errors array using the field name as the key, as in the following:

    $this->errors['fieldname'] = 'error message';
    $this->errors['fieldname'] = getLanguageText('id');

Click on the following hyperlink for a description of getLanguageText().

Here is some sample code:

    if ($rowdata['start_date'] > $rowdata['end_date']) {
        // 'Start Date cannot be later than End Date'
        $this->errors['start_date'] = getLanguageText('e0001');
        // 'End Date cannot be earlier than Start Date'
        $this->errors['end_date']   = getLanguageText('e0002');
    } // if
    
    if (!empty($rowdata['postcode'])) {
        $pattern = "^[A-Z]{1,2}[0-9]{1,2}[A-Z]{0,1} [0-9]{1}[A-Z]{2}$";
        if (!ereg($pattern, $rowdata['postcode'])) {
            // 'Invalid format for a postcode.'
            $this->errors['postcode'] = getLanguageText('e0020');
        } // if
    } // if
    
    return $rowdata;

It should be noted that all fields of type date and number will already have been validated and reformatted by either _validateInsert() or _validateUpdate(), so can be used without the need for any further action.

If it is necessary to perform validation using a value obtained from another database table you may use code similar to the following:

    require_once 'classes/other_table.class.inc';
    $other_table = new other_table;                            // method #1
    $other_table =& RDCsingleton::getInstance('other_table');  // method #2
    $other_table->sql_select = 'field1';
    $other_table_data = $other_table->getData("key_field='whatever'");
    if ($other_table->numrows < 1) {
        // 'Cannot locate entry on OTHER_TABLE'
        $this->errors['fieldname'] = getLanguageText('e0321');
    } else {
        $field1 = $other_table_data[0]['field1'];
        if ($rowdata['fieldname'] != $field1) {
            // 'there is something wrong here....'
            $this->errors['fieldname'] = getLanguageText('e0123');
        } // if
    } // if

Note that there are two methods for creating an instance of the 'other_table' class:

Also note that the getData() result may contain any number of rows, which means that the first level is indexed by row number, not the field name.

If you have an error message which is not related to a particular field then make it indexed instead of associative, as in the following example:

    $this->errors[] = getLanguageText('e0099');

Note that it is also possible to change any value in $rowdata before it is written to the database.

$fieldarray = $object->_cm_customButton ($fieldarray, $button)
$fieldarray (IN) is an associative array containing the details of a single database occurrence.
$button is the name of the button field which was pressed.
$fieldarray (OUT) will be the same array, but after any changes have been made.

This is called whenever a custom button in the form is pressed by the user. See FAQ137 for details.

In the following example the action taken is to activate another form, but any action is possible.

function _cm_customButton ($fieldarray, $button)
// user pressed a custom buttom.
{
    if ($button == 'whatever')) {
        ..... do something .....
    } // if

    return $fieldarray;

} // _cm_customButton

Note that $fieldarray contains a single database row. If the current object contains more than one row you can access them using $this->fieldarray instead.

$msg = $this->_cm_deleteSelection ($selection)
$selection is a string in the format of a WHERE clause of an SQL query.
$msg will contain an informative message.

This is used to perform an operation on those records which satisfy the selection criteria provided in $selection. The reason that this is a customisable method is because the operation may involve the deletion of database records, or it may involve the nullifying of pointers to records.

Here is an example of the default code which deletes the selected records:

    // delete selected records.
    $count = $this->_dml_deleteSelection($selection);

    // $count rows were deleted
    return getLanguageText('sys0004', $count, strtoupper($this->tablename));

This will result in the following sql statement being issued:

DELETE FROM $tablename WHERE $selection

Click on the following hyperlink for a description of getLanguageText().

Here is an example of an update using _dml_updateSelection():

    // $where must contain at least one occurrence of 'node_id='
    if (substr_count($selection, 'node_id=') < 1) {
        // 'Nothing has been selected yet'
        return getlanguageText('sys0081');
    } // if

    // delete relationships by setting NODE_ID_SNR to NULL on selected records.
    $count = $this->_dml_updateSelection($selection, 'node_id_snr=NULL');

    // "$count rows were updated"
    return getLanguageText('sys0006', $count, $this->tablename);

This will result in the following sql statement being issued:

UPDATE $tablename SET node_id_snr=NULL WHERE $selection

Here is an example of a customised delete using _dml_deleteSelection():

    // $where must contain at least one occurrence of 'node_id='
    if (substr_count($selection, 'node_id=') < 1) {
        // 'Nothing has been selected yet'
        return getlanguageText('sys0081');
    } // if

    // delete all selected records.
    $count = $this->_dml_deleteSelection($selection);

    // "$count rows were deleted"
    return getLanguageText('sys0004', $count, $this->tablename);

Here is an example of a multi-table delete:

    $from = 'ssn, trn, tbl, fld';
    $using = 'audit_ssn AS ssn'
           .' LEFT JOIN audit_trn AS trn ON (trn.session_id=ssn.session_id)'
           .' LEFT JOIN audit_tbl AS tbl ON (tbl.session_id=trn.session_id'
                                      .' AND tbl.tran_seq_no=trn.tran_seq_no)'
           .' LEFT JOIN audit_fld AS fld ON (fld.session_id=tbl.session_id'
                                      .' AND fld.tran_seq_no=tbl.tran_seq_no'
                                      .' AND fld.table_seq_no=tbl.table_seq_no)';
    $count = $this->_dml_deleteSelection($selection, $from, $using);

This will result in an SQL query similar to the following:

DELETE FROM ssn, trn, tbl, fld 
USING audit_ssn AS ssn
LEFT JOIN audit_trn AS trn ON (trn.session_id=ssn.session_id)
LEFT JOIN audit_tbl AS tbl ON (tbl.session_id=trn.session_id 
                           AND tbl.tran_seq_no=trn.tran_seq_no)
LEFT JOIN audit_fld AS fld ON (fld.session_id=tbl.session_id 
                            AND fld.tran_seq_no=tbl.tran_seq_no
                            AND fld.table_seq_no=tbl.table_seq_no)
WHERE ssn.ssn_date < '2010-01-01'
$file_name = $this->_cm_filePickerSelect ($file_name)
$file_name (IN) is the name of the selected file.
$file_name (OUT) is the name of the selected file.

This is used to deal with the file that has been selected in a filepicker screen. By default the file name will be passed back to the previous task so that it can be processed there. This behaviour can be altered by inserting code as in the following example, which passes the selection to a different task using the $this->scriptNext() method:

function _cm_filePickerSelect ($selection)
// Deal with selection from a filepicker screen.
{
    $this->scriptNext('batch_log(filedownload)', "file_id='$selection'");

    return $selection;

} // _cm_filePickerSelect
$output_name = $this->_cm_fileUpload ($input_name, $temp_file, $wherearray)
$input_name is the name of the file being uploaded from the client.
$temp_file is a copy of the file in the temporary directory.
$wherearray is the original $where string from initialiseFileUpload() converted to an array.
$output_name is the name to be used when the file is written to the server.

This is used to deal with the file being uploaded from the client before it is written to the server, for example changing it's name into something more meaningful. By default $output_name will be the same as $input_name.

A temporary copy of the file is available in $temp_file so that its contents may be checked, such as verifying that an image file has the correct dimensions.

If any messages are placed in $this->errors the upload will be abandoned.

$output_array = $this->_cm_filterWhere ()
$output_array is an array of field names.

This is used to provide a list of field names which should not be filtered from the $where string during the processing of the initialise() method. By default any field name found in the $where string which does not exist in $this->fieldspec will be filtered out, but fields can be excluded from this filtering process by adding them to the output array in this custom method.

Here is some sample code:

    function _cm_filterWhere ($array=null)
    // identify field names which are NOT to be filtered out of a $where string.
    {
        $array[] = 'supplier_party_id';

        return $array;

    } // _cm_filterWhere

Although the field supplier_party_id does not exist in the current database table the above code will prevent it from being filtered out of the $where string. Note that this field should exist on another database table which is accessed by a JOIN otherwise it will cause the SQL query to fail.

$rowdata = $this->_cm_formatData ($rowdata, &$css_array)
$rowdata (IN) is an associative array containing the data from a single database occurrence.
$rowdata (OUT) will contain the input data, plus any changes.
$css_array (IN) is an empty array.
$css_array (OUT) is an associative array which can contain the name of a CSS class for a field within the current row.

This is used when data read from the database needs to be formatted before it is displayed to the user, and where that formatting cannot be achieved in formatData(). It is the opposite of _cm_unFormatData().

Here is some sample code which demonstrates formatting:

    if (!isset($rowdata['person_name'])) {
        // merge first_name and last_name into person_name
        if (isset($rowdata['first_name']) AND isset($rowdata['last_name'])) {
            $rowdata['person_name'] = $rowdata['first_name']
                                    . ' '
                                    . $rowdata['last_name'];
        } // if
    } // if

    return $rowdata;

Here is some sample code which demonstrates the inclusion of a CSS class around individual fields:

    if ($fieldarray['count'] <= 50) {
        $css_array['count'] = 'whatever';
    } // if

    return $fieldarray;

This means that in any row where the value of field count is less than or equal to 50 then that value in the HTML output will be enclosed in a <DIV> with the specified class name, as in the following:

    <td><div class="whatever">49</div></td>

Of course the condition can be whatever you want, and the CSS class name can be whatever you want. It is possible to specify multiple class names if there is a space as a separator between each name, as in "class1 class2". The CSS class(es) should be specified in the style_custom.css file which belongs to that subsystem so that it does not conflict with any custom CSS styles for other subsystems.

$fieldarray = $this->_cm_getColumnNames ($fieldarray)
$fieldarray (IN) is the data produced by the standard code.
$fieldarray (OUT) is the data after any customisation.

$fieldarray will contain the list of fields that can be selected and their default value of 'Y' (selected) or 'N' (not selected). This method can be used to remove columns from the list, or to change the default values.

Here is some sample code:

    function _cm_getColumnNames ($fieldarray)
    // modify data to be used by 'std.output4.inc'.
    {
        unset($fieldarray['column_name1']);  // remove this column
        $fieldarray['column_name2'] = 'N';   // set initial value of selected to 'N'

        return $fieldarray;

    } // _cm_getColumnNames
$lock_array = $this->_cm_getDatabaseLock ()
$lock_array will be an array of table names, and can be a mixture of indexed and associative.

This is used to identify which database tables need to be locked for the duration of the database transaction which is about to start.

    $GLOBALS['lock_tables'] = TRUE;     // TRUE/FALSE
    $GLOBALS['lock_rows']   = FALSE;    // FALSE, SR (share), EX (exclusive)

    // the format of each $lock_array entry is one of the following:
    // $lock_array[] = 'tablename'         (within current database)
    // $lock_array[] = 'dbname.tablename'  (within another database)
    // $lock_array['READ'][] = '...'       (for a READ lock)
    switch ($GLOBALS['mode']){
        case 'insert':
            $lock_array[] = $this->tablename;
            break;
        case 'update':
            $lock_array[] = $this->tablename;
            $lock_array[] = 'x_tree_level AS t2';
            $lock_array[] = 'x_tree_node';
            break;
        case 'delete':
            $lock_array[] = $this->tablename;
            break;
        default:
            $lock_array = array();
    } // switch

    return $lock_array;

If $GLOBALS['lock_tables'] is FALSE then no tables are locked during this transaction.

If $GLOBALS['lock_tables'] is TRUE but $lock_array is empty then it will default to the following:

    $lock_array['WRITE'][] = $this->tablename;

Note that entries for $lock_array can be in various different formats:

    $lock_array[] = $this->tablename;
    $lock_array[] = 'x_tree_node';
    $lock_array[] = 'dbname.tablename';
    $lock_array[] = 'x_tree_level AS t2';
    $lock_array['READ'][]  = 'foo';
    $lock_array['WRITE'][] = 'bar';

Note that if a lock mode of READ or WRITE is not specified then it will default to WRITE, so it is only necessary to specify the mode when it is something other than the default.

If any locks are specified it will also be necessary to append some system tables to $lock_array, for example those in the AUDIT and WORKFLOW databases. This will be done automatically by the framework.

The end result will cause a statement similar to the following to be constructed and issued:

$this->query = "LOCK TABLES table1 mode, table2 mode, ...";
$fieldarray = $this->_cm_getExtraData ($where, $fieldarray)
$where is a string in the same format as the WHERE clause in SQL query.
$fieldarray (IN) is the same data, but converted to an associative array.
$fieldarray (OUT) is the same as the input array, plus any changes.

This is used to retrieve items of data other than that which can be retrieved from the current table, such as the contents of lookup lists to be used in dropdown menus or radio groups. Note that it is also called in transaction patterns of type ADD1/2 even when no data is read from the current table.

Here is some sample code which retrieves extra data from within itself:

    $array = $this->getValRep('arc_type');
    $this->lookup_data['arc_type'] = $array;

Click on the following hyperlinks for information regarding getValRep() and $this->lookup_data.

Here is some sample code which retrieves extra data from within a different object:

    if (!empty($fieldarray['workflow_id'])) {
        $workflow_id = $fieldarray['workflow_id'];

        // get contents of foreign table WF_TRANSITION and add as a LOOKUP list
        require_once 'classes/wf_transition.class.inc';
        $dbobject =& RDCsingleton::getInstance('wf_transition');
        $array = $dbobject->getValRep('transition_id', "workflow_id='$workflow_id'");
        $this->lookup_data['transition_id'] = $array;
    } // if
$rowdata = $this->_cm_getForeignData ($rowdata)
$rowdata (IN) is an associative array containing the data from a single database occurrence.
$rowdata (OUT) is the same array, plus any changes.

This will allow a SELECT operation on a foreign table to be performed so that one (or more) fields from that table can be incorporated into the current data array. This method need only be used if the standard code in getForeignData() is not sufficient.

Here is an example of some code:

    if (!empty($rowdata['prod_cat_id']) and empty($rowdata['prod_cat_desc'])) {
        // get description for selected entry
        require_once 'classes/product_category.class.inc';
        $dbobject =& RDCsingleton::getInstance('product_category');
        $dbobject->sql_select = 'prod_cat_desc';
        $foreign_data = $dbobject->getData("prod_cat_id='{$rowdata['prod_cat_id']}'");
        // merge with existing data
        $rowdata = array_merge($rowdata, $foreign_data[0]);
    } // if
$rowdata = $this->_cm_getInitialData ($rowdata)
$rowdata (IN) is an associative array containing the contents of the original $where string.
$rowdata (OUT) is the input array, plus any changes. This may be an associative array containing the details for a single database occurrence, or it may be converted into an indexed array for multiple database occurrences.

This is used to obtain any initial values before an INPUT screen is displayed to the user. This means that the input array will usually be empty, or will contain a foreign key passed down from the previous screen.

Here is some sample code:

    $count = $this->getCount("SELECT max(node_id) FROM $this->tablename");
    $rowdata['node_id'] = $count + 1;

    $rowdata['case_status'] = 'OP';
    $rowdata['start_date']  = date('Y-m-d H:i:s');
    
    // set these fields to 'noedit' (read only)
    $this->fieldspec['tree_level_id']['noedit']  = 'y';
    $this->fieldspec['tree_level_seq']['noedit'] = 'y';

    return $rowdata;
$rowdata = $this->_cm_getInitialDataMultiple ($rowdata)
$rowdata (IN) is an array containing the contents of the original $where string. As this may contain the details of multiple selections it will be a multi-dimensional array, first indexed by row number, then each row will be an associative array of name=value pairs.
$rowdata (OUT) is the input array, plus any changes. This may be an associative array containing the details for a single database occurrence, or it may be converted into an indexed array for multiple database occurrences.

This is used to construct one or more database occurrences that will be processed later by a call to insertMultiple().

$array = $this->_cm_getNodeData ($expanded, $where, $where_array)
$expanded is an indexed array which identifies the nodes which are to be expanded, or a string containing 'ALL'.
$where is a string which is used as selection criteria.
$where_array is the same as $where, but as an associative array
$array will be an indexed array of different database occurrences, and each occurrence will be an associative array of field names and values.

This is used for obtaining nodes from a hierarchical tree structure. The contents of $where will provide the initial selection criteria (such as tree_type='ORG'). If a retrieved node is identified in $expanded then all children of this node will also be retrieved and added to the output array. This is done by performing the operation recursively changing $where to identify the parent node (such as node_id_snr='27').

Unlike most other customisable classes the version in the abstract class contains default processing which is fully functional. It may be customised should the desired processing be different. Examples can be found in:

$keys = $this->_cm_getNodeKeys ($keys)
$keys (IN) is an associative array which identifies the key fields to access records in the hierarchy.
$keys (OUT) is the same array after being amended.

This is used to identify the key fields for the senior/parent and junior/child records in the hierarchy so that it can locate any child records for the current parent. You will need to supply your own values for each of the following keys:

    $keys['snr_id'] = 'snr_id';
    $keys['jnr_id'] = 'jnr_id';
$string = $this->_cm_getOrderBy ($string)
$string (IN) contains the current column name(s) used in an ORDER BY clause.
$string (OUT) is the amended string.

This is used when the column name on the screen has to be adjusted before it can be used in the ORDER BY clause of an SQL query. If it is not adjusted back to the original screen name then the ascending/descending icon cannot be attached to it.

In this example the screen name orig_user_id is changed to originator.user_id just before the SQL query is built:

    function _cm_pre_getData ($where, $where_array, $fieldarray=null)
    // perform custom processing before database record(s) are retrieved.
    {
        if (!empty($this->sql_orderby)) {
            $this->sql_orderby = str_replace('request.orig_user_id', 'originator.user_id', $this->sql_orderby);
        } // if

        return $where;

    } // _cm_pre_getData

This needs to be reversed so that when the HTML screen is built the ascending/descending icon can be attached to the orig_user_id field as originator.user_id does not exist on the screen.

    function _cm_getOrderBy ($orderby)
    // Adjust name of orderby item before it is added to XML output.
    {
        $orderby = str_replace('originator.user_id', 'orig_user_id', $orderby);

        return $orderby;

    } // _cm_getOrderBy
$pkey = $this->_cm_getPkeyNames ($pkey, $task_id, $pattern_id)
$pkey (IN) is an indexed array containing the primary key field(s) for the current table.
$task_id is the identity of the next task.
$pattern_id is the pattern identity of the next task.
$pkey (OUT) is the amended array.

Whenever a selection is made, either to to be passed forward to a new task by pressing a navigation button, or back to the previous task by pressing the CHOOSE button within a POPUP form, details are extracted from the selected records and placed into the selection string, which is in the form of the WHERE clause in an sql SELECT statement. By default the only fields which are used in this process are those which have been identified as being part of the primary key for that table.

In some cases it may be useful to construct the selection string using fields which are not part of the primary key, so this method can be used to modify the array of field names before the selection string is constructed. Values for $task_id and $pattern_id are made available in case they are needed to decide which modifications to the primary key are necessary.

    function _cm_getPkeyNames ($pkey_array, $task_id, $pattern_id)
    // perform custom processing before the selection string is passed to another form.
    {
        $pkey_array[] = 'whatever';       // append to array
        $pkey_array = array('whatever');  // replace array

        return $pkey_array;

    } // _cm_getPkeyNames

Note that if you use the contents of either $task_id or $pattern_id in a condition then you should add is_null to the condition, as shown in the following:

    function _cm_getPkeyNames ($pkey_array, $task_id, $pattern_id)
    // perform custom processing before the selection string is passed to another form.
    {
        if (is_null($pattern_id) OR preg_match('/^ADD/i', $pattern_id)) {
            $pkey_array[] = 'whatever';  // append to array
        } // if

        return $pkey_array;

    } // _cm_getPkeyNames

This is because in certain circumstances all non-primary key fields are removed from $this->fieldarray when the object is being serialized for storage in the session array. Refer to the __sleep() method inside file std.table.class.inc. The list of fields which constitute the primary key can be adjusted with this method.

Note that this modification to the list of primary key fields is purely temporary, and will have no impact on any subsequent database operations.

See also: _cm_getWhere() and Appendix I: Passing context between objects.

$output = $this->_cm_getWhere ($input, $task_id, $pattern_id)
$input is the unmodified $where string currently being used within the object.
$task_id is the identity of the called task.
$pattern_id is the pattern identity of the called task.
$output is the string that is returned after any modifications have been made.

This allows the $where string to be modified before it is passed to a child task when a navigation button is pressed.

See also: Appendix I: Passing context between objects.

$array = $this->_cm_getValRep ($item, $where=null)
$item contains the name of the list which is to be returned (there may be more than one).
$where is an optional string containing selection criteria.

This is used to return an associative array of values which can be used in a dropdown list or radio group.

Here is an example which uses hard-coded values:

    if ($item == 'direction') {
        $array['IN']  = 'In (P -> T)';
        $array['OUT'] = 'Out (P <- T)';
        return $array;
    } // if

Here is an example which returns values in the user's language by using getLanguageArray():

    if ($item == 'arc_type') {
        $array = getLanguageArray('arc_type');
        return $array;
    } // if

Here is an example which loads the contents of the current database table into an array which is then passed back to the calling form:

    if ($item == 'tree_level_id') {
        // get data from the database
        $this->sql_select      = 'tree_level_id, tree_level_desc';
        $this->sql_orderby     = 'tree_level_seq';
        $this->sql_orderby_seq = 'asc';
        $data = $this->getData($where);

        // convert each row into 'id=desc' in the output array
        foreach ($data as $row => $rowdata) {
            $rowvalues = array_values($rowdata);
            $array[$rowvalues[0]] = $rowvalues[1];
        } // foreach

        return $array;

    } // if

An example of the code in the calling form which obtains this data is described in _cm_getExtraData().

$where = $this->_cm_initialise ($where, &$selection, $search)
$where (IN) is a string in the format of the WHERE clause in an SQL query.
$selection is an optional string similar to $where
$search is an optional string similar to $where
$where (OUT) is the same string, plus any changes.

This is to allow any initialisation to be performed when the table object is first created.

For details regarding the $where, $selection and $search arguments please refer to the initialise() method.

Here is an example which ensures that the contents of $where, if empty, is loaded with a usable value:

    if (empty($where)) {
        $where = "user_id='" .$_SESSION['logon_user_id'] ."'";
    } // if

    return $where;

Here is an example where initial values are defined for a SEARCH screen:

    if ($GLOBALS['mode'] == 'search') {
        // set initial values in screen
        $where = "user_id='" .$_SESSION['logon_user_id'] ."' AND "
                ."date_from='" .getTimeStamp('date') ."'";
    } // if
    
    return $where;
void $object->_cm_initialiseFileDownload ($fieldarray)
$fieldarray is the array of values from the selected database record.

This is used in a file download transaction to specify values for the following:

Here is some sample code:

    function _cm_initialiseFileDownload ($fieldarray)
    // perform any initialisation for the file download operation.
    {
        // 'picture' field contains name of file to be downloaded
        $this->download_filename = $fieldarray['picture'];
        //$this->download_mode     = 'inline';  // disable option to save

        return;

    } // _cm_initialiseFileDownload
$fieldarray = $this->_cm_initialiseFilePicker ($fieldarray)
$fieldarray is the $where string from the calling task after it has been converted into an associative array.

This is used in a file picker transaction to change the default values for any of the following:

Here is some sample code:

    function _cm_initialiseFilePicker ($fieldarray)
    // perform any initialisation before displaying the File Picker screen.
    {
        // identify the subdirectory which contains the files
        $this->picker_subdir    = 'pictures';

        // identify the file types that may be picked
        $this->picker_filetypes = array();  // default is ANY file extension
        $this->picker_filetypes = array('bmp', 'jpg', 'png', 'gif');

        // specify the dimensions of the displayed image
        $this->image_height = 60;
        $this->image_width  = 60; 

        return $fieldarray;
        
    } // _cm_initialiseFilePicker
$fieldarray = $this->_cm_initialiseFileUpload ($fieldarray)
$fieldarray is the $where string from the calling task after it has been converted into an associative array.

This is used in a file upload transaction to change the default values for any of the following:

Here is some sample code:

    function _cm_initialiseFileUpload ($fieldarray)
    // perform any initialisation before displaying the File Upload screen.
    {
        $this->upload_subdir      = 'pictures';
either: $this->upload_filetypes   = array('image/x-png', 'image/gif');
or:     $this->upload_filetypes   = 'image';
or:     $this->upload_filetypes   = '*';
        $this->upload_maxfilesize = 100000;

        return $fieldarray;

    } // _cm_initialiseFileUpload

If any messages are placed in $this->errors the upload will be abandoned.

It is also possible to get the upload process to automatically create copies of an image file with different dimensions by populating the object's resize_array using code similar to the following:

        $this->resize_array[1]['directory'] = 'thumbs';
        $this->resize_array[1]['width']     = 60;
        $this->resize_array[1]['height']    = 60;

        $this->resize_array[2]['directory'] = 'large';
        $this->resize_array[2]['width']     = 400;
        $this->resize_array[2]['height']    = 400;

This indexed array can contain any number of entries.

$fieldarray = $object->_cm_ListView_header ($fieldarray)
$fieldarray (IN) is the data for the current record.
$fieldarray (OUT) is the same array after any amendments.

This method is called by listView() just before the titles are printed at the top of each page. It provides the opportunity to define fields which can be referenced with the '%%' prefix in the report structure file using code similar to the following:

function _cm_ListView_header ($fieldarray)
// insert data into $fieldarray before title is printed in List View.
{
    $fieldarray['foobar'] = 'This is a custom title';

    return $fieldarray;

} // _cm_ListView_header

Wherever the title array in the report structure file contains an element with 'text'=>'%%foobar' then the contents of $fieldarray['foobar'] will be substituted.

$array = $object->_cm_ListView_pre_print ($prev_row, $curr_row)
This method is deprecated - use _cm_ListView_print_before() and _cm_ListView_print_after() instead.
$output = $object->_cm_ListView_print_after ($curr_row, $next_row)
$curr_row is an associative array containing the contents of the current record.
$next_row is an associative array containing the contents of the next record.
$output is an indexed array containing any extra lines that need to be printed.

This is used in List view (with line breaks) to print any extra lines after the current database record.

Here is an example:

    function _cm_ListView_print_after ($curr_row, $next_row)
    // allow extra rows to be created in List View
    {
        $output = array();

       	$output[0]['pers_type_desc'] = 'after ' .$curr_row['person_id'];

        return $output;

    } // _cm_ListView_print_after
$output = $object->_cm_ListView_print_before ($prev_row, $curr_row)
$prev_row is an associative array containing the contents of the previous record.
$curr_row is an associative array containing the contents of the current record.
$output is an indexed array containing any extra lines that need to be printed.

This is used in List view (with line breaks) to print any extra lines before the current database record. For the first database record $prev_row will be empty.

Here is an example:

    function _cm_ListView_print_before ($prev_row, $curr_row)
    // allow extra rows to be created in List View
    {
        $output = array();

        if (!empty($prev_row)) {
            // row has changed
            if (substr($prev_row['last_name'], 0, 1) != substr($curr_row['last_name'], 0, 1)) {
                $this->pdf->AddPage();
            } else {
                $lines_left = $this->pdf->getLinesRemaining();
                if ($lines_left < 10) {
                    $this->pdf->AddPage();
                } // if
            } // if
        } // if

        $output[0]['pers_type_desc'] = 'before ' .$curr_row['person_id'];

        return $output;

    } // _cm_ListView_print_before

This example also shows how to force a new page when a particular part of the data changes.

$array = $object->_cm_ListView_total ()
$array is the data to be printed on the last line of the report.

This method is called by listView() after the last record obtained from the database has been printed to append any totals which may have been accumulated. If an empty array is returned then nothing is printed.

Data can be accumulated in the _cm_post_fetchRow() method using code similar to the following:

function _cm_post_fetchRow ($rowdata)
// perform custom processing after a call to fetchRow().
{
    if ($GLOBALS['mode'] == 'pdf-list') {
        // accumulate totals for PDF report
        $this->count += $rowdata['count'];
    } // if

    return $rowdata;

} // _cm_post_fetchRow

If there is any data to be printed then this must be returned in an associative array using the column names which have already been specified for inclusion in the report.

function _cm_ListView_total ()
// pass back any data to be printed on last line of PDF report (list view).
{
    $array = array();

    $array['pattern_desc'] = 'Total count for all Patterns:';
    $array['count'] = $this->count;

    return $array;

} // _cm_ListView_total

Note that only a single line can be printed, and it must use the same columns as were used in the body of the report.

This is described further in Example List view (with totals).

$outarray = $object->_cm_output_multi ($name, $inarray)
$name is the name of the zone for which data is required in the format 'multiX' where 'X' is in the range 1-9.
$inarray is the array of values from the parent record currently being processed.
$outarray is an array of additional child values provided by this method.

This method is called by detailView() if any additional data is required during the production of a PDF file.

$name identifies the zone for which data is being requested. The report may have several zones which require their own data.

$inarray contains the values from the parent record as this may provide the keys for accessing child tables.

$outarray contains the data which is obtained from those other tables. This array may contain any number of database rows, and each row will produce its own set of output. An empty array will not produce any output, but if an empty line needs to be printed (or if the line contains only labels and no data) then a dummy entry should be placed in the array.

Here is some sample code:

    function _cm_output_multi ($name, $fieldarray)
    // get extra data to pass to PDF class.
    {
        $outarray = array();

        switch ($name) {
            case 'multi1':
                // return a non-empty dummy array - this is labels only
                $outarray[] = array('dummy' => '');
                break;

            case 'multi2':
                // retrieve associated data from RELATED_COLUMN table
                $object =& RDCsingleton::getInstance('dict_related_column');
                $db_id_snr  = $fieldarray['database_id_snr'];
                $tbl_id_snr = $fieldarray['table_id_snr'];
                $outarray = $object->getData("database_id_snr='$db_id_snr' AND table_id_snr='$tbl_id_snr'");
                if (empty($outarray)) {
                    // print an empty line
                    $outarray[] = array('dummy' => '');
                } // if
                break;

            default:
                break;
        } // switch

        if ($outarray) {
            return $outarray;
        } else {
            return false;
        } // if

    } // _cm_output_multi
$where = $this->_cm_popupCall (&$popupname, $where, $fieldarray, &$settings)
$popupname (IN) is the name of the popup (picklist) form which is expected to be activated.
$popupname (OUT) is the (possibly modified) name of the popup form which is just about to be activated.
$where (IN) is a string in the format of the WHERE clause in an sql query.
$where (OUT) is the input string, plus any changes.
$fieldarray is an associative array containing the data for a single database occurrence.
$settings (IN) is an associative array containing settings which will to be passed to the popup form.
$settings (OUT) will be the input array, plus any changes.

This is used to create or modify any selection criteria or settings before they are passed to a popup form for processing:

Here is some sample code:

function _cm_popupCall (&$popupname, $where, $fieldarray, &$settings)
{
    switch ($popupname) {
        // replace $where string for this popup
        case 'dict_table(popup1)':
            $where = "database_id='{$fieldarray['database_id']}'";
            break;
        default:
            $where = '';
    } // switch

    // allow only one entry to be selected (the default)
    $settings['select_one'] = true;

    // allow more than one entry to be selected
    //$settings['select_one'] = false;
    
    // allow a single result to be selected without user intervention
    //$settings['choose_single_row'] = true;

    return $where;
    
} // _cm_popupCall
Note that the variable $popupname is supplied in case there is more than one popup button in the current form. Each popup field should refer to a different popup task, so this identifies which button was pressed in case different processing needs to be be performed.

If anything is placed in $this->errors the calling of the popup form will be cancelled.

$rowdata = $this->_cm_popupReturn ($rowdata, $return_from, &$select_array)
$rowdata (IN) is an associative array containing the current data for the current database occurrence. It does not yet include the contents of $select_array.
$return_from is a string which identifies the popup screen that has just finished.
$select_array is an associative array containing the selection made in the popup form.
$rowdata (OUT) is the input array, plus any changes.

This is used to deal with the selection that has just been made in a popup or filepicker screen.

In the case of a filepicker the selected filename will have already been inserted into $rowdata before this method is called, so no further action may be necessary.

In the case of a popup form which has just returned a new value for a foreign key, the standard processing provided in getForeignData() may be sufficient, so no further action may be necessary.

Here is an example of some code which deals with the situation where the name of the primary key field from the parent (foreign) table is converted into another name for use as the foreign key in the child table:

    if ($return_from == 'mnu_task(popup1)') {
       // change field name from 'task_id' to 'popup_task_id'
       $rowdata['popup_task_id'] = $select_array['task_id'];
       unset($select_array['task_id']);
    } // if

    return $rowdata;

Here is an example where the same foreign table is used to supply two foreign keys:

    switch ($return_from) {
        // move returned value to the correct field
        case 'dict_table(popup1)jnr':
            $rowdata['database_id_jnr'] = $select_array['database_id'];
            $rowdata['table_id_jnr']    = $select_array['table_id'];
            // remove redundant data
            unset($select_array['database_id']);
            unset($select_array['table_id']);
            break;
        case 'dict_table(popup1)snr':
            $rowdata['database_id_snr'] = $select_array['database_id'];
            $rowdata['table_id_snr']    = $select_array['table_id'];
            // remove redundant data
            unset($select_array['database_id']);
            unset($select_array['table_id']);
            break;
        default:
            ;
    } // switch
    
    return $rowdata;

Note that different popup names are used to supply each different value. Note also that the primary key which is passed back may contain more than one field.

Here is an example where the selection is validated, and if incorrect the popup form is reactivated by calling $this->scriptNext():

    if ($return_from == 'srv_tree_node(popup)') {
        // get description for selected item
        $dbobject =& RDCsingleton::getInstance('tree_node');
        $dbobject->sql_select = 'node_id, node_desc, node_type';
        $selection = array2where($select_array);
        $foreign_data = $dbobject->getData($selection);

        if ($foreign_data[0]['node_type'] == 'L') {
            // merge with existing data
            $rowdata = array_merge($rowdata, $foreign_data[0]);
        } else {
            // this is not a location, so try again
            $this->scriptNext($return_from);
        } // if
        unset($dbobject);
    } // if

    return $rowdata;
$rowdata = $this->_cm_post_deleteRecord ($rowdata)
$rowdata (IN) is an associative array containing the data for the current database occurrence.
$rowdata (OUT) is the input array, plus any changes.

This is used to perform any processing that may be required after the contents of $rowdata has been processed by _dml_deleteRecord().

Here is some sample code:

    // if there are any records with a sequence number greater than the one
    // which has just been deleted then they must be shuffled up in order
    // to absorb the now vacant number.
    $survey_id   = $rowdata['survey_id'];
    $section_seq = $rowdata['section_seq'];
    $where       = "survey_id='$survey_id' and section_seq > $section_seq";
    $this->sql_orderby = 'section_seq';
    $rowdata2 = $this->getData_raw($where);
    if ($this->numrows == 0) {
        return $rowdata;
    } // if

    foreach ($rowdata2 as $row => $data) {
        if ($data['section_seq'] <> $section_seq) {
            $data['section_seq'] = $section_seq;
            $data = $this->updateRecord($data);
        } // if
        $section_seq++;
    } // foreach

    return $rowdata;
$rowdata = $this->_cm_post_eraseRecord ($rowdata)
$rowdata (IN) is an associative array containing the data for the current database occurrence.
$rowdata (OUT) is the input array, plus any changes.

This is used to perform any processing that may be required after a record has been processed by eraseRelations() and _dml_deleteRecord().

$rowdata = $this->_cm_post_fetchRow ($rowdata)
$rowdata (IN) is an associative array containing the data for the current database occurrence.
$rowdata (OUT) is the input array, plus any changes.

This is used to perform any processing that may be required after a record has been obtained by the fetchRow() method.

This is similar to the _cm_post_getData() method, but deals with only one record at a time instead of multiple records.

If $rowdata (OUT) is emptied it will signify that the current record cannot be processed and will cause the framework to automatically read the next record. This is used in the Output5 pattern to build a collection of labels which are only printed when there are enough to fill an entire line, as shown in the following code sample:

    // construct address field from current row
    $address = $fieldarray['addr_line1'];
    if (!empty($fieldarray['addr_line2'])) {
        $address = "\n".$fieldarray['addr_line2'];
    } // if
    if (!empty($fieldarray['addr_line3'])) {
        $address = "\n".$fieldarray['addr_line3'];
    } // if
    if (!empty($fieldarray['town'])) {
        $address = "\n".$fieldarray['town'];
    } // if
    if (!empty($fieldarray['county'])) {
        $address = "\n".$fieldarray['county'];
    } // if
    if (!empty($fieldarray['postcode'])) {
        $address = "\n".$fieldarray['postcode'];
    } // if

    $this->address_array[] = $address;      // save address
    $fieldarray = null;                     // do not print anything yet

    // obtain the number of labels to print on each line
    $count = (int)$this->report_structure['pdf']['label_print'];
    if ($count < 1) $count = 1;

    if (count($this->address_array) == $count) {
        // print all saved addresses
        foreach ($this->address_array as $ix => $address) {
            $name = 'address'.($ix+1);
            $fieldarray[$name] = $address;
        } // foreach
        // remove addresses which have been printed
        $this->address_array = array();
    } // if

    return $fieldarray;

Notice here that the output is either empty, in which case nothing will be printed (and the next record will be read), or it will contain fields with the names address1, address2, etc. These names nust match those defined in the report structure file.

$filename = $this->_cm_post_fileUpload ($filename, $filesize)
$filename (IN) is the name of the file which has just been uploaded.
$filesize is the size of the file which has just been uploaded.
$filename (OUT) is the filename in case it needs to be changed.

This is called after the file has been uploaded and placed in the upload directory which was identified in _cm_initialiseFileUpload(). This will enable the file to be processed in some way, such as running a script which will load the contents of a CSV file into the database. Here is some example code:

    function _cm_post_fileUpload ($filename, $filesize)
    // perform processing after a file has been uploaded.
    {
        // remove directory and extension from the filename
        $name = basename($filename, '.csv');

        switch ($name) {
            case 'product':
                $next['task_id'] = 'pro_takeon_product(batch)';
                break;

            case 'product_extra_values':
                $next['task_id'] = 'pro_takeon_product_extra_values(batch)';
                break;

            case 'product_supplier':
                $next['task_id'] = 'pro_takeon_product_supplier(batch)';
                break;

            default:
                // 'unknown filename - cannot be processed'
                $this->errors[] = getLanguageText('e0086');
                break;
        } // switch
        
        if (isset($next)) {
            // run another task after this one terminates
            append2ScriptSequence($next);
        } // if

        return $filename;

    } // _cm_post_fileUpload
$rowdata = $this->_cm_post_lastRow ()
$rowdata is the data which will be added to the PDF output.

This is used in the Output5 pattern in order to deal with any labels which have not been processed after the last record has been read. Label data is captured and stored in the _cm_post_fetchRow() method.

Here is some sample code which will return any stored data using names address1, address2, address3, et cetera:

    $fieldarray = array();

    if (!empty($this->address_array)) {
        // return all saved addresses
        foreach ($this->address_array as $ix => $address) {
            $name = 'address'.($ix+1);
            $fieldarray[$name] = $address;
        } // foreach
    } // if

    return $fieldarray;
$rows = $this->_cm_post_getData ($rows, &$where)
$rows (IN) is an indexed array containing all the rows which have just been retrieved from the database. Each row will contain an associative array of field names and values.
$where (IN) a string in the format of the WHERE clause of an SQL query.
$rows (OUT) the input array, plus any changes.
$where (OUT) is the input string, plus any changes.

This is used to perform any processing that may be required after data has been retrieved from the database. There may be any number of rows.

Here is some sample code:

    if (basename($_SERVER['PHP_SELF']) == 'lesson_list(a).php') {
        if (isset($rows[0]['class_id'])) {
            // 'Student is assigned to a class - cannot assign individual lessons'
            $this->errors[] = getLanguageText('e0014');
            $GLOBALS['nav_buttons_omit'][] = 'student_lesson(add)a';
        } // if
    } // if

    return $rows;

Click on these hyperlinks for descriptions of $nav_buttons_omit and getLanguageText().

Here is some sample code taken from the Data Dictionary export functions which use the UPDATE4 transaction pattern:

    // create object for retrieving table details
    require_once 'classes/dict_table.class.inc';
    $tableobj =& RDCsingleton::getInstance('dict_table');
    $tableobj->sql_orderby = 'table_id';
    
    // process each database separately
    foreach ($rowdata as $database_data) {
        $database_id = $database_data['database_id'];
        $subsys_dir  = $database_data['subsys_dir'];
        // create heading in text file
        $output = '-- file created on ' .date('F j, Y, g:i a') ."\r\n\r\n";
        // export data from this table (dict_database)
        $output = $this->_exportSqlData('dict_database', $database_data, $output);
        // retrieve all tables for this database
        $data = $tableobj->getData_raw("database_id='$database_id'");
        foreach ($data as $table_data) {
            // export data from this table (dict_table)
            $output = $this->_exportSqlData('dict_table', $table_data, $output);
        } // foreach
        // create trailer in text file
        $output .= "-- finished\r\n";
        // write current text file to disk
        $this->_writeTextFile ($output, $database_id, "$subsys_dir/sql");
    } // foreach
    
    // return an empty array as there is no database update
    return array();

Note here that the output array is empty, which means that there is nothing to be updated in the database. All retrieved data has been written out to a text file instead.

$rows = $this->_cm_post_insertMultiple ($rows)
$rows (IN) is an indexed array containing all the rows which have just been processed. Each row will contain an associative array of name=value pairs.
$rows (OUT) the input array, plus any changes.

Use this to perform custom processing after each entry in $rows has been processed by _dml_insertRecord().

Here is some sample code taken from the Data Dictionary import functions which use the ADD4 transaction pattern:

    // save errors from any inserts
    $errors = $this->errors;

    // process any records marked for delete
    if (!empty($this->delete_array)) {
        $array = $this->deleteMultiple($this->delete_array);
        $errors = array_merge($errors, $this->errors);
    } // if

    // process any records marked for update
    if (!empty($this->update_array)) {
        $array = $this->updateMultiple($this->update_array);
        $errors = array_merge($errors, $this->errors);
    } // if

    // return all insert, update and delete errors
    $this->errors = $errors;

    return $rows;

Note that this follows the insertion of new records with possible updates and deletes of other records using the contents of $this->update_array and $this->delete_array which were created using code in _cm_getInitialDataMultiple() similar to the following:

    // get any existing dictionary data for this table
    $dbname    = $rowdata['database_id'];
    $tablename = $rowdata['table_id'];
    $old_data  = $this->getData("database_id='$dbname' AND table_id='$tablename'");
    
    // initialise all data arrays
    $insert_array       = array(); 
    $this->update_array = array();  // defined for this class only
    $this->delete_array = array();  // defined for this class only
    
    // get latest data structures from the database
    $new_data = $this->_ddl_showColumns($dbname, $tablename);
    
    foreach ($old_data as $rowid => $rowdata) {
        $column_id = $rowdata['column_id'];
        if (... the same column_id exists in both arrays, then ...) {
            // put this data into the update array
            $this->update_array[] = $new_data[$newdata_rowid];
            // delete from both 'old' and 'new' data
            unset($old_data[$rowid]);
            unset($new_data[$newdata_rowid]);
        } // if
    } // foreach
    
    // anything left in $old_data is to be deleted
    foreach ($old_data as $rowid => $rowdata) {
        $this->delete_array[] = $old_data[$rowid];
    } // foreach
    
    // anything left in $new_data is to be inserted
    foreach ($new_data as $rowid => $rowdata) {
        $insert_array[] = $new_data[$rowid];
    } // foreach
    
    // this replaces the original input array
    return $insert_array;

Note that $insert_array may be empty, but this does not cause the operation to fail.

$rowdata = $this->_cm_post_insertOrUpdate ($rowdata, $insert_count, $update_count)
$rowdata (IN) is an indexed array containing one or more rows of database data.
$insert_count is the count of records which were inserted.
$update_count is the count of records which were updated.
$rowdata (OUT) is the input array, plus any changes.

This is here to allow any custom code to be processed at the end of an insertOrUpdate() operation.

$rowdata = $this->_cm_post_insertRecord ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) is the input array, plus any changes.

Use this to perform custom processing after the contents of $rowdata has been processed by _dml_insertRecord().

Here is some example code which adds data to an additional table:

function _cm_post_insertRecord ($rowdata)
// perform custom processing after database record has been inserted.
{
    // create initial entries on PLACE table
    $dbobject =& RDCsingleton::getInstance('wf_place');

    // create entry for START place
    $place_array[0]['workflow_id'] = $rowdata['workflow_id'];
    $place_array[0]['place_id']    = 1;
    $place_array[0]['place_name']  = 'START';
    $place_array[0]['place_type']  = '1';

    // create entry for END place
    $place_array[1]['workflow_id'] = $rowdata['workflow_id'];
    $place_array[1]['place_id']    = 2;
    $place_array[1]['place_name']  = 'END';
    $place_array[1]['place_type']  = '9';

    $place_array = $dbobject->insertMultiple($place_array);
    if ($dbobject->errors) {
        $this->errors = array_merge($this->errors, $dbobject->getErrors());
    } // if

    return $rowdata;
		
} // _cm_post_insertRecord

It is also possible to pass the entire input array ($rowdata) to another object without the need for any filtering, as in the following example:

function _cm_post_insertRecord ($rowdata)
// perform custom processing after database record has been inserted.
{
    $dbobject =& RDCsingleton::getInstance('other_table');
    $other_data = $dbobject->insertRecord($rowdata);
    if ($dbobject->errors) {
        $this->errors = array_merge($this->errors, $dbobject->getErrors());
    } // if

    return $rowdata;
		
} // _cm_post_insertRecord

This is because each table class will automatically filter out any unwanted columns before giving the data to the Data Access Object which is responsible for constructing and executing the SQL query.

$string = $object->_cm_post_output ($string, $filename)
$string (IN) is the string produced by previous processing.
$filename is the name of the output file.
$string (OUT) is the input string with any amendments.

Even though it may not be necessary to amend the string which was produced by the previous processing, there may be a need to perform some other action. An example of the empty method into which the necessary code can be placed is given below:

    function _cm_post_output ($string, $filename)
    // perform any processing required after the output operation
    {
        // custom processing goes here
        if ($GLOBALS['mode'] == 'pdf-list') {
            ....
        } // if

        return $string;

    } // _cm_post_output

Possible values in $GLOBALS['mode'] are:

$rowdata = $this->_cm_post_popupReturn ($rowdata, $return_from, $select_array)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$return_from is the name of the popup form which has just been processed.
$select_array is an associative array containing the selection made in the popup form.
$rowdata (OUT) is the input array, plus any changes.

Use this to perform custom processing while a selection from a popup form is being processed by popupReturn().

Here is an example which sets a description field depending on what has been returned:

    function _cm_post_popupReturn ($fieldarray)
    // perform any post-popup processing.
    {
        if (!empty($fieldarray['product_id'])) {
            if (!empty($fieldarray['prod_feature_id'])) {
                $fieldarray['order_item_desc'] = $fieldarray['prod_feature_desc'];
            } else {
                $fieldarray['order_item_desc'] = $fieldarray['product_name'];
            } // if
        } // if

        return $fieldarray;

    } // _cm_post_popupReturn
$rowdata = $this->_cm_post_search ($search, $selection)
$search is a string in the format of the WHERE clause in an SQL query using the values which were submitted in the search task.
$selection is a string containing any selection criteria passed down from the parent task.

Use this to perform custom processing after all the input has been validated and assembled into a WHERE string, but before control is passed back to the previous task.

The optional $selection string contains any selections made before the SEARCH screen was activated.

Here is an example which passes the selection criteria to a new task:

    function _cm_post_search ($search, $selection)
    // perform any post-search processing.
    {
        // pass these details to a new task for processing
        $next['task_id']   = 'pro_product(batch)datafeed1';
        $next['search']    = $search;
        $next['selection'] = $selection;
        append2ScriptSequence($next);

        return $search;

    } // _cm_post_search

Note that this uses the append2ScriptSequence() function to activate the new task, although it is also possible to use the scriptNext() function depending on what behaviour suits your circumstances. This is documented in FAQ118.

$rows = $this->_cm_post_updateLinkData ($rows, $postarray)
$rows (IN) is an indexed array containing the data for several database occurrences.
$postarray is an array which shows which occurrences in $rows were SELECTED or NOT SELECTED.
$rows (OUT) the input array, plus any changes.

Use this to perform custom processing after the contents of $rows has been processed by updateLinkData().

$rows = $this->_cm_post_updateMultiple ($rows)
$rows (IN) is an indexed array containing the data for several database occurrences.
$rows (OUT) is the input array, plus any changes.

Use this to perform custom processing after the contents of $rows has been processed by _dml_updateRecord().

Here is an example which takes all the rows which have just been updated and resequences them before they are displayed back to the user:

    foreach ($rows as $row) {
        // create a new array indexed by 'tree_level_seq'
        $sortedarray[$row['tree_level_seq']] = $row;
    } // foreach

    ksort($sortedarray);      // sort this array
    $rows = $sortedarray;     // replace original array

    return $rows;
$rowdata = $this->_cm_post_updateRecord ($rowdata, $originaldata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$originaldata is from the same database occurrence, but before any changes were made.
$rowdata (OUT) is the input array, plus any changes.

Use this to perform custom processing after the contents of $rowdata has been processed by _dml_updateRecord(). The $originaldata array is provided so that you may determine which fields have actually been changed.

Here is some sample code:

function _cm_post_updateRecord ($rowdata, $old_data)
// perform custom processing after database record is updated.
{
    // calculate extended price on this record
    $rowdata['extended_price'] = $rowdata['product_price'] 
                               * $rowdata['item_quantity'];

    // update total on header record
    $order_id = $rowdata['order_id'];
    $where = "SELECT sum(product_price * item_quantity) "
            ."FROM order_item WHERE order_id='$order_id'";
    $order_value = $this->getCount($where);

    $dbobject =& RDCsingleton::getInstance('order_hdr');
    $data['order_id']    = $order_id;
    $data['order_value'] = $order_value;
    $data = $dbobject->updateRecord($data);
    if ($dbobject->errors) {
        $this->errors = array_merge($this->errors, $dbobject->getErrors());
    } // if

    return $rowdata;
		
} // _cm_post_updateRecord

It is also possible to pass the entire input array ($rowdata) to another object without the need for any filtering, as in the following example:

function _cm_post_updateRecord ($rowdata, $old_data)
// perform custom processing after database record has been updated.
{
    $dbobject =& RDCsingleton::getInstance('other_table');
    $other_data = $dbobject->insertRecord($rowdata);
    if ($dbobject->errors) {
        $this->errors = array_merge($this->errors, $dbobject->getErrors());
    } // if

    return $rowdata;
		
} // _cm_post_updateRecord

This is because each table class will automatically filter out any unwanted columns before giving the data to the Data Access Object which is responsible for constructing and executing the SQL query.

$rowdata = $this->_cm_pre_deleteRecord ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) is the input array, plus any changes.

This is used to perform any processing before the selected record is deleted from the database.

If any messages are placed in $this->errors the deletion will be abandoned.

$rowdata = $this->_cm_pre_eraseRecord ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) is the input array, plus any changes.

This is used to perform any processing before the selected record, and all its children, is erased from the database.

If any messages are placed in $this->errors the erase will be abandoned.

Here is some sample code:
    // no logging for this 'erase'
    $this->audit_logging = false;

Click on the hyperlink for details concerning $this->audit_logging.

$where = $this->_cm_pre_getData ($where, $where_array, $fieldarray=null)
$where is a string in the format of the WHERE clause in an SQL query.
$where_array is the same criteria as an associative array.
$fieldarray is optional, an associative array.

This is used to perform any processing before the current selection criteria is used to retrieve data from the database.

NOTE: if the current object is a junior object in the controller then $fieldarray will contain all the details of the current record in the parent object, not just its primary key. It is populated using code similar to the following:

$outer_data  = $dbouter->getData($where);
$inner_where = array2where($outer_data, $dbouter->getPkeyNames(), $dbouter);
$dbinner->setFieldArray($outer_data);
$inner_data  = $dbinner->getData($inner_where);

The contents of $fieldarray will be overwritten by the subsequent call to _dml_getdata() unless $this->skip_getdata is set to TRUE.

Here is an example which deals with the situation where a field name passed down by the previous form needs to be converted before it can be used in the current form:

    $where = str_replace('node_id=', 'node_id_snr=', $where);

Here is an example which defines the parameters for the SELECT statement which override what would be created by default:

    if (empty($this->sql_select)) {
        $this->sql_select = 'wf_arc.*, transition_name, place_name, place_type ';
        $this->sql_from   = 'wf_arc '
                           .'LEFT JOIN wf_transition '
                           .'ON (wf_transition.workflow_id=wf_arc.workflow_id '
                           .'AND wf_transition.transition_id=wf_arc.transition_id) '
                           .'LEFT JOIN wf_place '
                           .'ON (wf_place.workflow_id=wf_arc.workflow_id '
                           .'AND wf_place.place_id=wf_arc.place_id)';
    } // if
$rows = $this->_cm_pre_insertMultiple ($rows)
$rows (IN) is an indexed array containing the data for one or more database occurrences.
$rows (OUT) is the input array, plus any changes.

Use this to perform any custom processing on the contents of $rows before it is added to the database.

If any messages are placed in $this->errors the insert will be abandoned.

$rowdata = $this->_cm_pre_insertOrUpdate ($rowdata)
$rowdata (IN) is an indexed array containing the details of one or more database occurrences.
$rowdata (OUT) is the input array, plus any changes.

This is here to allow and custom code to be processed at the start of an insertOrUpdate() operation.

$rowdata = $this->_cm_pre_insertRecord ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) is the input array, plus any changes.

Use this to perform any custom processing on the contents of $rowdata before it is validated and added to the database.

If any messages are placed in $this->errors the insert will be abandoned.

Here is an example which loads a default value into an empty field:

    if (empty($rowdata['place_type'])) {
        // set default value
        $rowdata['place_type'] = '5';
    } // if

    return $rowdata;
$filename = $object->_cm_pre_output ($filename)
$filename (IN) is the initial value for this type of output.
$filename (OUT) is the input value with any amendments.

For PDF output $filename will only be relevant if 'destination' is set to 'file' or 'download').

As well as being able to provide a customised file name for PDF output, this method will also allow the object's structure to be modified before any data is processed. An example is given below:

    function _cm_pre_output ($filename)
    // perform any processing required before the output operation
    {
        // custom processing goes here
        if ($GLOBALS['mode'] == 'pdf-list') {
            // do not display long text from option list
            unset($this->fieldspec['subsys_id']['optionlist']);
            // do not display description from foreign table
            unset($this->fieldspec['start_task_id']['foreign_field']);
        } // if

        return $filename;

    } // _cm_pre_output

In List view, with one record per line, there may not be enough room to display full descriptive text from optionlist or foreign_field, so this forces the system to display the original short value instead.

Here is an example where the filename is customised:

    function _cm_pre_output ($filename)
    // perform any processing required before the output operation
    {
        $info = pathinfo($filename);
        if (version_compare(phpversion(), '5.2.0', '<')) {
            if (isset($info['extension'])) {
            	$info['filename'] = basename($filename, '.'.$info['extension']);
            } // if
        } // if
        
        $filename = $info['dirname'] .'/' .'whatever' .'.' .$info['extension'];

        return $filename;

    } // _cm_pre_output

Possible values in $GLOBALS['mode'] are:

$rows = $this->_cm_pre_updateLinkData ($rows, $postarray)
$rows (IN) is an indexed array containing the data for one or more database occurrences.
$postarray is an array which shows which occurrences in $rows were SELECTED or NOT SELECTED.
$rows (OUT) is the input array, plus any changes.

Use this to perform any custom processing on the contents of $rows before any database updates are performed.

If any messages are placed in $this->errors all further processing will be abandoned.

$rows = $this->_cm_pre_updateMultiple ($rows)
$rows (IN) is an indexed array containing the data for one or more database occurrences.
$rows (OUT) is the input array, plus any changes.

Use this to perform any processing on the contents of $rows before any is processed by _dml_updateRecord().

If any messages are placed in $this->errors all further processing will be abandoned.

Here is an example which checks that a value which the user may have changed in each row is unique within this set of rows:

    $count = count($rows);
    $used  = array();
    foreach ($rows as $rownum => $rowdata) {
        $seq = $rowdata['tree_level_seq'];
        if (intval($seq) < 1) {
            // 'Cannot be less than $minvalue'
            $this->errors[$rownum]['tree_level_seq'] = getLanguageText('e0007', 1);
        } elseif (intval($seq) > $count) {
            // "Must not be greater than $count"
            $this->errors[$rownum]['tree_level_seq'] = getLanguageText('e0008', $count);
        } // if

        // make sure that each value is used no more than once
        if (array_key_exists($seq, $used)) {
            // 'Value has already been used'
            $this->errors[$rownum]['tree_level_seq'] = getLanguageText('e0009');
        } else {
            $used[$seq] = 'y';
        } // if
    } // foreach

    return $rows;

Notice here that as multiple rows are being validated each entry in $this->errors is indexed by its row number.

Click on the following hyperlink for a description of getLanguageText().

$rowdata = $this->_cm_pre_updateRecord ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) is the input array, plus any changes.

This is used to perform any processing before the selected record is validated and processed by _dml_updateRecord().

If any messages are placed in $this->errors all further processing will be abandoned.

Here is some sample code:

    // if record does not currently exist then insert it,
    // which will cause the update to be skipped
    $where = array2where($rowdata, $this->getPkeyNames());

    $query = "SELECT count(*) FROM $this->tablename WHERE $where";
    $count = $this->getCount($query);

    if ($count == 0) {
        $rowdata = $this->insertRecord($rowdata);
    } // if

    return $rowdata;

Note also that row locking can be turned ON during a database transaction by amending $this->row_locks as follows:

    $this->row_locks = 'SH';    // shared
    $this->row_locks = 'EX';    // exclusive
void = $this->_cm_reset ($where)
$where is a string in the format of the WHERE clause of a SQL query.

This can be used to perform any additional processing which may be required when the RESET button is pressed.

void = $this->_cm_restart ($pattern_id, $zone, $return_from, $return_action, $return_string)
$pattern_id is a string which contains the pattern id of the current task.
$zone is a string which identifies the position of the current object within the current task. This may be 'main', 'outer', 'middle' or 'inner'.
$return_from is the identity of the task from which the system is returning.
$return_action is a string which indicates the completion status of that task.
$return_string is a string which is returned from the $return_from task.

This is used when a task is restarted after being suspended for the processing of a child task. It is also used in a MULTI2 task when the $search values are changed.

Here is an example of its use:

    function _cm_restart ($pattern_id, $zone, $return_from, $return_action, $return_string)
    // script is being restarted after running a child form, so check for further action.
    {
        if ($pattern_id == 'LIST2' AND $zone == 'outer') {
            if ($return_from == 'py_email_case(upd4)close' AND $return_action == 'OK') {
            	// this case is now closed, so return to previous task
            	scriptPrevious();
            } // if
        } // if

        return;

    } // _cm_restart
$javascript = $this->_cm_setJavaScript ($javascript)
$javascript is an array of javascript entries that will be transferred to the <HEAD> or <BODY> elements of the HTML document.

Here is some sample code:

    function _cm_setJavaScript ($javascript)
    // insert any javascript to be included in the <HEAD> or <BODY> elements.
    {
        $javascript['head'][]['file'] = '...';
        $javascript['head'][]['code'] = '...';

        $javascript['body']['onload'] = '...';
        $javascript['body']['onunload'] = '...';

        $javascript['tbody'] = array('id' => 'waitingRoom', 'style' => 'display:none');

        $this->fieldspec['field']['javascript'] = array('onclick' => 'doStuff(1);',
                                                        '...' => '...');

        return $javascript;

    } // _cm_setJavaScript

For more details please refer to RADICORE for PHP - Inserting Optional JavaScript.

$scrollarray = $this->_cm_setScrollArray ($where, $where_array)
$where is a string in the format of the WHERE clause of a SQL query.
$where_array is the same data in the form of an associative array.
$scrollarray will contain whatever data is required by the current transaction.

Here is the default code which uses splitWhereByRow:

    $array = array();

    $array = splitWhereByRow($where);    // default - replace with custom code

    return $array;

An example of this can be found in the Maintain Help Text function which shows the current help text for a selection of entries. Instead of reading the database and only showing those entries which currently exist it constructs an array of possible primary keys and reads the database using one of these keys at a time. If an entry does not currently exist it will show a blank screen and allow the user to create a new record instead of updating an existing one.

An example of a customised version can be found in the Survey/Questionnaire prototype regarding the Survey Answers screen which shows all the questions and all the answers from the current user. This cannot simply read the answers from the database because the answers may not have been entered yet, so it constructs an array of primary keys to possible answers using the available questions. When scrolling through the array it will attempt to read each answer from the database, but if one does not exist yet the screen will show the question without an answer. When the user presses the SUBMIT button it will add that answer to the database. The user can go back to an existing answer at an time and modify it.

$rowdata = $this->_cm_unFormatData ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) is the input array, plus any changes.

This is used when data received from the user needs to be formatted before it is written to the database, and where that formatting cannot be achieved in unFormatData(). It is the opposite of _cm_formatData().

$fieldarray = $this->_cm_updateFieldArray ($fieldarray, $postarray, $rownum)
$fieldarray (IN) is an associative array containing the current details of a single database occurrence.
$postarray is data extracted from the $_POST array which applies to this row.
$rownym is the row number, starting from zero, of this row in $this->fieldarray.
$fieldarray (OUT) is the input array, plus any changes.

This should only be used to detect and deal with screen updates which are NOT posted by a SUBMIT button, such as a javascript submit() function as this will not be followed by a database update. When a SUBMIT button is pressed you should refer to the $object->insertRecord() or $object->updateRecord() methods.

Your class may contain code such as the following to activate a javascript event:

    $this->fieldspec['packagesequenceno']['javascript'] = array('onchange' => 'this.form.submit();');

When a change is made to the packagesequenceno field on any row in the current screen you will need code to deal with that as shown in the following example:

function _cm_updateFieldarray ($fieldarray, $postarray, $rownum)
// allow object to deal with any changes POSTed from the form.
// $fieldarray contains current data from one row.
// $postarray contains any changes made in the form for this row.
// $rownum identifies which row is being processed
{
    if ($postarray['packagesequenceno'] != $fieldarray['packagesequenceno']) {
        // update package weights in parent object
        $parent_rows =& $this->getParentData();
        foreach ($parent_rows as &$parent_data) {
            $parent_data['pkg_weight'] = 0;  // reset current weights to zero
        } // foreach
        $this->fieldarray[$rownum]['packagesequenceno'] = $postarray['packagesequenceno'];
        foreach ($this->fieldarray as $rowdata) {
            $pkg_key = $rowdata['packagesequenceno'] -1;  // $postarray starts at 1, $fieldarray starts at zero
            if (!array_key_exists($pkg_key, $parent_rows)) {
                $this->errors[$rownum]['packagesequenceno'] = "Invalid Package Number";
                return $fieldarray;
            } // if
            $parent_rows[$pkg_key]['pkg_weight'] = $parent_rows[$pkg_key]['pkg_weight'] + $rowdata['weight'];
        } // foreach
        $this->setParentData($parent_rows);
    } // if

    return $fieldarray;

} // _cm_updateFieldarray

The parent object contains one or more packages being assembled for a shipment. The child object contains one or more items which need to be placed in those packages. If there is more than one item there may be more than one package, and each package will have a total weight which is the sum of the weights of each of the items which it contains. So if an item is moved from one package to another then the weights of all the packages must be recalculated.

Note here that the contents of $fieldarray, which contains the data from only one row, is used only to detect that the packagesequenceno from that row has changed. It then iterates through $this->fieldarray as it contains all the item rows.

$msg = $this->_cm_updateSelection ($selection, $replace)
$selection is a string in the format of the WHERE clause in an SQL query and may identify one or more database occurrences.
$replace is a string in the format of the SET clause in an sql UPDATE, and may specify changes to any number of fields.
$msg will contain a message in the format 'N rows were updated'.

This is used to perform the same update of one or more fields on a selection of database occurrences. The default code will call _dml_updateSelection(), as follows:

// this is the default code, which may be replaced if necessary
$count = $this->_dml_updateSelection($selection, $replace);

// '$count records were updated in $tablename'
return getLanguageText('sys0006', $count, strtoupper($this->tablename));

Click on the following hyperlink for a description of getLanguageText().

In case it is required to delete the selected records instead of update them you may wish to use the default code which can be found in the _cm_deleteSelection() method.

It will not be necessary to copy this method into the subclass unless the default code is inadequate.

Here is an example of some code which updates a session variable instead of the database:

    $array = where2array($selection);

    if (array_key_exists('schedule_id', $array)) {
        $_SESSION['crs_schedule_id'] = $array['schedule_id'];
    } else {
        unset($_SESSION['crs_schedule_id']);
    } // if

    return;

NOTE: if you wish to update a field with a function instead of a simple value take a look at $this->allow_db_function.

void $this->_cm_validateDelete ($rowdata, $parent_table)
$rowdata is an associative array containing the details of a single database occurrence.
$parent_table will be empty except for a cascade delete, in which case it will identify the table from which the delete was initiated.

Use this to verify that the selected record can be deleted. It is called before the DELETE screen is displayed which requires the user's confirmation before the record is actually deleted. If any message is placed in $this->errors the DELETE button will be removed from the screen's action bar, which will prevent the user from confirming that action on this occurrence.

If this method is called as part of a cascade delete then $parent_table will be non-empty.

Here is some sample code:

    switch ($fieldarray['place_type']) {
        case '1':
            // 'Cannot delete START place'
            $this->errors[] = getLanguageText('e0006');
            break;
        case '9':
            // 'Cannot delete END place'
            $this->errors[] = getLanguageText('e0007');
            break;
        default:
            ;
    } // switch

    return;

Click on the following hyperlink for a description of getLanguageText().

$rowdata = _cm_validateInsert ($rowdata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$rowdata (OUT) is the input array, plus any changes.

This is used to perform any processing that may be required before a new occurrence is added to the database.

If any messages are placed in $this->errors all further processing will be abandoned.

Here is some sample code:

    // valid characters are numbers, letters, and '_#$()'
    if (ereg('[^0-9a-zA-Z_#$()]', $rowdata['task_id'], $regs)) {
        // "Contains invalid character (X)"
        $this->errors['task_id'] = getLanguageText('e0022', $regs[0]);
    } // if

    return $rowdata;
$postarray = _cm_validateSearch ($postarray)
$postarray (IN) is an associative array containing the contents of the search screen.
$postarray (OUT) is the input array, plus any changes.

This is used to perform any processing before the data from a search screen is transformed into a string which is passed back to the previous form.

Here is some sample code:

    function _cm_validateSearch ($fieldarray)
    // perform custom validation on data entered via a search screen.
    // put any errors into $this->errors.
    {
        if (!empty($fieldarray['product_name'])) {
            // cannot have both of these fields
            $fieldarray['product_id'] = null;
        } // if

        return $fieldarray;

    } // _cm_validateSearch
$rowdata = $this->_cm_validateUpdate ($rowdata, $originaldata)
$rowdata (IN) is an associative array containing the details of a single database occurrence.
$originaldata is an associative array containing the data for the same occurrence before any changes were made.
$rowdata (OUT) is the input array, plus any changes.

This is used to perform an processing that may be required before a record is processed by _dml_updateRecord().

If any messages are placed in $this->errors all further processing will be abandoned.

Here is some sample code:

    if ($rowdata['product_subtype'] != $originaldata['product_subtype']) {
        // value has changed, so ...
        if ($originaldata['product_subtype'] == 'G') {
            $where = "product_id='{$rowdata['product_id']}'";
            $query = "SELECT count(*) FROM good_identification WHERE $where";
            $count = $this->getCount($query);
            if ($count > 0) {
                // reset to correct value
                $rowdata['product_subtype'] = 'G';
                // "Cannot change when identification values exist"
                $this->errors['product_subtype'] = getLanguageText('e0018');
                return $rowdata;
            } // if
        } // if
    } // if

    return $rowdata;

Class Properties

These properties (member variables) are defined within the abstract table class and are therefore available within all subclasses. Their purpose is to provide information, or to alter the way in which certain functions operate.

Note that when a subclass is created for an actual database table it is not necessary to create member variables for each field within that table. All data is passed into and out of the object in arrays, therefore individual fields are referenced as elements with the array.

$this->allow_db_function (array)

In some database updates you may have an entries in your $fieldarray such as:

field1 = Weight (gm)
field2 = REPLACE(field2,'_','-')

In this case the generated SQL update statement will look like the following:

UPDATE ... SET field1='Weight (gm)', field2='REPLACE(field2,\'_\',\'-\')' ...

But what if the expression for field2 is supposed to be a function? The only way to tell the framework not to enclose the pattern 'string(...)' in quotes is to add a value to this array as in:

$this->allow_db_function[] = 'field2';

This must be done immediately prior to the update and will automatically be cleared after the update has been completed.

This will cause the generated SQL update statement to look like the following:

UPDATE ... SET field1='Weight (gm)', field2=REPLACE(field2,'_','-') ...
$this->allow_empty_where (boolean)

In some transactions an empty $where string will cause the transaction to be terminated due to the fact that no selection criteria has been passed down from the parent form. By setting this value to TRUE the transaction will be allowed to continue on the assumption that data will be either retrieved or constructed by other means.

$this->alt_language_cols (string)

This identifies the columns on $this->alt_language_table that contain translations of text in alternative languages.

$this->alt_language_table (string)

This is coupled with $this->alt_language_cols, and identifies the table that contains translations of text in alternative languages, as described in Internationalisation and the Radicore Development Infrastructure (Part 2).

$this->audit_logging (boolean)

If this is set to TRUE then any changes made to this database table will be recorded in the AUDIT database when processed by the DAO.

This switch can be modified using the Update Table function in the Data Dictionary, and then passed to the application by using the Export Table function.

$this->checkPrimaryKey (boolean)

If this is set to TRUE it will cause the isPkeyComplete() function to be called.

If this is set to FALSE then isPkeyComplete() will not be called.

$this->child_object (object)

This is only available when the object is used within a page controller which uses two or more objects in a parent-child or parent-child-grandchild hierarchy. In the senior object of the pair this will be set as a reference to the junior object in the pair. This will then enable the getChildData() and setChildData() methods, as well as other methods, to operate on that object.

$this->child_relations (array)

This array contains details of any child tables where the current table is the parent in a parent-to-child (one-to-many) relationship.

This information is maintained by using the relevant functions within the Data Dictionary. When exported this will produce a particular structure.

$this->dbms_engine (string)

This identifies the RBDMS engine, such as "mysql" or "pgsql" (PostgreSQL), which is to be used when accessing this database table. All database access is performed through a single Data Access Object (DAO), but the class definition used to instantiate this object is obtained from a file whose name is in the format dml.<engine>.class.inc, which therefore provides separate implementation details for each database engine.

A default setting for all database tables is defined in the config.inc file as most applications will use a single database engine throughout. However, should individual tables require a different value this can be modified in the table's class file.

$this->dbname (string)

This is the name of the database (or "schema" in PostgreSQL) in which this table resides.

$this->default_orderby (string)

This is used as the default sort sequence in all LIST forms if no other sorting information has been specified, such as the ORDER BY field in Update Task, or when the user clicks on a hyperlink in a column heading.

This value can be modified using the Update Table function in the Data Dictionary, and then passed to the application by using the Export Table function.

$this->dirname (string)

This contains the name of the directory from which this class file was loaded, and is set in the class constructor. It is used when reading in the contents of file <tablename>.dict.inc which was exported from the Data Dictionary.

$this->errors (array)

This array is used to communicate any error messages back to the user, and is usually set when some sort of validation error occurs. Entries can be inserted into the array as follows:

$this->errors['fieldname'] = 'message text';  // associative
$this->errors[] = 'message text';             // indexed

Messages which are keyed by field name will be shown in the screen adjacent to that field, while all others will appear in the general message area.

Instead of using hard-coded text it may be advisable to use getLanguageText() instead as this will make it easier to provide the same text in different languages.

$this->expanded (array)

This is used in those transactions which display nodes in a tree structure where individual nodes can be expanded or collapsed in the display. If a node has an entry in this array then that node must be expanded, i.e. all the children of that node must be included in the display.

Entries in this array are added when the user presses the EXPAND button in the screen, and removed when the user presses the COLLAPSE button. This data is then processed by the getNodeData() method.

$this->fieldarray (array)

This is the data that is currently being processed. It may be an indexed array containing data for multiple database occurrences, or it may be an associative array of name=value pairs for a single database occurrence.

The contents of this array may be retrieved from the database either from a single table or multiple tables if a JOIN is used is the sql SELECT statement, or it may be created/modified within the table class.

If the current transaction has a screen then the entire contents of this array will be extracted and written out to the XML file so that it can be incorporated into the screen by the XSL transformation.

$this->fieldspec (array)

This array holds details of all the fields which belong in the current database table, and for each field it has an array of specifications such as size and type. Full details can be found in the Data Dictionary.

This array also holds details on how individual fields should be represented on the screen, such as which control is to be used. This information can be modified using the Update Column screen.

Alterations can be made to this array with custom code, typically within the _cm_changeConfig() method, but these alterations will be lost whenever an insert or update is performed as the original array will be reloaded from the file which was exported from the Data Dictionary.

$this->field_access (array)

This is an array of field names and access restrictions, such as noedit or nodisplay, which will be used to modify the current screen before it is displayed. The information is obtained from either the Maintain Field Access via Task or Maintain Field Access via Role screens.

$this->fields_not_for_trimming (array)

This is used to override the default behaviour of the field validation code which automatically trims leading and trailing spaces from all string fields. If there is any field that you want left untrimmed you must use code similar to the following in any customisable method prior to an insert or an update:

function _cm_commonValidation ($fieldarray, $originaldata)
// perform validation that is common to INSERT and UPDATE.
{
    $this->fields_not_for_trimming[] = 'field1';
    $this->fields_not_for_trimming[] = 'field2';
		
    return $fieldarray;
		
) // _cm_commonValidation
$this->initial_values (array)

This is loaded with values obtained by calling the _getInitialValues() method.

$this->inner_table (string)

This is set by the controller in a LINK1 transaction which deals with a many-to-many relationship. This is actually implemented in the database as a pair of one-to-many relationships with an intermediate table, known as an intersection, link or xref table. Although the screen shows data from all three tables, MANY1 (outer), LINK and MANY2 (inner), the data from LINK and MANY2 is combined into a single zone by means of an sql JOIN. This means that the LINK object must be told the identity of the table with which it needs to JOIN. This information is used within _sqlAssembleWhereLink().

See also $this->outer_table and $this->is_link_table.

$this->instruction (array)

This holds instructions that can be passed from one form to another. It is accessed using the following methods:

Here is an example of how to set the SELECT box in a parent screen:

An example of this can be found in the Survey/Questionnaire prototype in the List Survey Sections and List Survey Questions screens where an entry can be selected prior to pressing one of the navigation buttons labelled MOVE UP or MOVE DOWN. After the operation has been completed the List screen will be redisplayed with the selected item in its new position, but with the SELECT box still checked ON so that it can be moved again without having to be re-selected.

Here is an example of how to automatically expand a node in a parent screen:

An example of this can be found in the Survey/Questionnaire prototype in the Organisation Tree screen after adding a child node to a currently unexpanded parent node.

$this->is_link_table (boolean)

This is set by the controller in a LINK1 transaction. It is checked in _sqlAssembleWhere() and, if set, causes it to execute _sqlAssembleWhereLink() instead.

See also $this->outer_table and $this->inner_table.

$this->lastpage (integer)

When performing a getData() or getData_raw() operation and a particular page number is requested (when $this->pageno is non-zero) in conjunction with a particular page size (when $this->rows_per_page is non-zero), the number of rows actually retrieved ($this->numrows) is divided by the page size to calculate the total number of pages ($lastpage). This value is passed to the XML file and used in the pagination area as a visual guide to the user.

$this->link_item (string)

This is set during the execution of _sqlAssembleWhereLink() and identifies the foreign key on the LINK table which joins to the INNER table, as is shown as link.inner_id in the CASE statement.

$this->lookup_css (array)

This is an associative array which is used to hold the CSS class name which is to be used for individual entries in a radio group. For example, entries for field authentication are defined within variable $this->lookup_data as follows:

$this->lookup_data = array [2]
    authentication = array [3]
        INTERNAL = (string:8) Internal
        LDAP = (sting: 4) LDAP
        RADIUS = (string: 6) Radius
    login_type = array [2]
        ....

To specify the CSS class for an individual entry you must put it in $this->lookup_css as follows:

$this-lookup_css = array [1]
    authentication = array [1]
        INTERNAL = (string: 8) whatever

When the XML document is constructed it will contain something like the following:

<root>
  ....
  <lookup>
    <authentication>
      <option id="INTERNAL" class="whatever">Internal</option> 
      <option id="RADIUS">RADIUS</option> 
      <option id="LDAP">LDAP</option> 
    </authentication>
  </lookup>
  ....
</root>

When the HTML output is constructed it will contain something like the following:

    <div class="whatever">
      <input class="radio" type="radio" name="authentication" value="INTERNAL" id="INTERNAL" checked="checked"/>
      <label for="INTERNAL">Internal</label>
    </div>
    <div>
      <input class="radio" type="radio" name="authentication" value="RADIUS" id="RADIUS"/>
      <label for="RADIUS">RADIUS</label>
    </div>
    <div>
      <input class="radio" type="radio" name="authentication" value="LDAP" id="LDAP"/>
      <label for="LDAP">LDAP</label>
    </div>

For an example of how to use this feature please refer to FAQ117.

$this->lookup_data (array)

This is an associative array which is used to hold the contents of picklists (dropdown lists or radio groups). These are obtained using getValRep(). As there can be several lists for several fields each entry must be keyed by its field name.

This data will be written out to its own area in the XML file so that the XSL transformation process can extract the correct data and load it into the relevant field's option list.

Standard code within the framework will automatically add a blank entry to the start of each list. In SEARCH screens this will show the text "(undefined)" while in other screens it will be blank.

The CSS style of individual entries can be modified by placing entries within $this->lookup_css.

$this->messages (string or array)

This can be used to output a message which is not an error message to the message area in the screen. A single message can be presented as a string, as in:

$this->messages = 'to be, or not to be, that is the question';

Multiple messages will have to be presented in an indexed array, as in:

$this->messages[] = 'to be, or not to be, that is the question';
$this->messages[] = 'a horse, a horse, my kingdom for a horse';

Instead of using hard-coded text it may be advisable to use getLanguageText() instead as this will make it easier to provide the same text in different languages.

$this->no_controller_msg (boolean)

Default is FALSE. If set to TRUE it will prevent the "Update Successful" message from being displayed in the following:

$this->no_display_count (boolean)

Default is FALSE. If set to TRUE it will prevent the count of affected records from being displayed in the following:

$this->no_duplicate_error (boolean)

This can be set to TRUE prior to a call to the insertRecord() method to prevent an error if a record with that primary key already exists. The following will occur:

This can be used in scripts which load data into the database from a CSV file so that they can be rerun without causing an error.
$this->numrows (integer)

In operations that can deal with multiple rows, such as getData(), getData_raw(), insertMultiple(), updateMultiple() and deleteMultiple(), this variable will contain the actual row count encountered by that operation. For the insertRecord() method it will be set to 1 if the record was inserted, or 0 if it was not due to a duplicate primary key.

$this->outer_table (string)

This is set by the controller in a LINK1 transaction which deals with a many-to-many relationship, and is used within _sqlAssembleWhereLink().

See also $this->inner_table and $this->is_link_table.

$this->pageno (integer)

In screens which show multiple database occurrences (rows), such as LIST screens, there is a limit on the maximum number of rows which are to be displayed at any one time (see $this->rows_per_page). This breaks all the available rows into a number of groups or pages, and after the initial display, which starts at page number 1, the user can request a different page to be displayed by using one of the hyperlinks in the pagination area. This value is then used in the OFFSET clause of the subsequent SQL query.

$this->parent_object (object)

This is only available when the object is used within a page controller which uses two or more objects in a parent-child or parent-child-grandchild hierarchy. In the junior object of the pair this will be set as a reference to the senior object in the pair. This will then enable the getParentData() and setParentData() methods, as well as other methods, to operate on that object.

$this->parent_relations (array)

This array contains details of any parent tables where the current table is the child in a parent-to-child (one-to-many) relationship.

This information is maintained by using the relevant functions within the Data Dictionary. When exported this will produce a particular structure.

$this->primary_key (array)

This array identifies all the fields (yes, there may be more than one) which constitute the primary key for this database table.

This information is automatically obtained when the table structure is imported into the Data Dictionary. When exported this will produce a particular structure.

$this->reuse_previous_select (boolean)

The default value is FALSE. If set to TRUE this will cause the _dml_ReadBeforeUpdate() method to reuse the current SELECT statement instead of creating a new one. This is for those cases where the current SELECT statement contains a field from a JOINed table, and that same field needs to be made available in the $originaldata arguments of the _cm_commonValidation() and _cm_validateUpdate() methods.

If this option is required it can be set in the _cm_pre_updateRecord() method

$this->rows_per_page (integer)

This is used to limit the size of each HTML page in a multi-page display (see $this->pageno). The value is initially set to 10 for those tasks which have HTML screens, but can be altered by clicking on one of the SHOWnn links in the middle row of the navigation bar. This value is then used in the LIMIT clause of the subsequent SQL query.

If this value is set to 0 then no LIMIT clause will be added to the sql SELECT statement, thus causing all database records which match the selection criteria to be retrieved. This should not be used in HTML screens as huge pages of output are not considered to be user-friendly, as well as the performance overhead of reading and rendering all that data.

$this->row_locks (string)

This is used in _dml_getData when reading records from the database via the current object while a database transaction is in progress. It can be set to any of the following values, which will cause the relevant clause to be added to the SQL query:

This can be set in _cm_pre_updateRecord(), and takes precedence over $GLOBALS['lock_rows'].

$this->scrollarray (array)

This is an internal array of primary keys which may or may not currently exist in the database.

This array is manipulated by the following:

$this->scrollindex (integer)

This holds the current pointer into $this->scrollarray.

$this->selectall (boolean)

This is set to TRUE when the SELECT ALL option in the middle row of the navigation bar is used, and set to FALSE when the UNSELECT ALL option is used.

$this->skip_getdata (boolean)

Except for INPUT and SEARCH screens, data will be loaded into $this->fieldarray by calling _dml_getData() to retrieve it from the database. However, there may be circumstances in which the data to be displayed does not actually exist in the database and has to be constructed by custom code instead. By setting this value to TRUE the call to _dml_getData() will be skipped, thus preventing a read failure and the overwriting of $this->fieldarray.

$this->skip_validation (boolean)

In some circumstances when a particular record is inserted or updated it may require another record to be inserted or updated. It is also possible that while inserting or updating the second record it will attempt repeat the action with the first record, which will attempt to repeat the action with the first record, and so on, causing an infinite loop.

A way around this is to ensure that all validation is performed within the initial processing of the first record, and when it communicates with the object for the second record it sets this switch to TRUE which will cause it to perform all standard processing except for the following:

An example of this processing can be found in the Address processing within the Example prototype. A person can change his address over a period of time, so each address record has a start date and an end date. The golden rule is that these dates are contiguous, i.e. there are no overlapping dates and no missing dates. The start date on each address must be 1 day later than the end date of the previous address. When a new address is created the application will automatically update the end date on the current address.

It is also possible to change the start date or end date on any existing record, which means that the corresponding date on the neighboring record must also be updated. This is where a problem could arise - by changing the end date on address #1 the application will also update the start date on address #2, which will then want to update the end date on address #1, which will then want to update the start date on address #2, and so, ad infinitum, ad nauseam. By setting this switch before the first update to record #2 any possible loop is stopped in its tracks - the update to record #2 is allowed to happen, but any custom code which would cause it to update record #1 is deliberately ignored.

$this->sql_from (string)

This string is used as the FROM clause in the sql SELECT statement which is constructed in _dml_getData().

It may contain anything which is valid in the FROM clause, including JOINs with other tables.

If left blank it will default to $this->tablename.

$this->sql_groupby (string)

This string is used as the GROUP BY clause in the sql SELECT statement which is constructed in _dml_getData().

It may contain anything which is valid in the GROUP BY clause.

$this->sql_having (string)

This string is used as the HAVING clause in the sql SELECT statement which is constructed in _dml_getData().

It may contain anything which is valid in the HAVING clause.

$this->sql_no_foreign_db (boolean)

If set to TRUE this will prevent the $this->_sqlForeignJoin() method from including any table which belongs in a different database.

This is to deal with the situation where an application contains several databases which are spit across more than one server, in which case cross-database JOINs would cause an SQL error.

$this->sql_orderby (string)

This string is used as the ORDER BY clause in the sql SELECT statement which is constructed in _dml_getData().

It may contain anything which is valid in the ORDER BY clause.

$this->sql_orderby_seq (string)

This is used to indicate if the current ORDER BY clause is asc (ascending) or desc (descending) so that it can be toggled from one to the other when a hyperlink in the column headings is selected.

$this->sql_orderby_table (string)

This string identifies the table name to be used for qualifying field names in the $this->sql_orderby clause. This should only need to be used when an unqualified field name causes an error, and the default processing cannot qualify it because the field does not exist in the current table.

$this->sql_search (string)

A string containing additional selection criteria which will be added to the contents of the $where string when the sql SELECT statement is constructed in _dml_getData().

The string may come from a SEARCH screen, the 'Selection (temporary)' column on the MNU_TASK record, or may be added by code in a custom method.

The string may be modified during program execution, but the original values will be available in $this->sql_search_orig

This string will be cleared if the RESET button on the action bar is pressed.

$this->sql_search_orig (string)

This is a duplicate of $this->sql_search and is available should the contents be changed during program execution. For example, the original search string may contain something like:

prod_cat_id != '67'
which needs to be changed to something like the following before it can be successfully executed:
IF NOT EXISTS (SELECT .....)
$this->sql_search_table (string)

This string identifies the table name to be used for search criteria when it is different from the current table. This is to ensure that the correct table name can be used if the field names in the WHERE clause need to be qualified.

This is used, for example, in LINK1 screens where the associated SEARCH screen shows the contents of the INNER table, but where the LINK1 transaction itself does not contain an object for the INNER table. It is instead accessed from within the LINK table which then JOINs to the INNER table.

The format of this string can be either tablename or tablename AS alias.

$this->sql_select (string)

This string is used as the SELECT clause in the SQL query which is constructed in _dml_getData().

It may contain anything which is valid in the SELECT clause.

If left blank it will default to '*' to signify all fields.

$this->sql_where (string)

This is a piece of fixed selection criteria that will be appended to whatever is supplied in $where, which is variable.

For example, the TASK table in the MENU database contains a field called TASK_TYPE which has two values, PROC and MENU. By default any transaction which queries this table will retrieve entries of both types, but it may be a requirement to have one task that will retrieve nothing but PROC entries and another task which retrieves nothing but MENU entries. This can be achieved by creating two scripts which specify different $sql_where values, such as:

A similar effect can be created by having one script which is accessed by two separate entries on the TASK table, each of which has the relevant selection criteria specified in the 'Selection (fixed)' field.

$this->tablename (string)

This string identifies the name which will be used as the default table name in all SQL queries which are constructed and issued.

$this->unique_keys (array)

This array identifies any candidate (unique) keys that may have been defined for the table in addition to the primary key. Note that there may be more than one unique key, and each key may be comprised of more tan one field.

This information is automatically obtained when the table structure is imported into the Data Dictionary. When exported this will produce the following structure.

$this->wf_case_id (integer)

This is set by _examineWorkflowInstance() if the current combination of task_id and context is recognised as being an outstanding workitem in an ongoing workflow case. It is then used in _examineWorkflow() to signify that the workitem needs to have its status updated to 'finished'.

$this->wf_workitem_id (integer)

Refer to $this->wf_case_id.

$this->where (string)

This string is used as the WHERE clause in any SQL query which is constructed and issued. It is primarily passed down from a parent form to a child form when a selection is made in the parent form.

If any fixed selection criteria needs to be added to this variable selection criteria, it can be specified in $this->sql_where.

$this->xsl_params (array)

This is an associative array which is used to pass additional values to the XSL transformation process by way of the XML document. Entries will be ignored unless the XSL stylesheet is programmed to deal with them.

The following options are valid for any screen:

    $this->xsl_params['noselect']     = 'y';  // remove 'select all/unselect all ' hyperlinks
    $this->xsl_params['noshow']       = 'y';  // remove 'show 10/show 25/...' hyperlinks
    $this->xsl_params['nosort']       = 'y';  // remove sort hyperlinks in column headings
    $this->xsl_params['outer_noedit'] = 'y';  // make 'outer' zone non-editable
    $this->xsl_params['inner_noedit'] = 'y';  // make 'inner' zone non-editable
    $this->xsl_params['main_noedit']  = 'y';  // make 'main' zone non-editable

Here is an example of options which are available in std.timetable1.xsl which is demonstrated in the Classroom Scheduling prototype. The following details are supplied via $xsl_params as they are not available within the main data:

    $this->xsl_params['start_time']  = $data[0]['start_time'];
    $this->xsl_params['end_time']    = $data[0]['end_time'];
    $this->xsl_params['last_day_no'] = $data[0]['last_day_no'];

Singleton class

This implements the Singleton design pattern by using a single getInstance() method in a single RDCsingleton class instead of the usual duplicated getInstance() method within every class in the system. This approach has several advantages:

Using it is very simple - just supply the name of the class you want as the argument, as in:

$object =& RDCsingleton::getInstance('<name>');

Where <name> can be one of the following:

To make this work the 'includes' directory should be in your include_path. If you have more than one subsystem, and a task may need to access classes from more than one subsystem at a time, then each '<subsystem>' directory needs to be in the include_path as well.

NOTE: This class used to be called simply 'singleton', but I had to change it to 'RDCsingleton' when trying to integrate some of my code into a website that was built using a front-end framework. The authors of this framework had created an interface called singleton without following the naming convention of using an 'i' prefix to make it iSingleton. Due to an oversight/bug in PHP it was not possible to have an interface and a class with the same name, and as the authors of this framework refused to change their crappy code I had no option but to change my beautiful code.


Non OO functions

The following functions are procedural, which means that they were not defined as methods within a class.

$date = adjustDate ($date, $adjustment, $units='days')
$date (IN) is a valid date which can be in a variety of formats.
$adjustment is an integer which identifies the adjustment to be made, in units, such as '+3' or '-1'.
$units is a string which identifies the units as either 'days' (the default), 'weeks' or 'months'.
$date (OUT) will contain the adjusted date in the format 'YYYY-MM-DD'.

This function is used to add a number of units (days,weeks or months), which may be a negative number, to the given date.

If the units are 'days' or 'weeks' it will use functions GregorianToJD and JDtoGregorian, so it will be able to handle any year between 4714 BC to 9999 AD.

If the units are 'months' it will use the strtotime function, which means that it can only handle dates between 1st January 1970 and 19th January 2038 due to the limitations of 32 bit integers.

The $date (IN) argument will be processed by getInternalDate() to verify that it is a valid date.

Here is an example which sets a date to the following day and the previous day:

$rowdata['start_date'] = adjustDate($rowdata['start_date'], +1);
$rowdata['start_date'] = adjustDate($rowdata['start_date'], -1);
$date = add_days_to_date ($date, $days, $exclude_saturday=false, $exclude_sunday=false, $exclude_bank_holiday=false)
$date (IN) is a valid date which must be in the format CCYY-MM-DD.
$days is an integer which identifies the number of days to be added to $date.
$exclude_saturday is a boolean which identifies if Saturdays are to be excluded.
$exclude_sunday is a boolean which identifies if Sundays are to be excluded.
$exclude_bank_holiday is a boolean which identifies if UK bank holidays are to be excluded.
$date (OUT) will contain the adjusted date in the format 'YYYY-MM-DD'.

This function is used to add a number of days to the given date. If non-working days are to be excluded then the boolean arguments may be set to TRUE

Here is an example used in an order processing system which adds a product's lead_time to the order date in order to provide the expected delivery date:

$due_date = add_days_to_date ($order_date, $lead_time, true, true, true);
$datetime = adjustDateTime ($datetime, $adjustment)
$datetime (IN) can be a unix timestamp, or a string in the format 'YYYY-MM-DD HH:MM:SS' which will be converted into a unix timestamp.
$adjustment is a string which identifies the adjustment to be made, such as '+3 hours' or '-1 week'.
$datetime (OUT) will contain the adjusted date/time in the format 'YYYY-MM-DD HH:MM:SS'.

This is used to take a starting $datetime and to adjust it by a given period which should be in a format acceptable to the strtotime function, which means that it can only handle years between 1970 and 2038.

Here is an example which computes the date/time which an item expires:

$rowdata['deadline'] = adjustDateTime(time(), "+{$arc_data['time_limit']} hours");
$time = adjustTime ($time, $adjustment)
$time (IN) is a string in the format 'HH:MM:SS'.
$adjustment is a string which identifies the adjustment to be made, such as '+30 seconds' or '-1 hour'.
$time (OUT) will contain the adjusted time in the format 'HH:MM:SS'.

This is used to take a starting $time and to adjust it by a given period which should be in a format acceptable to the strtotime function.

Here is an example which can be used to identify items which are 5 minutes old:

$lookup_time = adjustTime($start_time, '-5 minutes');
void = append2ScriptSequence ($array, $prepend=false)
$array is an associative array of task details.
$prepend is an optional boolean switch which defaults to FALSE.

This is used to either append or prepend an entry to the $_SESSION['script_sequence'] array in order to identify a task which is processed automatically when the current script finishes, which is determined by a call to either the $this->scriptNext() or the $this->scriptPrevious() method.

$next['task_id']  = 'mnu_user(upd1)b';
$next['where']    = "user_id='$logon_user_id'";
$next['action']   = 'OK';
$next['messages'] = getLanguageText('sys0146');  // 'You must change your password'
append2ScriptSequence($next);

For a description of the entries which can be placed in the $next array please refer to $_SESSION['script_sequence'].

Note also that any number of $next entries can be added to the $_SESSION['script_sequence'] array, and they will be processed in the order in which they appear.

If $prepend is set to TRUE it will cause the task details to be prepended, not appended, to the $_SESSION['script_sequence'] array. This is used by the Workflow engine to force AUTO tasks to the head of the queue so that they are executed before any non-workflow tasks.

$range = array2range ($array)
$array is an indexed array of one or more database rows, where each row is an associative array containing one selected value.
$range will be a string containing those primary key values which can be used in a WHERE clause.

This is used to take an array of values and construct a string which is suitable for use in a WHERE clause in the format:

item_id IN ($range)

Here is an example which starts with a particular product category (office supplies) and examines the hierarchy looking for sub-categories (pens and paper) to construct a list of all related categories all the way down to the bottom of the hierarchy:

$range1 = "'OFFICE'";  // starting category
$range2 = $range1;
$dbobject->sql_select = 'prod_cat_id_jnr';
// repeat until end of hierarchy is reached
do {
    // look for children of current range
    $array2 = $dbobject->getData_raw("prod_cat_id_snr IN ($range2)");
        if (!empty($array2)) {
            // convert array of rows into a range string
            $range2  = array2range($array2);
            // append to whole range string
            $range1 .= ",$range2";
        } // if
    } while (!empty($array2));

// add complete range string to array
$array['prod_cat_id'] = "IN ($range1)";

At the end of this loop the string $range1 will contain 'OFFICE','PEN','PAPER'.

$where = array2where ($array, $fieldlist=null, $dbobject=null)
$array is an associative array of name=value pairs which may or may not be indexed by a row number.
$fieldlist (optional) is an array of field names which are to be included in the output string. If empty then all fields in $array will be included.
$dbobject (optional) is a database object for calling unFormatData() in order to undo the effects of formatData().
$where will contain he generated string.

This is used to convert an array, which may contain an associative array for a single row, or an indexed array for multiple rows, and produce a string suitable for use as the WHERE cause in an SQL query.

For each row it will construct a string in the format:

(field1='value',field2='value',...)

If there are multiple rows they will be separated by ' OR ', as in:

(field1='value',field2='value',...) OR (field1='value',field2='value',...) OR (...)

If the $where string is to be limited to only those fields which form the primary key then the calling sequence should be something like this:

$where = array2where ($rowdata, $this->getPkeyNames());

This is the opposite of where2array().

void addJump ($jump_from, $jump_to)
$jump_from is the start of a jump point.
$jump_to is the end of a jump point.

This is used to create a jump point in an array created by setScrollArray. A jump point is used to signify that a sequence of entries in the array is to be regarded as "off limits" or "invisible" so that scrolling through the array will totally ignore all those entries which exist between $jump_from and $jump_to.

An example of this is to be found in the Survey/Questionnaire prototype where a particular answer to a particular question may require the remaining questions in that section to be irrelevant, in which case they become "invisible" and the user is taken automatically to the question at the beginning of the next section.

$errors = checkSelection ($task_id, $where=null)
$task_id is the identity of the selected task.
$where is an optional WHERE string.
$errors is an array of error messages.

This function is used by the framework to check the following:

This will then perform the following:

If any errors are found they will be returned in $errors.

void childform ($post, $dbobject, $objectname, $where=null)
$post is the $_POST array.
$dbobject is the name of the current object in case a call to $dbobject->popupcall() is required.
$objectname is a string containing the object's zone identity in the controller, such as "main", "outer" or "inner".
$where is an optional WHERE string.

This is called whenever the POST method is used in order to look for a SUBMIT button which contains the identity of another task, such as from a navigation button or a popup button. If no task has been requested then the function returns control immediately, otherwise it passes control to the selected task by calling scriptNext(). If the requested task has a type of "popup" or "filepicker" then $dbobject->popupcall() will be called beforehand in case the contents of $where needs to be adjusted.

$messages = choosebutton ($postarray, $dbobject)
$postarray is the $_POST array.
$dbobject is the object for the current database table.
messages will contain any messages to be displayed in the current screen.

This is called whenever the CHOOSE button is pressed in a POPUP form. It will perform the following actions:

If nothing has been selected then a message will be returned instead.

void clearJump ($index)
$index is the start of an existing jump point.

This is used to remove a jump point which was previously created by addJump when the condition which caused it to be created no longer exists.

$string = convertEncoding ($string, $to_encoding, $from_encoding=null)
$string (IN) is the string before any conversion.
$string (OUT) is the string after conversion.
$from_encoding is the current encoding of the input string.
$to_encoding is the encoding of the output string.

This function will convert the character encoding of a string. Note that it can only function if the Multibyte String Functions have been included in your PHP installation.

If $from_encoding is not supplied it will be detected automatically using the mb_detect_encoding function.

$datetime = convertTZ ($datetime, $tz_from, $tz_to)
$datetime (IN) is the datetime before the conversion.
$datetime (OUT) is the datetime after the conversion.
$tz_from is the timezone from which the input string is to be converted.
$tz_to is the timezone to which the input string is to be converted.

This function will only be available if the PHP version is 5.2 or greater.

This will convert a datetime value from one timezone to another. The two usual timezone values will be made available in $_SESSION['timezone_server'] and $_SESSION['timezone_client']. For more details please refer to FAQ125.

$date = convertTZdate ($date, $time, $tz_from, $tz_to)
$date (IN) is the date before the conversion.
$date (OUT) is the date after the conversion.
$time is the associated time (if empty the current time will be used).
$tz_from is the timezone from which the input string is to be converted.
$tz_to is the timezone to which the input string is to be converted.

This will use the convertTZ() function to convert a date from one timezone to another. If an associated time is not supplied the current server time will be used.

Here is an example where the date and time values are held in separate fields:

    if (isset($_SESSION['timezone_server']) AND isset($_SESSION['timezone_client'])) {
        $dateobj =& RDCsingleton::getInstance('date_class');
        // combine 'date' and 'time' fields before performing timezone conversion
        $date = $fieldarray['logon_date'];
        $time = $fieldarray['logon_time'];
        $fieldarray['logon_date'] = convertTZdate($date, $time, $_SESSION['timezone_server'],
                                                                $_SESSION['timezone_client']);
        $fieldarray['logon_date'] = $dateobj->getExternalDate($fieldarray['logon_date']);
        $fieldarray['logon_time'] = convertTZtime($date, $time, $_SESSION['timezone_server'],
                                                                $_SESSION['timezone_client']);
    } // if

Here is an example where there is only the date field:

    $date = date('Y-m-d');
    if (isset($_SESSION['timezone_server']) AND isset($_SESSION['timezone_client'])) {
        $date = convertTZdate($date, null, $_SESSION['timezone_server'], 
                                           $_SESSION['timezone_client']);
    } // if
$time = convertTZtime ($date, $time, $tz_from, $tz_to)
$date is the associated date (if empty the current date will be used).
$time (IN) is the time before the conversion.
$time (OUT) is the time after the conversion.
$tz_from is the timezone from which the input string is to be converted.
$tz_to is the timezone to which the input string is to be converted.

This will use the convertTZ() function to convert a time from one timezone to another. If an associated date is not supplied the current server date will be used.

Here is a example of its use:

    $time = date('H:i:s');
    if (isset($_SESSION['timezone_server']) AND isset($_SESSION['timezone_client'])) {
        $time = convertTZdate(null, $time, $_SESSION['timezone_server'], 
                                           $_SESSION['timezone_client']);
    } // if
$msg = copyform ($post, $dbobject)
$post is the $_POST array.
$dbobject is the current database table object.
$msg will be an informative message.

This will merge the contents of $post with $dbobject->fieldarray, then save it in the $_SESSION array so that it can be used in a PASTE operation in another form on the same table. Note that a PASTE button will only be shown if data has previously been copied.

$msg will either contain "There is no data to copy" or "Data has been copied".

$string = currentOrHistoric ($string, $start_date='start_date', $end_date='end_date')
$string (IN) is a string containing curr_or_hist='C/H/F'.
$start_date is the name of the field which represents the start date.
$end_date is the name of the field which represents the end date.
$string (OUT) contains a usable replacement for curr_or_hist='C/H/F'.

When utilising the feature described in How can I search for records with historic, current or future dates? the search criteria which is returned by a SEARCH screen will contain a reference to a dummy field called curr_or_hist. Because this is a dummy field it does not exist in the database, therefore including it in an SQL statement will cause a fatal error. This function will therefore translate the curr_or_hist='C/H/F' string into something which is usable.

If the field names that you use are called start_date and end_date then you do not need to do anything as the following code will be executed automatically by the framework before the _dml_getData() method is called:

    if (!empty($this->sql_search)) {
        // turn 'current/historic/future' into a range of dates
        $this->sql_search = currentOrHistoric($this->sql_search);
    } // if
If you use different field names then you will have to call this function manually, as in:
function _cm_pre_getData ($where, $where_array, $fieldarray=null)
{
    if (!empty($this->sql_search)) {
        // turn 'current/historic/future' into a range of dates
        $this->sql_search = currentOrHistoric($this->sql_search, 'available_from_date', 'available_to_date');
    } // if
    
    return $where;
    
} // _cm_pre_getData
$diff = dateDifference ($date1, $date2)
$date1 is the first date.
$date2 is the second date.
$diff is the difference expressed in days.

This will subtract $date1 from $date2 and give the difference in days. Note that the result will be negative if $date2 is earlier than $date1.

$dbname = findDBName ($target_db, $this_db=null)
$target_db is the database name as known in the data dictionary.
$this_db (optional) is the database name of the current table object.
$dbname is actual name on the database server.

This is designed to be used in the _cm_pre_getData() method when defining a JOIN to a table which exists in another database, in which case the table name must be qualified with the database name. It is possible for a database to have one name on the development server (where the Data Dictionary is used) and a different name when it is moved to another server. The name may be altered in one of two ways:

This function will return the correct name for that database on the current server which will be enclosed in double quotes (the ANSI standard) and followed by a period. Here is some sample code:

function _cm_pre_getData ($where, $where_array, $fieldarray=null)
{
    // find out if tables in the 'party' database need to be qualified
    $partyDB = findDBName('party');

    if (empty($this->sql_select)) {
        // include columns from foreign table
        $this->sql_select = 'order_type, order_id, order_date, order_value';
        $this->sql_from   = 'order_header '
                          . 'LEFT JOIN '.$partyDB.'party ON (party.party_id=order_header.party_id) ';
    } // if

    return $where;

} // _cm_pre_getData

The optional 2nd argument should only be used in those cases where there is a possibility that $target_db, while being different on the development server, could be merged with the current database on a different server. For example, the RADICORE framework has four databases - MENU, DICT, AUDIT and WORKFLOW - which some users want to have merged into one (see FAQ150 for details). This is handled in the RADICORE classes using code such as the following:

function _cm_pre_getData ($where, $where_array, $fieldarray=null)
{
    // find out if tables in the 'menu' database need to be qualified
    $menuDB = findDBName('menu', $this->dbname);

    if (empty($this->sql_select)) {
        // include columns from foreign table
        $this->sql_select = 'rdcaccount_id, user_id, role_id, wf_workitem.workflow_id, case_id, workitem_id, transition_id'
                         .', wf_workitem.task_id, transition_trigger, workitem_status, enabled_date, cancelled_date, finished_date'
                         .', deadline, context, workflow_name, task_desc';
        $this->sql_from   = 'wf_workitem '
                          . 'LEFT JOIN wf_workflow ON (wf_workflow.workflow_id=wf_workitem.workflow_id) '
                          . 'LEFT JOIN '.$menuDB.'mnu_task ON (mnu_task.task_id=wf_workitem.task_id) ';
    } // if

    return $where;

} // _cm_pre_getData

If the two databases are the same then $menuDB will be empty, otherwise it will contain the name of the MENU database as it is defined on the current server.

$index = findJump ($next_index, $curr_index)
$next_index is the index to be tested.
$curr_index is the current valid index.
$index is the valid index to be used.

This will examine the array created by setScrollArray to see if $next_index falls between a pair of jump points. If it does not then it will be returned unchanged, otherwise it will be adjusted as follows:

A jump point is created using addJump and deleted using clearJump.

$language = getBrowserLanguage ($directory)
$directory is the container for one or more language subdirectories.
$language is the first subdirectory which matches the user's language settings in his browser.

This function will identify the first language from the client's preferences in the browser settings which matches the languages which are actually supported. It does this using the following steps:

  1. Construct a list of subdirectories which exist under $directory.
  2. Step through the array of browser languages in $_SESSION['user_language_array'] and stop when the first match is found in the array produced in step (1).

This is used in the getLanguageFile() function.

$result = getBrowserVersion ()
$result is a string which identifies the browser name and version number.

This function will identify the name and version number of the user's browser, such as 'ie7', 'ie8', etc. This is used in the option to include browser-specific CSS files.

$headings = getColumnHeadings ()

DEPRECATED - please use replaceScreenHeadings() instead.

$method = getEntryPoint ($object)
$object is an object, usually $this for the current object.
$method is a string that will contain a method name.

This is used to identify the first entry point into the specified object (usually the current object) in the current operation. There may be several routes into a particular method, so this will identify the current route.

$extdate = $dateobj->getExternalDate ($indate, $date_format=null)
$indate is a date in the format 'YYYY-MM-DD' or 'YYYYMMDD'.
$date_format can be used to override the global setting in the config.inc file.
$extdate will contain the date in external format.

This is used to convert a date format from internal (as used internally by the software) to external (as seen by the user) in the format specified by $GLOBALS['date_format'] in the CONFIG.INC file. Note that this value can be overridden for the current user's language on the MNU_LANGUAGE table.

Here is an example of how it can be used:

    $dateobj =& RDCsingleton::getInstance('date_class');
    $output = $dateobj->getExternalDate($input);

$this->errors will contain any errors.

$indate = $dateobj->getInternalDate ($extdate, $date_format=null)
$extdate is a date which may have been entered by the user.
$date_format can be used to override the global setting in the config.inc file.
$indate will contain the date in the format 'YYYY-MM-DD'.

This is used to take an input string, which may have just been entered by the user in a variety of formats, and convert it to a format which is suitable for writing to the database.

A variety of input formats are allowed, as specified in FAQ113. Note that this value can be overridden for the current user's language on the MNU_LANGUAGE table.

Here is an example of how it can be used:

    $dateobj =& RDCsingleton::getInstance('date_class');
    $output = $dateobj->getInternalDate($input);

$this->errors will contain any errors.

$array = getLanguageArray ($id)
$id is the identity of an entry in a language array file.
$array will contain the associative array for $id.

Whenever an array of values is required, such as for a dropdown list or radio group, this will obtain the contents of that array from a separate language file which is located using the getLanguageFile() function. This allows the contents of the array to be delivered in the user's preferred language.

This is part of the feature documented in Internationalisation and the Radicore Development Infrastructure.

Each subsystem exists in its own directory and has its own language files, so when a script uses an object in a different directory to perform validation it will need to set $GLOBALS['classdir'] so that entries can be retrieved from the correct language file in the correct directory.

$string = getLanguageFile ($filename, $directory, $ignore_if_not_found=false)
$filename is the name of the file whose existence will be checked.
$directory is the directory which contains a separate subdirectory for each supported language.
$ignore_if_not_found is a TRUE/FALSE switch.
$string will identify the path to the file in the format <directory>/<language>/<filename>.

This will check the existence of $filename in one of the language subdirectories which exists below $directory. It will cycle through the possible languages and stop when the first one is found.

$filename will typically be either [sys.]language_text.inc or [sys.]language_array.inc.

$directory will typically be either ./text or ../menu/text.

If $ignore_if_not_found is set to FALSE the function will abort with a suitable error message if the named file cannot be found in any of the language subdirectories.

The possible language codes will be searched in the following order:

This is used by getLanguageArray and getLanguageText.

$string = getLanguageText ($id, $arg1=null, $arg2=null, $arg3=null, $arg4=null, $arg5=null)
$id is the identity of an entry in a language text file
$arg1/2//3/4/5 is a list of optional arguments.
$string will contain the text for $id.

This will obtain the specified piece of text from the language file, insert any optional arguments, then return the resulting text string. Up to five optional arguments can be supplied, and they will be inserted into the string at the designated points (%n\$s, where n is a number between 1 and 5). The language file is located using the getLanguageFile() function

This is part of the feature documented in Internationalisation and the Radicore Development Infrastructure.

Each subsystem exists in its own directory and has its own language files, so when a script uses an object in a different directory to perform validation it will need to set $GLOBALS['classdir'] so that entries can be retrieved from the correct language file in the correct directory.

$string = getNewSession ($prefix='menu')
$prefix is the session name to which a numeric suffix will be added.
$string will contain the generated session name.

Whenever the new session hyperlink is pressed in the top row of the menu bar this will construct a session name as $prefix followed by a number (starting at 1) and look to see if that name already exists in the $_COOKIE array. If that name exists the number will be incremented (up to 99), otherwise the unused name will be returned.

$dir = getParentDIR ()
$dir will contain the name of the parent directory to the current script.

The directory structure used by RADICORE puts each subsystem into its own subdirectory, which means that script names, relative to the document root, will be in the format /parent/subdir/script.php.

This function will return the name of the parent directory, i.e. everything in front of /subdir/script.php. Note that this may be empty ('/' is not returned if there are no parent directories), or it may contain a hierarchy of several parent directories, such as /grandparent/parent.

Here is an example which constructs a file name used in an export function:

$fname = $_SERVER['DOCUMENT_ROOT'] 
        .getParentDIR() .'/' .$subdir .'/sql/export.sql';
$string = getPatternId ($script_id=null)
$script_id is a string which will default to the current script.
$string will contain the pattern_id of the specified script.

This will look in the $_SESSION[$script_id] array, then return the value for pattern_id, which identifies the pattern type of the specified task as recorded on the MNU_PATTERN table.

$string = getPreviousPattern ($task_id=null)
$task_id is a string which will default to the current task.
$string will contain the pattern_id of the previous task.

This will find where $task_id exists in the $GLOBALS['page_stack'] array, then return the pattern_id for the previous entry.

$string = getPreviousScript ($task_id=null)
$task_id is a string which will default to the current task.
$string will contain the script_id of the previous task.

This will find where $task_id exists in the $GLOBALS['page_stack'] array, then return the script_id for the previous entry.

$string = getPreviousTask ($script_id=null)
$script_id is a string which will default to the identity of the previous script (see getPreviousScript).
$string will contain the task_id associated with $script_id.

The same script may be shared by more than one task, and this function will return the task_id which was last used with the specified script.

$self = getSelf ()
$self will contain the name of the current script in the format /subdir/script.php.

This function will return the name of the current script but will exclude any parent directories identified by getParentDIR().

This is used instead of $_SERVER['PHP_SELF'] which will include all parent directories up to the web root.

$original = getTableAlias1 ($alias, $from_string)
$alias is a string containing a table's alias name.
$from_string is the FROM clause in an sql SELECT statement which may contain any number of 'table AS alias' substrings.
$original will contain the original table name from the qualifying 'table AS alias' substring, or NULL.

This function will examine $from_string and look for an occurrence of 'table AS alias' where 'alias' matches $alias and return the 'table' portion in $original. For example, if $from_string contains 'x_tree_level AS t2' and $alias is supplied as 't2' then the value 'x_tree_level' will be returned in $original.

If $alias is not found then $original will be NULL.

$alias = getTableAlias2 ($original, $from_string)
$original is a string containing a table name.
$from_string is the FROM clause in an sql SELECT statement which may contain any number of 'table AS alias' substrings.
$alias will contain the table's alias name from the qualifying 'table AS alias' substring, or NULL.

This function will examine $from_string and look for an occurrence of 'table AS alias' where 'table' matches $original and return the 'alias' portion in $alias. For example, if $from_string contains 'x_tree_level AS t2' and $original is supplied as 'x_tree_level' then the value 't2' will be returned in $alias.

If $original is not found then $alias will be NULL.

list($original, $alias) = getTableAlias3 ($string)
$string is a string which either contains 'table' or 'table AS alias'.
$original will contain the original table name, or NULL.
$alias will contain the table's alias name, or NULL.

This function examines $string looking for the pattern '<word1> AS <word2>' and if found then '<word1>' will be returned in $original and '<word2>' will be returned in $alias.

If the pattern is not found then both $original and $alias will be NULL.

$timestamp = getTimeStamp ($type=null)
$type (optional) is a string containing either 'date' or 'time'. Empty will mean both.
$timestamp will contain the requested datestamp or timestamp.

This function will return the current timestamp in various formats depending on $type:

void initSession ()

This will perform all initialisation for each script, which includes retrieving data from the $_SESSION array.

The contents of $GLOBALS['script_vars'] will be obtained from $_SESSION[$script_id]. If no entry is found then it means that the requested script is invalid, so control will be passed to the last entry in $GLOBALS['page_stack'] instead.

The contents of $GLOBALS['page_stack'] may be modified by calling one of the following:

boolean isDirectoryValid ($dir)
$dir is the directory name to be checked.

This will check that the directory $dir exists within the open_basedir path. If open_basedir is defined and $dir does not exist with the path(s) defined then it will return FALSE.

Here is some sample code:

$include_path = ini_get('include_path');
if (isDirectoryValid('../radicore/menu')) {
    $include_path .= PATH_SEPARATOR .realpath('../radicore/menu');
    $include_path .= PATH_SEPARATOR .realpath('../radicore/audit');
    $include_path .= PATH_SEPARATOR .realpath('../radicore/workflow');
		
} elseif (isDirectoryValid('../../radicore/menu')) {
    $include_path .= PATH_SEPARATOR .realpath('../../radicore/menu');
    $include_path .= PATH_SEPARATOR .realpath('../../radicore/audit');
    $include_path .= PATH_SEPARATOR .realpath('../../radicore/workflow');

} elseif (isDirectoryValid('../../../radicore/menu')) {
    $include_path .= PATH_SEPARATOR .realpath('../../../radicore/menu');
    $include_path .= PATH_SEPARATOR .realpath('../../../radicore/audit');
    $include_path .= PATH_SEPARATOR .realpath('../../../radicore/workflow');
} // if
ini_set('include_path', $include_path);
$errors = isPkeyComplete ($rowdata, $PkeyNames)
$rowdata can be either a WHERE string or an associate array containing the details of a single database occurrence.
$PkeyNames is an indexed array which identifies all the fields which comprise the primary key.
$errors may be indexed or associative.

This will check that the data in $rowdata contains an entry for each of the primary key fields identified in $PkeyNames. If any field is missing an error message will be generated.

boolean = isPrimaryObject ($object)
$object can be either $this to specify the current object, or a string containing the class name of another object.

This will identify whether the specified object was called directly from a controller or not.

This can be useful in circumstances where some custom processing needs to be performed if the current object was called from a controller, but ignored if the current object was called from a different object.

$output = rangeFromTo ($from, $to, $is_date=FALSE)
$from is the lowest value in the range.
$to is the highest value in the range.
$is_date is an optional switch to signify if each value is a date.
$output is a string which can be used in an sql SELECT statement.

By default when selection criteria is solicited on a SEARCH screen there is a single control for each field, but in some situations there may be a need to specify a range of values, in which case two fields would be needed in order to specify the lowest and highest values in the range. However, these two input fields need to be combined into a single expression before being used in an sql SELECT statement:

If the $is_date switch is set to TRUE then:

Here is an example of its use:

    if (!empty($fieldarray['date_from']) OR !empty($fieldarray['date_to'])) {
        $fieldarray['err_timestamp'] = rangeFromTo($fieldarray['date_from'], $fieldarray['date_to'], true);
        unset($fieldarray['date_from']);
        unset($fieldarray['date_to']);
    } // if
$array = reducePageStack ($task_id, $keep_entry=FALSE)
$task_id is the identity of a task (user transaction).
$keep_entry is an optional switch.
$array is the updated page stack.

This is used to update the contents of $GLOBALS['page_stack'] by removing any entries which follow $task_id.

If $keep_entry is TRUE the entry for $task_id will be kept in the array, otherwise it will be deleted as well.

boolean = replaceReportHeadings ($array)
$array is an associative array containing the field names with their replacement labels.

This is used to replace column headings in the report structure file at runtime. For example, take the report headings show below:

output-to-pdf-002 (9K)

This is produced with the following code in the report structure file:

// identify column names and associated labels
$structure['body']['fields'][] = array('subsys_id' => 'Subsys Id');
$structure['body']['fields'][] = array('subsys_desc' => 'Description');
$structure['body']['fields'][] = array('subsys_dir' => 'Directory');
$structure['body']['fields'][] = array('task_prefix' => 'Task Prefix');
$structure['body']['fields'][] = array('count' => 'Count');
then labels can be replaced at runtime with code similar to the following:
function _cm_pre_output ($string)
// perform any processing required before the output operation
{
    $replace['subsys_id']   = 'NEW subsys_id';
    $replace['subsys_desc'] = 'NEW subsys_desc';
    $replace['subsys_dir']  = 'NEW subsys_dir';

    $result = replaceReportHeadings($replace);
    
    return $string;

} // _cm_pre_output
boolean = replaceScreenColumns ($array)
$array is an associative array containing the field names with their replacement details.

This is used to replace columns and their headings in the screen structure file at runtime. This is for LIST screens which show multiple rows of data horizontally across the screen below a single row of column headings. For example, a screen contains a column called order_id, but under some circumstances this needs to be changed to a column called order_count.

The screen structure file contains the following:

$structure['inner']['fields'][] = array('country_name' => 'Country');
$structure['inner']['fields'][] = array('order_id' => 'Order Id');
$structure['inner']['fields'][] = array('order_value' => 'Goods Value');
$structure['inner']['fields'][] = array('order_adjustment' => 'Adjustments');
$structure['inner']['fields'][] = array('sales_tax' => 'Sales Tax');
$structure['inner']['fields'][] = array('surcharge' => 'Surcharges');
$structure['inner']['fields'][] = array('discount' => 'Discounts');

This can be amended at runtime with code similar to the following:

    if (is_True($group_by_country)) {
        $replace_array['order_id'] = array('order_count' => 'Count', 'nosort' => 'y');
        $result = replaceScreenColumns($replace_array);
    } // if

This has the effect of changing this:

replaceScreenColumns_1 (15K)

into this:

replaceScreenColumns_2 (15K)

How cool is that!

boolean = replaceScreenHeadings ($array)
$array is an associative array containing the field names with their replacement headings.

This is used to replace column headings in the screen structure file at runtime. This is for LIST screens which show multiple rows of data horizontally across the screen below a single row of column headings. In such screens it may sometimes be useful to change the column headings with values which are decided at runtime. For example, a timesheet entry program has columns labelled Day#1 to Day#7, but at runtime these can be changed to actual dates.

The screen structure file contains the following:

$structure['inner']['fields'][] = array('selectbox' => 'Select');
$structure['inner']['fields'][] = array('work_effort_id' => 'Work Effort');
$structure['inner']['fields'][] = array('day_1' => 'Day#1'); // Saturday
$structure['inner']['fields'][] = array('day_2' => 'Day#2'); // Sunday
$structure['inner']['fields'][] = array('day_3' => 'Day#3'); // Monday
$structure['inner']['fields'][] = array('day_4' => 'Day#4'); // Tuesday
$structure['inner']['fields'][] = array('day_5' => 'Day#5'); // Wednesday
$structure['inner']['fields'][] = array('day_6' => 'Day#6'); // Thursday
$structure['inner']['fields'][] = array('day_7' => 'Day#7'); // Friday
$structure['inner']['fields'][] = array('total' => 'Total');

This can be amended at runtime with code similar to the following:

function _cm_post_getData ($rows, &$where)
{
    $replace['day_3'] = 'Monday 7th April';
    $replace['day_4'] = 'Tuesday 8th April';
    $replace['day_5'] = 'Wednesday 9th April';
    $replace['day_6'] = 'Thursday 10th April';
    $replace['day_7'] = 'Friday 11th April';

    $result = replaceScreenHeadings($replace);
    
    return $rows;

} // _cm_post_getData

This has the effect of changing this:

setColumnAttributes (3K)

into this:

setColumnHeadings (4K)

How cool is that!

boolean = replaceScreenLabels ($array, $zone)
$array is an associative array containing the field names with their replacement labels.
$zone will default to 'main', but can be specified as either 'outer' or 'middle'.

This is used to replace column labels in the screen structure file at runtime. This is for DETAIL screens which show a single row of data vertically down the screen with labels on the left and values on the right. In such screens it may sometimes be useful to change the column headings with values which are decided at runtime. For example, a address entry program has columns labelled City/Town, County and Postcode, but at runtime these can be changed to different values for different countries.

The screen structure file contains the following:

$structure['main']['fields'][6][] = array('label' => 'City/Town');
$structure['main']['fields'][6][] = array('field' => 'city');
$structure['main']['fields'][7][] = array('county' => 'County');
$structure['main']['fields'][8][] = array('label' => 'Postcode');
$structure['main']['fields'][8][] = array('field' => 'postcode');

This can be amended at runtime with code similar to the following:

function _cm_getExtraData ($where, &$fieldarray)
{
    if ($fieldarray['country_id'] == 2) {  // USA
        $replace['county']   = 'State';
        $replace['postcode'] = 'Zip Code';
    } // if

    $result = replaceScreenLabels($replace);
    
    return $fieldarray;

} // _cm_post_getData

This has the effect of changing this:

replaceScreenLabels_1 (2K)

into this:

replaceScreenLabels_2 (2K)

How cool is that!

$message = resizeImage ($source, $destination, $width, $height)
$source is the path to an existing image file.
$destination is the directory where the resized image will be written.
$width is an integer which provides the width for the resized image.
$height is an integer which provides the height for the resized image.
$message will contain a string which indicates either the success or failure of this operation.

This is used to copy the source image to the destination directory with a new set of dimensions. The new file will be the same type (jpg, gif, png) as the original.

This function will be called automatically within the File Upload pattern if the object's resize_array is populated during the execution of the _cm_initialiseFileUpload() method.

void scriptNext ($task_id, $where=null, $selection=null, $task_array=null)
$task_id is the identity of the next task which is to be activated.
$where is the WHERE string which is to be passed to that task.
$selection is a selection string (created by selection2where) which is to be passed to that task.
$task_array is an associative array of details for this task (usually retrieved from the TASK table in the MENU database).

This is called to activate a new task, such as one which has been selected in a form via a navigation button or a popup button. This has the following effects:

This has the effect of suspending the current script and activating the next script. When the next script is activated it will find all the data it needs in the $_SESSION array. When it terminates via $this->scriptPrevious it will return control back to the current script.

Here is an example where a LIST screen, if currently empty, should jump automatically to an ADD screen:

function _cm_post_getData ($rowdata, &$where)
// perform custom processing after database record(s) are retrieved.
{
    if ($GLOBALS['mode'] == 'list') {
        if (empty($rowdata)) {
            switch ($GLOBALS['return_from']) {
                case 'order_item(add)':
                case 'order_item(del)':
                    // just returned from ADD or DELETE screen, so do nothing
                    break;

                default:
                    // no data, so jump immediately to the ADD screen
                    scriptNext('order_item(add)', $where);
            } // switch
        } // if
    } // if

    return $rowdata;

} // _cm_post_getData

NOTE: If the current task has changed the database then you should call the $this->scriptNext() method instead of the scriptNext() function in order to commit the current database transaction.

void scriptPrevious ($errors=null, $messages=null, $action=null, $instruction=null)
$errors is an array of error messages to be passed to the previous script.
$messages is an array of non-error messages to be passed to the previous script.
$action is a string such as "choose", "OK" or "quit".
$instruction is an array of instructions (see $this->instruction)

This is used to pass control back to the previous form in the current session when the current form terminates, such as when the user presses the CANCEL button, or when it has nothing left to process. The sequence of events is as follows:

When the previous script is re-activated (which then makes it the current script again) its $_SESSION data will contain any information passed back to it so it can take any necessary action.

void scriptRestart (&$object, $query_string=null)
$object is the current object instance (i.e. $this).
$query_string is an optional string to be appended to the generated URL.

After performing some processing it may be necessary to restart the current script, perhaps with the ability to jump to another database record. For example, in the Survey/Questionnaire prototype a particular answer to a particular question may require that the next group of questions be skipped over, so some method is necessary to force the transaction to restart from a point which may or may not be the next record in the current sequence. This can be done by issuing another URL via the header('location: ...') directive which contains a request (via the scrolling mechanism) to jump to a different record in the current sequence.

Here is some sample code:

    if (isset($jumpto_section_seq)) {
        // locate question to jump to
        $jump_to = .........;
    } else {
        // default is to jump to current question plus 1
        $jump_to = $this->scrollindex + 1;
    } // if

    // set query string to jump to next answer, then restart current script
    scriptRestart($this, "scrolling=$this->tablename&item=$jump_to");

This function will perform the following steps:

$where = selection2where ($pkeyarray, $selections)
$pkeyarray is an indexed array of row numbers, and each row contains an associative array of name=value pairs.
$selections is an array which identifies which row numbers have been selected.
$where will be a string in the format of a WHERE clause in an SQL query.

For each row in $pkeyarray which is marked as SELECTED in $selections this will take the associative array and convert it into a string, with multiple fields separated by ' AND '. The fields for each row will be enclosed in '(' and ')', and the string for each row will be separated by ' OR '. The resulting string will look something like:

(key1='A' AND key2='B') OR (key1='C' AND key2='D') OR (...) OR (...)

This is the opposite of splitWhereByRow.

boolean = setColumnAttributes ($zone, $input_data)
$zone is a string which identifies the zone in the screen structure
$input_data is an associative array which specifies which attribute(s) to add to which column(s)

In a screen which contains multiple rows in a horizontal arrangement, with a heading above each column, it may sometimes be useful to remove one or more columns from the screen at runtime. For example, a timesheet entry program has a separate column for each of the seven days in a week, but if Saturday and Sunday are not used then they should be removed from the screen.

The screen structure file contains the following:

$structure['inner']['fields'][] = array('selectbox' => 'Select');
$structure['inner']['fields'][] = array('work_effort_id' => 'Work Effort');
$structure['inner']['fields'][] = array('day_1' => 'Day#1'); // Saturday
$structure['inner']['fields'][] = array('day_2' => 'Day#2'); // Sunday
$structure['inner']['fields'][] = array('day_3' => 'Day#3'); // Monday
$structure['inner']['fields'][] = array('day_4' => 'Day#4'); // Tuesday
$structure['inner']['fields'][] = array('day_5' => 'Day#5'); // Wednesday
$structure['inner']['fields'][] = array('day_6' => 'Day#6'); // Thursday
$structure['inner']['fields'][] = array('day_7' => 'Day#7'); // Friday
$structure['inner']['fields'][] = array('total' => 'Total');

This can be amended at runtime to hide the 'Saturday' and 'Sunday' columns with code similar to the following:

    $attribute_array['day_1'] = array('nodisplay' => 'y');
    $attribute_array['day_2'] = array('nodisplay' => 'y');
    $result = setColumnAttributes('inner', $attribute_array); 

This will have the effect of changing this:

unsetColumnAttributes (3K)

to this:

setColumnAttributes (3K)

This operation can be reversed using the unsetColumnAttributes() function with the same $input_data.

This procedure is possible due to the fact that the contents of the screen structure file are read into memory at the start of each task, but not actioned until the end of the task.

void = setColumnHeadings ($headings)

DEPRECATED - please use replaceScreenHeadings() instead.

void = setCurrentOrHistoric ()

In order to utilise the feature described in How can I search for records with historic, current or future dates? it will first be necessary to add the relevant control to your SEARCH screen. This is done in three parts:

  1. Add the curr_or_hist field to your $fieldspec array so that it will be treated as an editable field.
  2. Define the options that will appear in the dropdown list.
  3. Identify where on the screen the control will be displayed.

Steps (1) and (2) are dealt with by this function which contains the following code:

function setCurrentOrHistoric ()
// this table contains fields START_DATE and END_DATE, so insert into search screen
// a dropdown list to select 'current', 'historic', 'future' or 'all' dates.
{

    // create array of options and and put into LOOKUP_DATA
    $array = getLanguageArray('curr_or_hist');
    $this->lookup_data['curr_or_hist'] = $array;

    // insert field into $fieldspec
    $this->fieldspec['curr_or_hist'] = array('type' => 'string',
                                             'control' => 'dropdown',
                                             'optionlist' => 'curr_or_hist');
    return;

} // setCurrentOrHistoric

The contents of $array will be as follows:

If your database table contains fields called start_date and end_date this function will be called automatically by the framework during the initialisation phase of a SEARCH screen. If you use different field names the you will have to call this function manually.

Step (3) will require the following line of code somewhere in your screen structure file:

$structure['main']['fields'][] = array('curr_or_hist' => 'Date Range');

Please refer to currentOrHistoric() to see how the selection made using this control is translated into selection criteria which is valid in an SQL statement.

$array = splitWhereByRow ($where)
$where is a string in the format of a WHERE clause in an SQL query.
$array will contain the details within that string converted into an array.

This will take a string created by selection2where and convert it back into an array, indexed by row number at the top level and containing an associative array of name=value pairs at the next level. Note here that the string ' OR ' is used to separate one row from another, and the data enclosed by '(' and ')' will be converted into an associative array.

$array = unqualifyFieldArray ($array)
$array (IN) is an associative array of name=value pairs.
$array (OUT) is the input array after all names have been unqualified.

There may be occasions when the field names within an associative array have been qualified with their table names (as in tablename.fieldname instead of just fieldname), which means that looking for a particular field name becomes more complex as it must be correctly qualified before a match can be found. The easiest solution would be to strip off the 'tablename.' prefix within each name, thereby making the table name irrelevant.

boolean = unsetColumnAttributes ($zone, $input_data)
$zone is a string which identifies the zone in the screen structure
$input_data is an associative array which specifies which attribute(s) to remove from which column(s)

This removes the attributes which were set by the setColumnAttributes() function.

Here is some sample code:

    $attribute_array['day_1'] = array('noedit' => 'y');
    $attribute_array['day_2'] = array('noedit' => 'y');
    if (condition) {
        $result = setColumnAttributes('inner', $attribute_array);
    } else {
        $result = unsetColumnAttributes('inner', $attribute_array);
    } // if
void updatePageStack ($task_id, $task_desc, $script_id)

This is used to update the contents of $GLOBALS['page_stack'] by adding in an entry for $task_id.

$array = updateScriptVars ($script_vars=null)

This is used to update the contents of $GLOBALS['script_vars'] as follows:

The updated array is then returned.

$array = where2array ($where, $pageno=null, $strip_operators=true)
$where is a string in the format produced by array2where().
$pageno, if set, will cause a multi-row entry to be limited to that row.
$strip_operators, if set (the default), will cause any operators such as 'LIKE' to be stripped and replaced with '='
$array will contain the contents of $where converted to an array.

This function is used to convert a WHERE string into an array so that that each field name can be examined individually. This is used by the framework to perform such tasks as:

This is the opposite of array2where().


Session Variables

These are stored in the $_SESSION array so that they can be accessed in future requests which have the same session id.

$_SESSION['data'] (array)

Whenever the COPY button on the action bar is pressed it will save the contents of the screen using

$_SESSION['data'][$tablename] = $fieldarray;

When an ADD screen is activated it will examine this array looking for an entry for the current table name, and if one is found it will add a PASTE button to the action bar. If the PASTE button is pressed then the saved data will be copied into the current screen.

The saved data can be cleared by pressing COPY on an empty screen.

$_SESSION['default_language'] (string)

This is obtained from the CONTROL table in the MENU database during the processing of the logon screen. It defines the default language code for the session and will be used unless an alternative is provided, such as via $_SESSION['user_language'] or $_SESSION['user_language_array'].

$_SESSION['logon_user_id'] (string)

This is the identity of the current user. It is set after passing through the LOGON screen.

$_SESSION['logon_user_name'] (string)

This is the name of the current user. It is set after passing through the LOGON screen.

$_SESSION['role_id'] (string)

This is the ROLE to which the current user belongs, and which may be shared by other users. All security access is either allowed or denied at the ROLE level, so this is used to identify which parts of the application the user is allowed to access.

$_SESSION[$script_id] (array)

Each script that is executed has is own set of details stored in the $_SESSION array. This is so that variables used in one HTTP request can be made available to the next request within the same session.

This data is copied between the $_SESSION array and the $GLOBALS['script_vars'] array as follows:

Data in this array for a task may be maintained even though the corresponding entry for the task has been removed from the $page_stack array. This is made possible if the 'Keep Data on Exit' field is set to TRUE in the Update Task screen. This allows the current state for that task to be maintained throughout the session.

Note that as it is possible for the same script to be accessed by more than one task the contents of this array is quite complex. The entry for each script will contain a separate entry for each task, and there will be a separate entry which identifies which is the current of those tasks. It will look something like the following:

/menu/mnu_task_list.php = array [4]
    task_id = (string) mnu_task(list2)b
    pattern_id = (string) LIST1
    mnu_task(list2)a = array [12]
        button_text = (string) Task (Proc)
        where = (string) task_type='PROC'
        keep_data = (string) Y
        log_sql_query (string) N
        ....
    mnu_task(list2)b = array [12]
        ....
    mnu_task(list2)c = array [12]
        ....
$_SESSION['script_sequence'] (array)

Normally the movement from one script to another is controlled by methods $this->scriptNext() and $this->scriptPrevious(). However, it may sometimes be necessary to interrupt this flow by introducing one or more tasks into the sequence. This can be achieved by adding entries into this indexed array in the order in which they are to be processed. Each entry is an associative array containing the following values:

task_id (required) A string which identifies the task to be performed.
where (optional) A string to be used as the WHERE argument for that task.
action (optional) A string which forces the task to be repeated unless it completes with the same action string. For example, if this is set to 'OK' then the task will be repeated if returns an action of 'cancel'.
messages (optional) A single message string (or a multiple-message array) that will be displayed in the message area when the task is activated.
query_string (optional) A string which will be appended to the URL when the task is activated using the header('location: ...') directive.

Note that the instructions in this array are not processed until the current object finishes executing and returns control to the controller. This will then process the contents of this array as follows:

For example, during the logon process it may be determined that the user's current password has expired and must therefore be changed. This means that instead of selecting the standard MENU screen as the next task, the sequence is altered to Reset User Password and then the MENU screen. This is performed using code similar to the following:

    // find out if user needs to change his password
    if ($mnu_control->getControlData('pswd_change', $updatearray)) {
        $next['task_id']  = 'mnu_user(upd1)b';
        $next['where']    = "user_id='$logon_user_id'";
        $next['action']   = 'OK';
        // 'You must change your password'
        $next['messages'] = getLanguageText('e0002');
        // this is processed by scriptNext() and scriptPrevious()
        append2ScriptSequence($next);
    } // if

Note that the append2ScriptSequence() function is used to add the new details to this array.

Here is an example from the Survey/Questionnaire prototype where the input of a question requires the automatic processing of additional screens to define the choice of possible answers. This uses code similar to the following:

function _cm_post_insertRecord ($fieldarray)
// perform custom processing after database record is inserted.
{
    $where = array2where($fieldarray, $this->getPkeyNames());

    switch ($fieldarray['answer_type']) {
        case 'M':
            $next['task_id'] = 'srv_answer_option(multi)';
            $next['where']   = $where;
            append2ScriptSequence($next);
            break;
        case 'N':
            $next['task_id'] = 'srv_number_option(multi)';
            $next['where']   = $where;
            append2ScriptSequence($next);
            break;
        default:
            ;
    } // switch

    $next['task_id'] = 'srv_question_prompt(link)';
    $next['where']   = $where;
    append2ScriptSequence($next);

    return $fieldarray;

} // _cm_post_insertRecord

Note also that any number of $next entries can be added to this array, and they will be processed in the same order.

$_SESSION['search'] (array)

Whenever the SUBMIT button is a SEARCH screen is pressed it will save the contents of the screen using

$_SESSION['search'][$tablename] = $where;

When a SEARCH screen for the same table name is activated it will start by reloading the contents of the search string.

In a LIST screen which has a navigation button to activate a SEARCH screen, the presence of an entry in this array for the current table name will cause a PREVIOUS SEARCH button to be added to the navigation bar. This will cause that selection criteria to be applied without having to go into the SEARCH screen.

The saved data can be cleared by pressing the SUBMIT button on an empty SEARCH screen.

Please also see $GLOBALS['search'].

$_SESSION['timezone_client'] (string)

This is used by the convertTZ() function.

It is not possible to identify the client's timezone from any HTTP headers, so instead it must be defined on each individual MNU_USER record. This value will be loaded into memory when the user passes through the LOGON screen.

$_SESSION['timezone_server'] (string)

This is used by the convertTZ() function.

This is supplied from the date_default_timezone_get() function, but can be overridden by the $GLOBALS['server_timezone'] variable in the CONFIG.INC file.

$_SESSION['user_language'] (string)

This will only exist if the user's record on the MENU database has a non-blank value for language code. This will override any language code defined in the client's browser settings.

This is used in the Internationalisation feature.

$_SESSION['user_language_array'] (array)

This is an indexed array which contains one or more language codes provided by the client's browser in $_SERVER["HTTP_ACCEPT_LANGUAGE"]. Each entry provides the following values in a nested indexed array:

  1. The full language abbreviation, e.g. en-gb
  2. The primary language, e.g. en
  3. The full language string, e.g. English (United Kingdom) [en_GB]
  4. The primary language string, e.g. English

The full list of possible language codes is contained in file menu/text/en/sys.language_array.inc

This is used in the getBrowserLanguage() function.


Global Variables

Although some people say that global variables should NEVER be used I happen to disagree. Wherever possible the best way of passing values to a function/method is via arguments on the call statement, but sometimes it may be necessary to give access to a variable that is rarely used. In cases where there are a large number of these "rarely used" variables it would be a total PITA to include each an every one in the argument list, so my philosophy is to leave them in global scope and access them as and when required.

In PHP a global variable can be accessed from within a function/method in either of the following ways:

global $var_name;
$var_name = 'whatever';
$GLOBALS['var_name'] = 'whatever';
$GLOBALS['act_buttons'] (array)
These are the items that will appear in the screen's action bar. They are set by code within each controller, such as in the following example:
$act_buttons['submit']     = 'submit';
$act_buttons['submitstay'] = 'submitstay';
$act_buttons['copy']       = 'copy';
$act_buttons['quit']       = 'cancel';

Note that the calls to getLanguageText will be performed in the standard code during the construction of the XML document.

It may be a requirement that under some circumstances one (or more) of these buttons should be removed from the screen and not displayed. This can be achieved with code similar to the following:

unset($GLOBALS['act_buttons']['reset']);
$GLOBALS['classdir'] (string)
Whenever the getLanguageText() or getLanguageArray() functions are used to obtain entries from the files used to implement internationalisation, they will look for files which exists in the <cwd>/text/<language>/ directory where <cwd> is the current working directory and <language> is the desired language code.

However, it is possible to activate a class which exists in a different directory from the current working directory to either perform some validation or some database updates, which will cause these functions to look in the wrong files and therefore retrieve the wrong text. In those situations where this can occur the solution is to supply the location of the current class file in $GLOBALS['classdir'] prior to the function call, as shown in the following example:

    if (getcwd() != dirname($this->dirname)) {
        // switch to correct directory for retrieving message text
        $GLOBALS['classdir'] = dirname($this->dirname);
    } // if

    if (condition) {
        // 'There is no rule which permits the status to be changed from X to Y'
        $this->errors[] = getLanguageText('e0044', $curr_status_desc, $new_status_desc);
    } // if

    unset($GLOBALS['classdir']);

The getLanguageText() function will switch to the directory found in $GLOBALS['classdir'] before retrieving any values, then switch the current working directory back to what is contained within $_SERVER['SCRIPT_FILENAME'].

Note that this procedure is done automatically when calling the standard deleteRecord(), deleteSelection(), insertRecord() and updateRecord() methods, but will have to be done manually in any others.

$GLOBALS['date_format'] (string)

This resides in the CONFIG.INC file, and is used to define how dates will be displayed. Possible values are:

SettingOutput
dmy20 Aug 2006 (default)
mdyAug 20 2006
ymd2006 Aug 20
dd/mm/yyyy20/08/2006
dd.mm.yyyy20.08.2006
dd/mm/yy20/08/06
yyyy-mm-dd2006-08-20

Note that this value can be overridden for the current user's language on the MNU_LANGUAGE table.

$GLOBALS['http_server'] (string)

This resides in the CONFIG.INC file, and is only used if $GLOBALS['https_server'] is non-blank. Please refer to How can I use a secure server in my application? for details.

$GLOBALS['https_server'] (string)

This resides in the CONFIG.INC file, and is used to indicate if the HTTPS protocol is available on the web server. Please refer to How can I use a secure server in my application? for details.

$GLOBALS['https_server_suffix'] (string)

This resides in the CONFIG.INC file, and is used to indicate if the HTTPS protocol is available on the web server through a shared address which requires a domain name as a suffix. Please refer to How can I use a secure server in my application? for details.

$GLOBALS['lock_tables'] (boolean)
This is checked during startTransaction after a call to _cm_getDatabaseLock. If set to FALSE then no tables will be locked at the start of this transaction. If set to TRUE then any table that may be accessed during the database transaction, whether for reading or writing, must be locked.
$GLOBALS['lock_rows'] (string)
This is used in _dml_getData when reading records from the database while a database transaction is in progress. It can be set to any of the following values, which will cause the relevant clause to be added to the SQL query:

This can be set in _cm_getDatabaseLock which is called from startTransaction and will apply to all database table objects which are accessed during the current database transaction. If a different setting is required for a particular database table then refer to $this->row_locks.

$GLOBALS['log_sql_query'] (boolean)
If you wish to discover what SQL statements are constructed and issued by the framework you can set this variable to TRUE inside the CONFIG.INC file and it will cause all SQL statements to be written out to a log file in the application's sql subdirectory, one per script, with the name <script_id>.sql.

If you only want SQL logging turned on for individual tasks then go to the Update Task screen and set 'Log SQL Query' to YES.

$GLOBALS['mode'] (string)

The same table class and the same XSL stylesheet can be used by several controllers which perform different functions, so some method is required whereby the function of the controller can be readily identified. This value is set by the controller so that it can be examined within the application code, and it is also copied out to the XML document so that it is also available to the XSL stylesheet.

The possible values are: list, insert, delete, read, search and update.

$GLOBALS['nav_buttons_omit'] (array)
When a form is displayed it may contain a series of navigation buttons. The details for these will be extracted from the menu database after the table class has finished executing, so there is normally no way in which these details can be affected. However, there may be circumstances in which something in the data which has just been retrieved means that one (or more) of these navigation buttons is now invalid.

Under normal circumstances the navigation button would still be visible and, if pressed, would pass control to the child transaction which, if programmed correctly, should issue an error message and pass control immediately back to its parent. If the preferred behaviour is not to show the navigation button in the first place then this can be achieved by adding the task_ids of the child transactions to this array using code such as the following:

$GLOBALS['nav_buttons_omit'][] = 'foo(add)';
$GLOBALS['nav_buttons_omit'][] = 'bar(add)';

After the data for the navigation buttons has been retrieved from the database it will be written out to the XML file, but any entry found in the $nav_buttons_omit array will be ignored.

$GLOBALS['orderby'] (string)

This is populated whenever the user clicks on one of the column headings in a LIST screen to cause the data to be sorted on that column (field) name.

$GLOBALS['orderby_seq'] (string)

This toggles between 'asc' (ascending) and 'desc' (descending) each time the user clicks on one of the column headings in a LIST screen to cause the data to be sorted on that column (field) name.

$GLOBALS['page_stack'] (array)

This is an associative array which is keyed by $task_id, and each value is another associative array containing two entries, one for button_text and another for script_id.

This is used to keep track of transactions which the user has visited in the current session, and which have become suspended while a child transaction is processed. The last entry in the stack is always the current "active" transaction while all previous entries are "suspended".

If the user presses a navigation button or a popup button in a transaction, then that transaction (the parent) becomes suspended while the child transaction associated with that button has control. As soon as the child transaction terminates control is automatically passed back to its parent, and the child transaction disappears from the stack.

Suspending the current transaction and activating a new one is achieved using $this->scriptNext().

Terminating the current transaction and returning to the previous one is achieved using $this->scriptPrevious().

This is totally different from the browser history which keeps track of different HTTP requests. By attempting to navigate with the browser's BACK button it is possible to re-issue an HTTP request which is no longer valid. For example, the user visits a LIST screen with a GET request, chooses an ADD screen with a POST request, jumps to that ADD screen with a GET request, enters data and submits it with a POST request, then returns to the list screen with another GET request. The browser history resembles the following:

MENU, LIST [get], LIST [post], ADD [get] ADD [post], LIST [get]

While within the ADD screen the stack contains just the following:

MENU, LIST, ADD

When the ADD screen finishes it drops its entry from the stack and returns control to its parent, which leaves the following:

MENU, LIST

Going backwards from the LIST screen would cause the following:

The contents of this array is also is used to populate the 'breadcrumbs' area in the menu bar.

This array is maintained using the following:

$GLOBALS['party_language'] (string)

Within an application that may need to produce output in the language of the recipient, where the recipient is an external party and not the person who is generating the document, this variable will be used to identify the language of the recipient. The language of the person generating the output will be held in $_SESSION['user_language'].

$GLOBALS['return_action'] (string)

Whenever a task is re-activated after returning from a child task this will indicate how that child task terminated. Possible values are as follows:

$GLOBALS['return_from'] (string)

Whenever a task is re-activated after returning from a child task this will contain the identity of that child task.

$GLOBALS['screen_structure'] (array)

This holds the contents of the task's screen structure file. The contents of the disk file are copied to this array during the initialisation process of the controller script, and this array is then transferred to the XML document at the end of the script so that the HTML output can be constructed during the XSL transformation.

This means that at any time during the period when the controller has handed execution over to a business layer object the contents of this array can be modified so that the structure of the HTML output can be tailored to suit particular circumstances.

$GLOBALS['script_vars'] (array)

This contains variables for the current script that will be copied to/from the $_SESSION array as follows:

During the processing of initSession() the contents of this array will be extracted into individual global variables. Entries may then be deleted from the array so that they are not used again.

$GLOBALS['search'] (string)

This string is in the format of the WHERE clause in an SQL query and contains any search criteria that was entered on a SEARCH screen. Note that because wild card characters are allowed the format of a search string will be as follows:

field1 LIKE 'value' AND field2 LIKE 'value'

The contents of this string will be merged with the contents of the $GLOBALS['where'] string before any records are retrieved from the database.

Please note the following:

Please also see $_SESSION['search'].

$GLOBALS['selection'] (string)

This string is in the format of a WHERE clause in an SQL query and contains the primary keys for all those occurrences marked as SELECTED in a multi-occurrence screen such as a LIST1.

The primary key for each selected occurrence will be enclosed in '(' and ')', and the keys for multiple occurrences will be separated by ' OR ' as in the following example:

(key1='A' AND key2='B') OR (key1='C' AND key2='D') OR (...) OR (...)

This string is created by the selecton2where() function.

$GLOBALS['server_timezone'] (string)

This resides in the CONFIG.INC file, and is used to replace the identity of the server's time zone as identified by the date_default_timezone_get() function.

$GLOBALS['settings'] (array)

This is an associative array of values which is initially obtained from the settings field in the MNU_TASK data, but which can be amended with custom code within any database object. Any values will also be included in the <params> area of the XML document so that they are also available to the XSL transformation process.

The following values are currently supported within the Radicore framework for use with POPUP forms, and which can be set in the _cm_popupCall() method:

$GLOBALS['task_id'] (string)
This is the identity of the current TASK from the MENU database, which provides the identity of the current script. Note that a particular script may be shared by more than one task.
$GLOBALS['where'] (string)

This string is in the format of the WHERE clause in an SQL query. It is initially passed down from the previous (parent) screen so will identify a record by its primary key. If this is used as a foreign key in the child screen then it may retrieve more than one database record.


© Tony Marston
10th March 2006

http://www.tonymarston.net
http://www.radicore.org

Amendment history:

01 Jul 2014 Added fetchRowChild() which is used when outputting a tree structure (hierarchy) to a CSV file.
01 Jun 2014 Updated _cm_getPkeyNames() to include notes regarding the use of the __sleep() method.
Updated $GLOBALS['date_format'] to show that a different setting can be supplied for different languages.
Added add_days_to_date().
08 May 2014 Added replaceScreenLabels().
Added an entry for the Singleton class.
07 Mar 2014 Updated getExternalDate and getInternalDate to include the optional $date_format argument.
Added $this->executeQuery and $this->multiQuery
Added findDBName().
01 Dec 2013 Added $this->fields_not_for_trimming to prevent string fields from being trimmed before an insert or an update.
Added $object->updateFieldArray() to update an object with the contents of the $_POST array when form data is submitted by a javascript submit() function instead of a SUBMIT button or custom button.
Added the $child_object and $parent_object properties.
Added the setChildObject() and setParentObject() methods.
Added the getParentData() and setParentData() methods.
Added the getChildData() and setChildData() methods.
Removed the _cm_setParentData() method as it is now redundant. Use the getParentData() method instead.
02 Mar 2013 Updated $GLOBALS['date_format'] to include the format 'yyyy-mm-dd'.
14 Apr 2012 Added $this->allow_db_function to Class Properties.
Added isDirectoryValid() function.
Updated restart() and _cm_restart() to include the $return_string argument.
20 Feb 2012 Modified the _cm_deleteSelection() method to allow a delete to span multiple tables.
25 Aug 2011 Added the _cm_post_lastRow() method.
Added the outputPDF_LabelView() and $PDF->labelView () methods.
01 Jun 2011 Added the dateDifference() function.
Added the getBrowserVersion() function.
01 Nov 2010 Updated the post_fileupload() function to include the size of the file which has just been uploaded.
Added the restart() and _cm_restart() methods.
01 Jun 2010 Updated the _cm_initialisefileupload() function to allow any type of file to be uploaded.
Updated the post_fileupload() and _cm_post_fileupload() methods to return the $filename argument in case it needs to be changed.
Added the customButton() and _cm_customButton() methods.
01 May 2010 Updated the append2ScriptSequence() function to include $prepend as an optional second argument.
01 Feb 2010 Updated _cm_ListView_print_after() method to include $next_row as a second argument.
01 Oct 2009 Added functions convertTZdate() and convertTZtime().
Updated $PDF->listView() to replace the _cm_ListView_pre_print() method with the _cm_ListView_print_before() and _cm_ListView_print_after() methods.
01 Sep 2009 Modified reset() so that it calls _cm_reset().
Modified _cm_initialise() so that the $selection argument is available in all tasks.
Modified _cm_pre_output() to allow the filename to be passed when the output is either PDF or CSV.
Modified _cm_post_output() to include the filename as the second argument.
01 Aug 2009 Modified _cm_initialise() so that it includes the $search string as a third argument in tasks with pattern OUTPUT.
08 Jul 2009 Added the post_search() and _cm_post_search() methods.
Added the replaceScreenColumns() function.
Added function convertTZ() for timezone conversions.
01 Jun 2009 Added the $this->no_display_count and $this->no_controller_msg variables.
01 May 2009 Added the getPreviousPattern() function.
Added the $object->scriptPrevious() method.
Added the $object->no_duplicate_error variable.
Added the post_fileUpload() and _cm_post_fileUpload() methods.
01 Apr 2009 Added the $object->scriptNext method.
01 Jan 2009 Added the $this->sql_no_foreign_db variable.
Added the replaceScreenHeadings() function. This makes getColumnHeadings() and setColumnHeadings() redundant.
Added the replaceReportHeadings() function.
01 Dec 2008 Amended getColumnNames() method so that it calls _cm_getColumnNames() to allow customisation before the screen is displayed.
Added variable $this->lookup_css to allow individual entries in radio groups to be given their own CSS style.
Added the filePickerSelect() and _cm_filePickerSelect() methods.
01 Oct 2008 Added the getPkeyNamesAdjusted() function.
Added the sqlSelectDefault() function.
Amended $GLOBALS['date_format'] to include new values.
01 Sep 2008 Amended the _cm_initialiseFilePicker() function to accept the $fieldarray argument.
01 Aug 2008 Amended the _cm_popupCall() function to allow $this->errors to be set and $popupname to be changed.
01 Jul 2008 Added the rangeFromTo() function.
Added the unqualifyFieldArray() function.
Added the setCurrentOrHistoric() and currentOrHistoric() functions.
01 May 2008 Added the setParentData() and _cm_setParentData() methods so that changes made in the parent object can be passed down to the child object.
Added the setColumnAttributes() and unsetColumnAttributes() functions so that individual columns can be hidden/unhidden.
Added the getColumnHeadings() and setColumnHeadings() functions so that column headings can be changed at runtime.
Added the getWhere() and _cm_getWhere() methods so that the WHERE string which is passed to a child task can be amended.
01 Apr 2008 Modified _cm_initialise() so that it includes the $selection string as a second argument in tasks with pattern ADD2.
01 Mar 2008 Modified _sqlAssembleWhere() so that it will automatically move any aliased names from WHERE to HAVING.
Added _cm_filterWhere() method.
01 Feb 2008 Added variables $this->alt_language_cols and $this->alt_language_table.
Added methods _sqlProcessJoin() and _sqlSelectAlternateLanguage().
Added method _cm_ListView_pre_print().
01 Nov 2007 Added function resizeImage() which can be used to resize an image during the file upload process.
01 Oct 2007 Added method free_result() in order to release a resource created by getData_serial().
20 Aug 2007 Added function append2ScriptSequence() for adding entries to $_SESSION['script_sequence'].
25 Jul 2007 Changed the _cm_initialiseFileUpload() method so that if any type of image is allowed then the $filetypes variable can be set to the string value image instead of having to specify an array of all possible image types.
Added the _cm_ListView_header() method to allow fields to be defined which can be inserted into the page title.
09 Jun 2007 Changed the fetchRow() method to call _cm_post_fetchRow() instead of _cm_post_getData().
Removed getData_batch() as it does the same thing as getData_serial().
28 May 2007 Added the _getInitialValues() method which is used in the initialise() method.
01 May 2007 Updated _dml_ReadBeforeUpdate() to include argument $reuse_previous_select.
Changed $remove_buttons to $nav_buttons_omit so that the name is more meaningful.
Added method insertOrUpdate() for use in the new Update 5 pattern.
Added method _cm_getPkeyNames() and _cm_validateSearch().
Added include.subsystem.inc to the list of files in the introduction.
31 Mar 2007 Added custom method _cm_post_popupReturn() to popupReturn().
Added $parent_table argument to validateDelete() and _cm_validateDelete() methods.
27 Jan 2007 Added custom method _cm_getOrderBy().
15 Jan 2007 Added _cm_ListView_total() to provide the ability to print a final line in the PDF output containing any accumulated totals.
Amended _cm_formatData() to include an additional argument for CSS styles for individual fields.
05 Dec 2006 Amended _dml_ReadBeforeUpdate() to include a special field called 'rdcversion' (Radicore Version Number) which can be used to prevent simultaneous updates to the same record.
18 Nov 2006 Added functions initialiseFilePicker() and _cm_initialiseFilePicker().
Added functions initialiseFileDownload() and _cm_initialiseFileDownload().
01 Nov 2006 Added optional argument $fieldarray to _cm_pre_getData() method.
Added functions getTableAlias1(), getTableAlias2() and getTableAlias3().
15 Oct 2006 Added method getPatternId().
28 Aug 2006 Added methods outputPDF_ListView() and outputPDF_DetailView().
12 Aug 2006 Added methods getData_serial() and outputCSV().
09 Aug 2006 Added customisable method _cm_setJavaScript().
09 Jul 2006 Added function adjustDate() and isPrimaryObject().
15 May 2006 Added an extra parameter ($wherearray) to _cm_fileUpload() to pass the contents of the $where string from initialiseFileUpload().
30 Apr 2006 Added initialiseFileUpload() and fileUpload() methods to add more flexibility to the file upload feature.

counter