Location: PHPKode > scripts > ZBase > zbase/objects/zbase_support.inc
<?
/****************************************************************************
 * zbase_support.inc : This file gathers modules search, create, modify,
 * delete, show, and types_support.
 ****************************************************************************
 * $Id: zbase_support.inc,v 1.18 2000/08/29 01:19:30 massiot Exp $
 ****************************************************************************
 *  ZBase - a gateway between the database and the WWW
 *  Copyright (c) 1999 The ZBase team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 ****************************************************************************/

if (!isset($GLOBALS[setzbase_support])) {

class zbase_support extends services {

/*
 * Object definition variables (constants)
 */

  var $TABLE,        /* Name of the table. */
      $TITLE,        /* Default title which will be displayed in tables. */
      $FIELDS = array(), /* List of the fields. Note that the attributes
                          * (see types_support.inc) of every field are
                          * stored in $ATTR_fieldname class variables. */
      $KEYS = array(), /* Set of fields allowing to reference a unique
                        * row in the table. */
      $FOREIGNKEYS = array(), /* Sets of fields that can be used to
                               * reference one or several rows in that table.
                      * Eg : array("login", "name", array("building", "room"))
                               */
      $SHOW_LIST;    /* List of fields that will be displayed, by default,
                      * when you call show_list(). */

/*
 * Temporary content of the object
 */

  var $row,          /* The actual content of the current row.
                      * Eg :  array("login" => "SMITH") */
      $old_row,      /* The old content of the row, in case of a
                      * modify(). */
      $result_id;    /* The result ID returned by the result class
                      * (see result_support.inc). */

/*
 * The SEARCH module
 */

/*
 * $dict can be anything useful to identify one or several rows. That
 * includes :
 * - An array : "field" => "value", ...
 *           or "field" => array("value", "operator"), ...
 *     where operator can be =, <, >, <=, >=, !=, <> (default is =)
 *     Please note that a "like"-style operator is automatically used
 *     if "value" contains %.
 * - An object : in that case we look for the _entire_ set of $KEYS, then
 *     for each of the sets in $FOREIGNKEYS, in the given object, until
 *     we find something that can be used for a search.
 *
 * $order_by can be used as follows :
 * - field1 
 * - array (field1, field2)
 * - array (field1 => "desc") 
 * - array (field1 => "asc","field2"=>"desc")
 * But array (field1, field2 => "desc") is incorect and can lead 
 * to an error or a random order.
 *
 * As all other modules, in case of errors, the explanation will be put
 * in $ZB_ERROR, and the name of the function in $ZB_ERRFUNC.
 * If the request is successful, the function returns the number of
 * rows matched (search_numrows()).
 */
function search($dict = "", $order_by = "") {
  if (is_object($dict)) {
    /* Second use of the function */
    $gt_dict = $this->search_extractdict($dict);

    if (!$gt_dict)
      return 0;

  } else {
    /* The dict is directly usable by the appropriate gate. */
    $gt_dict = $dict;
  }

  return $this->gt_select($dict, $order_by);
} //search

/* Just a placeholder for gt_numrows(). */
function search_numrows() {
  return $this->gt_numrows();
} //search_numrows

/* Just a placeholder for gt_fetch_array(). */
function search_fetch_array($nb) {
  global $ZB_ERROR;
  $temp = $this->gt_fetch_array($nb);

  if (!$ZB_ERROR) {
    /* Remove extra numeric keys */
    foreach ($temp as $key => $val)
      if (is_int($key))
        unset($temp[$key]);

    $this->row = $this->old_row = $temp;
  }
} //search_fetch_array

/* Return a dictionary extracted from $object. Used by search(). */
function search_extractdict($object) {
  if (srv_isin($object->FIELDS, $this->KEYS))
    $gt_keys = $this->KEYS;
  else {
    foreach(srv_makearray($this->FOREIGNKEYS) as $keys) {
      if (srv_isin($object->FIELDS, $keys)) {
        $gt_keys = $keys;
        break;
      }
    }
  }

  if (!$gt_keys)
    return;

  foreach($gt_keys as $key) {
    $gt_dict[$key] = $object->row[$key];
  }
  return $gt_dict;
} //search_extractdict

/* Continue the search on other tables if needed. */
function search_afterresult() {
  /* By default do not do anything. Please feel free to overload this
   * function. */
} // search_afterresult

/* Prompt a dialog asking the user to narrow the search. */
function search_narrowresult($nb) {
  if (!$this->SEARCH_NARROWFIELD)
    return; /* Not supported yet */

  $attr = $this->types_getattr($this->SEARCH_NARROWFIELD);
  if ($attr["type"] == "text" || in_array("text", $attr["extends"])) {
    if ($nb > srv_loadvar("htmlpage", "search_beginnarrow")) {
      global $THEME_OBJ;
      $THEME_OBJ->th_echo($nb." ".srv_str("html", "search_result_toomany"));
      srv_narrowsearch($this->TABLE, $this->SEARCH_NARROWFIELD,
                       $this->SEARCH_NARROWFIELD);
      if (function_exists("srv_stopandask") &&
           $nb > srv_loadvar("htmlpage", "search_beginask")) {
        srv_stopandask(srv_str("html", "search_result_reallytoomany"));
      }
    }
  }
} //search_narrowresult


/*
 * The CREATE module
 */

/* Do everything you need to do to create a new row. Warning : you will
 * have a $RESULT_OBJ as a global variable. */
function create() {
  global $ZB_ERROR;
  $this->create_setup();
  $this->create_verify();
  if ($ZB_ERROR)
    return;
  $this->create_maketree();
  if ($ZB_ERROR)
    return;

  $this->create_phase1();
  if ($ZB_ERROR) {
    $this->create_rollback();
    return;
  }
  $this->create_phase2();
  if ($ZB_ERROR) {
    $this->create_rollback();
    return;
  }

  $this->create_commit();
  if ($ZB_ERROR) {
    $this->create_rollback();
    return;
  }
} //create

/* Retrieve the content of a field from a form. */
function create_retrieve($form, $raw_format = 0) {
  /* We assume the calling class has already called $form->form_getnb()
   * and form_getobj() and cares about it. */
  global $ZB_ERROR, $RESULT_OBJ;
  foreach ($this->FIELDS as $field) {
    $form->form_getinput($field, $this->row[$field]);
    if (!$raw_format) {
      $this->row[$field] = $this->types_retrieve($field, $this->row[$field]);
      if ($ZB_ERROR) {
        /* We need the $RESULT_OBJ to be set up to use this. */
        $this->create_setup();
        $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "retrieve");
        return;
      }
    }
  }
} //create_retrieve

/* Prepare $RESULT_OBJ to manage the results of the creation. */
function create_setup() {
  global $RESULT_OBJ;
  if (!is_object($RESULT_OBJ)) {
    /* We create a new result object since we absolutely NEED it, but
     * CAUTION : we don't call rlt_end(). */
    $RESULT_OBJ = new result;
    $RESULT_OBJ->rlt_begin();
    $this->result_id = $RESULT_OBJ->rlt_newid();
    $RESULT_OBJ->rlt_addroot($this->result_id);
  } else
    $this->result_id = $RESULT_OBJ->rlt_newid();

  $RESULT_OBJ->rlt_addinfo($this->result_id, $this->TABLE,
                           srv_getfields($this->row, $this->KEYS));
} //create_setup

/* Verify the content of the row. */
function create_verify() {
  global $ZB_ERROR, $ZB_ERRFUNC, $RESULT_OBJ;
  $this->types_verify();
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "verify");
    return;
  }

  /* Verify that we don't already have a row with the same keys. */
  $object = new $this->TABLE;
  if ($object->search(srv_getfields($this->row, $this->KEYS))) {
    $ZB_ERROR = srv_str("create", "err_exist");
    $ZB_ERRFUNC = "zbase:create_verify()";
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "verify");
    return;
  }
} //create_verify

/* Build the dependances tree. */
function create_maketree() {
  global $ZB_ERROR;
  if (method_exists($this, "dep_maketree"))
    $this->dep_maketree();
  if ($ZB_ERROR)
    return;
} //create_maketree

/* Phase 1 : We only modify what is safely cancellable. */
function create_phase1() {
  global $ZB_ERROR, $RESULT_OBJ;
  if ($this->GT_TRANS_SAVVY)
    $this->gt_insert($this->row);
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "1");
    return;
  }

  if (method_exists($this, "dep_walk"))
    $this->dep_walk("phase1");
  if ($ZB_ERROR)
    return;
} //create_phase1

/* Phase 2 : We modify what is not safely cancellable. */
function create_phase2() {
  global $ZB_ERROR, $RESULT_OBJ;
  if (!$this->GT_TRANS_SAVVY)
    $this->gt_insert($this->row);
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "2");
    return;
  }

  if (method_exists($this, "dep_walk"))
    $this->dep_walk("phase2");
  if ($ZB_ERROR)
    return;
} //create_phase2

/* End of work, tell the gate to validate the queries, send mail if
 * necessary, etc. */
function create_commit() {
  global $ZB_ERROR, $RESULT_OBJ;

  if (method_exists($this, "dep_reverse_walk"))
    $this->dep_reverse_walk("commit");
  if ($ZB_ERROR)
    return;

  $this->gt_commit();
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "1");
    return;
  }

  $RESULT_OBJ->rlt_adddone($this->result_id);
} //create_commit

/* Cancel what we've done. */
function create_rollback() {
  global $ZB_ERROR, $RESULT_OBJ;

  if (method_exists($this, "dep_reverse_walk"))
    $this->dep_reverse_walk("rollback");

  $this->gt_rollback();
} //create_rollback

/* Called by create_result.php after a new row has been created. */
function create_afterresult() {
  /* By default display the created row */
  $this->show_row();
} //create_afterresult


/*
 * The MODIFY module
 * For comments, please look at the create module, and adapt adequately.
 */

/* Do everything you need to do to modify a row. Warning : you will end up
 * with a $RESULT_OBJ as a global variable. */
function modify() {
  global $ZB_ERROR;
  $this->modify_setup();
  $this->modify_verify();
  if ($ZB_ERROR)
    return;
  $this->modify_maketree();
  if ($ZB_ERROR)
    return;

  $this->modify_phase1();
  if ($ZB_ERROR) {
    $this->modify_rollback();
    return;
  }
  $this->modify_phase2();
  if ($ZB_ERROR) {
    $this->modify_rollback();
    return;
  }

  $this->modify_commit();
  if ($ZB_ERROR) {
    $this->modify_rollback();
    return;
  }
} //modify

/* Retrieve the content of a field from a form. */
function modify_retrieve($form, $raw_format) {
  /* We assume the calling class has already called $form->form_getnb()
   * and form_getobj() and cares about it. */
  foreach ($this->FIELDS as $field) {
    $form->form_getinput($field, &$temp);
/* FIXME !! */
//    if (strlen($temp)) {
      $this->row[$field] = $temp;
      if (!$raw_format) {
        $this->row[$field] = $this->types_retrieve($field, $this->row[$field]);
        if ($ZB_ERROR) {
          /* We need the $RESULT_OBJ to be set up to use this. */
          $this->modify_setup();
          $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "retrieve");
          return;
        }
      }
//    }
  }
} //modify_retrieve

function modify_setup() {
  global $RESULT_OBJ;
  if (!is_object($RESULT_OBJ)) {
    /* We create a new result object since we absolutely NEED it, but
     * CAUTION : we don't call rlt_end(). */
    $RESULT_OBJ = new result;
    $RESULT_OBJ->rlt_begin();
    $this->result_id = $RESULT_OBJ->rlt_newid();
    $RESULT_OBJ->rlt_addroot($this->result_id);
  } else
    $this->result_id = $RESULT_OBJ->rlt_newid();

  $RESULT_OBJ->rlt_addinfo($this->result_id, $this->TABLE,
                           srv_getfields($this->row, $this->KEYS));
} //modify_setup

function modify_verify() {
  global $ZB_ERROR, $ZB_ERRFUNC, $RESULT_OBJ;
  $this->types_verify();
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "verify");
    return;
  }
} //modify_verify

function modify_maketree() {
  global $ZB_ERROR;
  if (method_exists($this, "dep_maketree"))
    $this->dep_maketree();
  if ($ZB_ERROR)
    return;
} //modify_maketree

function modify_phase1() {
  global $ZB_ERROR, $RESULT_OBJ;
  if ($this->GT_TRANS_SAVVY)
    $this->gt_update($this->row, $this->old_row);
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "1");
    return;
  }

  if (method_exists($this, "dep_walk"))
    $this->dep_walk("phase1");
  if ($ZB_ERROR)
    return;
} //modify_phase1

function modify_phase2() {
  global $ZB_ERROR, $RESULT_OBJ;
  if (!$this->GT_TRANS_SAVVY)
    $this->gt_update($this->row, $this->old_row);
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "2");
    return;
  }

  if (method_exists($this, "dep_walk"))
    $this->dep_walk("phase2");
  if ($ZB_ERROR)
    return;
} //modify_phase2

function modify_commit() {
  global $ZB_ERROR, $RESULT_OBJ;

  if (method_exists($this, "dep_walk"))
    $this->dep_walk("commit");
  if ($ZB_ERROR)
    return;

  $this->gt_commit();
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "1");
    return;
  }

  $RESULT_OBJ->rlt_adddone($this->result_id);
} //modify_commit

function modify_rollback() {
  global $ZB_ERROR, $RESULT_OBJ;

  if (method_exists($this, "dep_reverse_walk"))
    $this->dep_reverse_walk("rollback");

  $this->gt_rollback();
} //modify_rollback

/* Called by modify_result.php after a row has been modified. */
function modify_afterresult() {
  /* By default display the modified row */
  $this->show_row();
} //modify_afterresult


/*
 * The DELETE module
 * For comments, please see the create module and adapt adequately.
 */

function delete() {
  global $ZB_ERROR;
  $this->delete_setup();
  $this->delete_verify();
  if ($ZB_ERROR)
    return;
  $this->delete_maketree();
  if ($ZB_ERROR)
    return;

  $this->delete_phase1();
  if ($ZB_ERROR) {
    $this->delete_rollback();
    return;
  }
  $this->delete_phase2();
  if ($ZB_ERROR) {
    $this->delete_rollback();
    return;
  }

  $this->delete_commit();
  if ($ZB_ERROR) {
    $this->delete_rollback();
    return;
  }
} //delete

function delete_setup() {
  global $RESULT_OBJ;
  if (!is_object($RESULT_OBJ)) {
    /* We create a new result object since we absolutely NEED it, but
     * CAUTION : we don't call rlt_end(). */
    $RESULT_OBJ = new result;
    $RESULT_OBJ->rlt_begin();
    $this->result_id = $RESULT_OBJ->rlt_newid();
    $RESULT_OBJ->rlt_addroot($this->result_id);
  } else
    $this->result_id = $RESULT_OBJ->rlt_newid();

  $RESULT_OBJ->rlt_addinfo($this->result_id, $this->TABLE,
                           srv_getfields($this->row, $this->KEYS));
} //delete_setup

function delete_verify() {
  /* We have nothing to verify, but maybe the user will overload this
   * function. */
} //delete_verify

function delete_maketree() {
  global $ZB_ERROR;
  if (method_exists($this, "dep_maketree"))
    $this->dep_maketree();
  if ($ZB_ERROR)
    return;
} //delete_maketree

function delete_phase1() {
  global $ZB_ERROR, $RESULT_OBJ;
  if ($this->GT_TRANS_SAVVY)
    $this->gt_delete(srv_getfields($this->row, $this->KEYS));
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "1");
    return;
  }

  if (method_exists($this, "dep_walk"))
    $this->dep_walk("phase1");
  if ($ZB_ERROR)
    return;
} //delete_phase1

function delete_phase2() {
  global $ZB_ERROR, $RESULT_OBJ;
  if (!$this->GT_TRANS_SAVVY)
    $this->gt_delete(srv_getfields($this->row, $this->KEYS));
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "2");
    return;
  }

  if (method_exists($this, "dep_walk"))
    $this->dep_walk("phase2");
  if ($ZB_ERROR)
    return;
} //delete_phase2

function delete_commit() {
  global $ZB_ERROR, $RESULT_OBJ;

  if (method_exists($this, "dep_reverse_walk"))
    $this->dep_reverse_walk("commit");
  if ($ZB_ERROR)
    return;

  $this->gt_commit();
  if ($ZB_ERROR) {
    $RESULT_OBJ->rlt_adderror($this->result_id, srv_geterr(), "1");
    return;
  }

  $RESULT_OBJ->rlt_adddone($this->result_id);
} //delete_commit

function delete_rollback() {
  global $ZB_ERROR, $RESULT_OBJ;

  if (method_exists($this, "dep_reverse_walk"))
    $this->dep_reverse_walk("rollback");

  $this->gt_rollback();
} //delete_rollback

/* Called by delete_result.php after a row has been deleted. */
function delete_afterresult() {
  /* By default do not do anything */
} //delete_afterresult


/****************************************************************************
 * types_support : Support for type conversion routines
 ****************************************************************************/

  /* Cache the attributes of the fields. */
  var $attributes_cache;


/*
 * Reminder : text, int, float are standard primary types (ie. MUST be
 * understood by all modules).
 *
 * Other types defined here are secondary types.
 * Modules that don't understand them SHOULD fall back to the closest
 * type, using the 'extends' attribute. You can define your own secondary
 * types in zbase.inc.
 */


/*
 * Reminder : the valid attributes for both types and fields (a field
 * automatically inherits its type's attributes) are :
 *   type           Type of the field
 *   extends        When defining the type : the first parent of the type
 *                  When returned by srv_getattr : all the parents of the type
 *   display_as     The type that can be used to display this type, in
 *                  case the theme doesn't know anything about it. If
 *                  defined, will be used preferably to 'extends' in themes.
 *   store_as       The type that can be used to store this type, in
 *                  case the gate doesn't know anything about it. If
 *                  defined, will be used preferably to 'extends' in gates.
 *   min            Minimum value (for types derived from int, float, date
 *                  and time only)
 *   max            Maximum value (idem)
 *   size           Maximum size (in bytes) of the field
 *   showsize       Average size (in bytes) of the field
 *   vsize          Vertical size (in lines) of the field
 *   caption        Caption to use in the SHOW module
 *   regexp         Regular Expression the field must match
 *   default        Default content of the field
 *   required       TRUE if the field must not be empty
 *   menu           The different options (only for the menu type)
 *   help           String to display as a help balloon
 *   bgcolor        The background color of the cell
 *   func_retrieve  Function used to retrieve the content of the field
 *   func_verify    Function used to verify the content of the field
 *   func_show      Function used to display the content of the field
 *   func_form      Function used to edit the content of the field
 *   func_link      Function used to return a link associated with the field
 *   sequence       Name of the sequence in the database [sequence type only]
 *   ref_table      Table this field references to [reference type only]
 *   ref_field      Field to display in that table [reference type only]
 * Other attributes may be added in the future. You should ignore them
 * without displaying an error message.
 */


/*
 * Standard primary types
 */
  
  var $TYPES_PRIMARY = array ("int", "float", "text"),

      $TYPEATTR_text = array("func_show" => "types_show_text"),
      $TYPEATTR_int = array("regexp" => "^[0-9]*\$", "showsize" => 8),
      $TYPEATTR_float = array("regexp" => "^\\-?[0-9]*(\\.[0-9]*)?\$",
                              "showsize" => 15);

function types_show_text($field) {
  return stripslashes($this->row[$field]);
} //types_show_text

/*
 * Standard secondary types
 */


/* MENU */

  var $TYPEATTR_menu = array("extends" => "int",
                       "min" => 1,
                       "display_as" => "text", 
                       "func_show" => "types_show_menu",
		       "func_verify" => "types_verify_menu");
  /* The menu type requires moreover a 'menu' attributes, that is an
   * array of all items in the menu. */

/* Used to convert $this->row[$field] to a value that can be displayed
 * by the theme. */
function types_show_menu($field) 
{
  $ATTR = $this->types_getattr($field);
  $menu = $ATTR["menu"];
  $value = $this->row["$field"];
  return $menu[$value - 1];
} //types_show_menu

/* Used to verify that the content of the field (after retrieve) is correct. */
function types_verify_menu($field)
{
  if (strlen($this->row[$field]) == 0)
    return FALSE;
  $ATTR = $this->types_getattr($field);
  $menu = $ATTR["menu"];
  $value = $this->row["$field"];
  return ($menu[$value - 1] == "");
} //types_verify_menu


/* BOOLEAN */

  var $TYPEATTR_boolean = array("extends" => "menu", 
                                "min" => 1, "max" => 2,
                                "menu" => array ("True", "False"), 
                                "func_show" => "types_show_boolean",
                                "func_retrieve" => "types_retrieve_boolean");

function types_show_boolean($field)
{
  global $ZB_TRUE;
  $value = $this->row["$field"];
  if ($value==$ZB_TRUE) return srv_str("show","boolean_true");
         else    return srv_str("show","boolean_false");
} //types_show_boolean

function types_retrieve_boolean($field, $value)
{
  /* The $value is 1 for TRUE and 0 for FALSE. Let's convert it into
   * ZB's standards. */
  global $ZB_TRUE, $ZB_FALSE;

  if ($value == 1)
    return $ZB_TRUE;
  else
    return $ZB_FALSE;
} //types_retrieve_boolean


/* EMAIL */

  var $TYPEATTR_email = array("extends" => "text", "showsize" => 20,
                              "regexp" =>
             "^([a-zA-Z0-9_]|\\-|\\.)+@(([a-zA-Z0-9_]|\\-)+\\.)+[a-zA-Z]{2,4}\$",
                              "func_link" => "types_email_link");

function types_email_link($field) {
  return "mailto:".$this->row[$field];
}


/* PASSWORD */

  var $TYPEATTR_password = array("extends" => "text", "showsize" => 10,
                                 "func_show" => "types_show_password",
                                 "func_form" => "types_form_password",
                                 "func_retrieve" => "types_retrieve_password");

function types_show_password($field) {
  /* Of course we don't show a password ! */
  return "********";
} //types_show_password

function types_form_password($field, &$more_attr) {
  return "********";
} //types_form_password

function types_retrieve_password($field, $value) {
  if ($value == "********")
    /* The password hasn't been modified. */
    return $this->old_row[$field];
  else
    /* Encrypt the new password. */
    return crypt($value);
} //types_retrieve_password


/* DATE */

  var $TYPEATTR_date = array("extends" => "int",
                             "min" => 0, /* An invalid date is -1. */
                             "display_as" => "text",
                             "func_show" => "types_date_unix2ISO", 
                             "func_form" => "types_date_unix2ISO",
                             "func_retrieve" => "types_date_ISO2unix",
                             "showsize" => 15);


/* func_show, func_form */
function types_date_unix2ISO($field)
{
  if ($this->row[$field])
    return date ("Y-m-d",$this->row["$field"]);
} //types_date_unix2ISO

/* func_retrieve */
function types_date_ISO2unix($field, $date)
{
  if ($date) {
    if (ereg("^[0-9]{1,2}-[0-9]{1,2}$",$date)) 
      $date = date ("Y-").$date;  
    ereg("([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})",$date,$temp);
    return mktime(0,0,0,$temp[2],$temp[3],$temp[1]);
    /* Return -1 in case of error. */
  }
} //types_date_ISO2unix


/* TIME */

  var $TYPEATTR_time = array("extends" => "int",
                             "min" => 0, /* An invalid time is -1. */
                             "display_as" => "text",
                             "func_show" => "types_time_unix2ISO",
                             "func_form" => "types_time_unix2ISO",
                             "func_retrieve" => "types_time_ISO2unix",
                             "showsize" => 15);

/* func_show, func_form */
function types_time_unix2ISO($field)
{
  if ($this->row[$field])
    return date ("H:i:s",$this->row["$field"]);
} //types_time_unix2ISO

/* func_retrieve */
function types_time_ISO2unix($field, $time)
{
  if ($time) {
    if (ereg("^[0-9]{1,2}:[0-9]{1,2}$",$time)) 
      $time = $time.":00";
    ereg("([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})",$time,$temp);
    return mktime ($temp[1],$temp[2],$temp[3], 1, 2, 1970);
  }
} //types_time_ISO2unix


/* SEQUENCE */

  var $TYPEATTR_sequence = array("extends" => "int",
                                 "min" => 0, /* -1 is an invalid sequence ID. */
                                 "func_retrieve" => "types_sequence_retrieve");
  /* The sequence type requires moreover a 'sequence' attribute, that is
   * the name of the sequence that will be passes to gt_nextseq() to get
   * the next sequence ID. */

/* If no ID is given, get a new one from gt_nextseq(). If no gt_nextseq()
 * function is provided, ZBase will erase your hard drives and send a
 * hurricane to Florida. */
function types_sequence_retrieve($field, $value) {
  if (strlen($value) == 0) {
    global $ZB_ERROR;
    $attr = $this->types_getattr($field);
    $value = $this->gt_nextseq($attr["sequence"]);
    if ($ZB_ERROR)
      return -1;
/* FIXME !!!!!!!!!!!! */
$toto = "FORM_".$field;
global $$toto;
$$toto = $value;
  }
  return $value;
} //types_sequence_retrieve


/* REFERENCE */

  var $TYPEATTR_reference = array("extends" => "int", "display_as" => "text",
                                  "func_show" => "types_show_reference",
                                  "func_form" => "types_form_reference",
                                  "func_retrieve" => "types_retrieve_reference",
                                  "func_link" => "types_link_reference",
                                  "display_as" => "menu");

function types_show_reference($field) {
  if (strlen($this->row[$field])) {
    $attr = $this->types_getattr($field);
    srv_include($attr["ref_table"]);
    $obj = new $attr["ref_table"];
    $obj->search(array($field => $this->row[$field]));
    if ($obj->search_numrows() == 1) {
      $obj->search_fetch_array(0);
      return $obj->types_show($attr["ref_field"]);
    }
    else
      return $this->row[$field];
  }
} //types_show_reference

function types_form_reference($field, &$more_attr) {
  $attr = $this->types_getattr($field);
  if (!$attr["required"])
    $menu["-1"] = "";
  srv_include($attr["ref_table"]);
  $obj = new $attr["ref_table"];
  $obj->search(array(), $attr["ref_field"]);
  $nb = $obj->search_numrows();
  for ($i = 0; $i < $nb; $i++) {
    $obj->search_fetch_array($i);
    /* -1 because form_std.inc automatically adds 1 to each menu index */
    $menu[$obj->row[$field]-1] = $obj->types_show($attr["ref_field"]);
  }
  $more_attr["menu"] = $menu;
  return $this->row[$field];
} //types_form_reference

function types_retrieve_reference($field, $value) {
  /* empty value */
  if (!$value) $value = "";
  return $value;
} //types_retrieve_reference

function types_link_reference($field) {
  $attr = $this->types_getattr($field);
  return srv_loadvar("htmlpage", "search_result")
         ."?FORM_NB=1&FORM_1_table=".$attr["ref_table"]
         ."&FORM_1_ATTR=a%3A1%3A%7Bs%3A3%3A%22raw%22%3Bi%3A1%3B%7D"
         ."&FORM_1_field=$field&FORM_1_operator=%3D&FORM_1_value="
         .$this->row[$field];
} //types_link_reference

/* URL */

  var $TYPEATTR_url = array("extends" => "text", "showsize" => 30,
                            "default" => "http://www.", "colspan" => 3,
                            "func_link" => "types_url_link");
 
function types_url_link($field) {
  return $this->row[$field];
} //types_url_link

/* PHONE */

  var $TYPEATTR_phone = array("extends" => "text", "showsize" => 20,
                              "default" => "(33) ",
                              "func_retrieve" => "types_retrieve_phone");

function types_retrieve_phone($field, $value) {
  if (ereg("^\(33\)[[:space:]]*([0-9]{2})[[:space:]]*([0-9]{2})[[:space:]]*([0-9]{2})[[:space:]]*([0-9]{2})[[:space:]]*([0-9]{2})$",
           $value, $result))
    return "(33) ".$result[1]." ".$result[2]." ".$result[3]." ".$result[4]
           ." ".$result[5];
  else
    return $value;
} //types_retrieve_phone

/* UPPERTEXT */

  var $TYPEATTR_uppertext = array("extends" => "text",
                                  "func_retrieve" => "types_retrieve_uppertext");

function types_retrieve_uppertext($field, $value) {
  return strtoupper($value);
} //types_retrieve_uppertext

/* LOWERTEXT */

  var $TYPEATTR_lowertext = array("extends" => "text",
                                  "func_retrieve" => "types_retrieve_lowertext");

function types_retrieve_lowertext($field, $value) {
  return strtolower($value);
} //types_retrieve_lowertext


/*
 * Types inheritance management
 */

/* Return the attributes of $field (cached in $attributes_cache by the
 * class constructor, using types_computeattr). */
function types_getattr($field) {
  return $this->attributes_cache[$field];
} //types_getattr

/* Return the attributes of a field, after all relationships of inheritance
 * are computed. */
function types_computeattr($field)
{
  $varname = "ATTR_$field";
  $attr_field = srv_makearray($this->$varname);
  $type = $attr_field["type"] ? $attr_field["type"] : "text";

  $extends = $this->types_getextendsparents($type);
  $display_as = $this->types_getdisplayasparents ($type);
  $store_as = $this->types_getstoreasparents($type);

  $result = $attr_field;

  /* Build the attributes list of the type. */
  foreach ($extends as $type)
  {
    $varname = "TYPEATTR_$type";
    $attr = srv_makearray($this->$varname);
    foreach($attr as $key => $value)
      if (!isset($result[$key])) $result["$key"] = $value;
  }

  /* Overload extends and display/store_as */
  $result["extends"] = $extends;
  $result["display_as"] = $display_as;
  $result["store_as"] = $store_as;

  return $result;
} //types_computeattr

/* The following methods are private : */

/* Return an array containing all the parents of a type  */
function types_getextendsparents($type)
{
  if (srv_isin ($this->TYPES_PRIMARY, $type)) return array ($type);
  else
  {
    $varname = "TYPEATTR_$type";
    $TYPEATTR = $this->$varname;
    return array_merge(array($type),$this->types_getextendsparents($TYPEATTR["extends"])); 
  }
} //types_getextendsparents

/* Return an array containing all the "display_as" parents of a type */
function types_getdisplayasparents($type)
{
  if (srv_isin ($this->TYPES_PRIMARY, $type)) return array ($type);
  else
  {
    $varname = "TYPEATTR_$type";
    $TYPEATTR = $this->$varname;
    $parent = $TYPEATTR["display_as"];
    if ($parent=="") $parent = $TYPEATTR["extends"];
    return array_merge(array($type),$this->types_getstoreasparents($parent));
  }
} //types_getdisplayasparents

/* Return an array containing all the "store_as" parents of a type */
function types_getstoreasparents($type)
{
  if (srv_isin ($this->TYPES_PRIMARY, $type)) return array ($type);
  else
  {
    $varname = "TYPEATTR_$type";
    $TYPEATTR = $this->$varname;
    $parent = $TYPEATTR["store_as"];
    if ($parent=="") $parent = $TYPEATTR["extends"];
    return array_merge(array($type),$this->types_getstoreasparents($parent));
  }
} //types_getstoreasparents

/* We currently use the constructor of the class to pre-compute the
 * attributes of each field. They're stored in $attributes_cache. */
function zbase_support() {
  foreach ($this->FIELDS as $field) {
    $this->attributes_cache[$field] = $this->types_computeattr($field);
  }

  services::services();
} //zbase_support


/*
 * Utilities used to choose a type in all parents of the field.
 */

/* Choose the best display type for the specified field in the specified 
 * known types list (which countains at least the 3 primary types) */
function types_bestdisplay ($field, $knowntypes)
{
  $attr = $this->types_getattr($field);
  $display_as = $attr ["display_as"];
  foreach ($display_as as $type)
    if (srv_isin ($knowntypes, $type)) return $type;
} //types_bestdisplay


/* types_beststore : The same, for the storage. */
function types_beststore ($field, $knowntypes="")
{
  if ($knowntypes=="") $knowntypes = $this->GT_KNOWN_TYPES;
  $attr = $this->types_getattr($field);
  $store_as = $attr ["store_as"];
  foreach ($store_as as $type)
    if (srv_isin ($knowntypes, $type)) return $type;
} //types_beststore


/*
 * Content validation
 */

/* Verify that the variables in $row are consistent with their attributes.
 * Return FALSE or an array of couples (name, reason), where reason can
 * be :
 *   regexp         The regular expression check has failed
 *   size           Field too large
 *   min            Value too small
 *   max            Value too high
 *   func           Check failed in the func_verify function
 * Other reasons may be added in the future. You should display a standard
 * error message for them.
 */
function types_verify()
{
  foreach ($this->FIELDS as $field)
  {
    $field_test = $this->types_verify_field ($field);
    if ($field_test)
    {
      $error[] = $field." (".$field_test.")";
    }
  }
  if ($error)
    srv_seterr("types_support:types_verify()",
               srv_str("types", "verify_err").implode(", ", $error));
} //types_verify

/* The following functions are private : */

/* Verify the coherence between the content of a field and its attributes. */
function types_verify_field($field)
{
  $ATTR = $this->types_getattr ($field);
  $value = $this->row["$field"];
  // Checks structural information first
  // -> size
  if ($ATTR["size"] > 0 && strlen($value)>$ATTR["size"])
    return "size";

  // -> regexp 
  if ($ATTR["regexp"] != "" && strlen($value))
  {
    if (!ereg($ATTR["regexp"],$value)) return "regexp";
  }

  if ($ATTR["required"] && strlen($value) == 0)
    return "required";

  // Interval control
  if ($ATTR["min"] != "" && $ATTR["min"] > $value) return "min";
  if ($ATTR["max"] != "" && $ATTR["max"] < $value) return "max";

  // Custom control
  if ($ATTR["func_verify"] != "")
  {
    $func=$ATTR["func_verify"];
    if ($this->$func($field)) return "func_verify";
  }
} //types_verify_field


/*
 * Content manipulation
 */

/* Return the content of the field ready for displaying (e.g. transform
 * the boolean type in yes or no). */
function types_show($field) 
{
  // The data is supposed to have already been checked by types_verify
  $attr_field = $this->types_getattr($field);
  if  ($attr_field["func_show"] != "") 
    return $this->$attr_field["func_show"]($field);
  else
    return $this->row[$field];
} //types_show

/* Return the content of the field ready for editing (ie. call the
 * func_form functions). */
function types_form($field, &$more_attr) 
{
  // The data is supposed to have already been checked by types_verify
  $attr_field = $this->types_getattr($field);
  if  ($attr_field["func_form"] != "")
    return $this->$attr_field["func_form"]($field, &$more_attr);
  else
    return $this->row[$field];
} //types_form

/* Put $value in $row after calling func_retrieve. */
function types_retrieve($field, $value) 
{
  $attr_field = $this->types_getattr($field);
  if ($temp = $attr_field["func_retrieve"])
    return $this->$temp($field, stripslashes($value));
  else
    return $value;
} //types_retrieve

/* Set field value to defaults. */
function types_default($fields = "") {
  if (!$fields)
    $fields = $this->FIELDS;
  else
    $fields = srv_makearray($fields);
  foreach($fields as $field) {
    $attr_field = $this->types_getattr($field);
    $this->row["$field"] = $attr_field["default"];
  }
} //types_default


/****************************************************************************
 * show_support.inc : Support for display routines
 ****************************************************************************/

/* Show a simple row, allowing optionnally to edit it. Uses the row helper
 * intensively. */
function show_row($fields = "", $to_edit = "", $to_merge = "", $link = "") {
  global $ROW_OBJ;
  if (!is_object($ROW_OBJ)) {
    $ROW_OBJ = new row;
    if ($this->TITLE)
      $attributes = array("title" => strtoupper($this->TITLE));
    else
      $attributes = array("title" => strtoupper($this->TABLE));
    $attributes["banner"] = $this->show_getbanner();
    if ($link)
      $attributes["link"] = $link;
    $ROW_OBJ->row_begin($attributes);
    $end_row = TRUE;
  }
  $ROW_OBJ->row_newobj(array("name" => $this->TABLE, "keys" => srv_getfields($this->row, $this->KEYS)));

  if (!$fields || $fields == "*")
    $fields = $this->FIELDS;
  if ($to_edit == "*")
    $to_edit = $this->FIELDS;
  if ($to_merge == "*")
    $to_merge = $fields;

  $to_edit = srv_makearray($to_edit);
  $to_merge = srv_makearray($to_merge);

  foreach ($this->FIELDS as $field) {
    $attr = $this->types_getattr($field);
    $caption_attr = array("style" => 0);
    $attr["style"] = $this->show_getstyle($field);
    $attr["bgcolor"] = $this->show_getbgcolor($field);

    if (!srv_isin($fields, $field))
      $attr["type"] = $attr["display_as"] = array("hidden");

    if (srv_isin($to_merge, $field))
      $attr["merged"] = TRUE;

    if ($link = $this->show_getfieldlink($field))
      $attr["link"] = $link;

    if (srv_isin($this->KEYS, $field)) {
      /* Make keys larger */
      $caption_attr["colspan"] = 2;
      if ($attr["colspan"] < 2)
        $attr["colspan"] = 2;
    }

    /* Retrieve the content of the field */
    $more_attr = "";
    if (!srv_isin($to_edit, $field))
      $content = $this->types_show($field);
    else {
      $content = $this->types_form($field, &$more_attr);
      if ($more_attr)
        $attr = array_merge($attr, $more_attr);
      /* Clear link flag */
      $attr["link"] = "";
    }

    if ($attr["type"][0] != "hidden")
      /* Make two cells */
      $ROW_OBJ->row_newfield($attr["caption"] ? $attr["caption"] : $field, 
                             $content, $caption_attr, $attr,
                             (srv_isin($to_edit, $field)) ? $field : "");
    else
      echo $ROW_OBJ->form->form_newinput($field, $content, $attr);
  }

  if ($end_row) {
    $ROW_OBJ->row_end();
    $ROW_OBJ = ""; /* clean up */
  }
} //show_row

/* Manage a list of several rows. Uses the list helper. */
function show_list_begin($fields = "", $link = "") {
  if (!$fields)
    $fields = $this->SHOW_LIST;
  if (!$fields)
    $fields = $this->FIELDS;

  global $LIST_OBJ;
  if (!is_object($LIST_OBJ)) {
    $LIST_OBJ = new zblist;
    if ($this->TITLE)
      $attributes = array("title" => strtoupper($this->TITLE));
    else
      $attributes = array("title" => strtoupper($this->TABLE));
    if ($link)
      $attributes["link"] = $link;

    foreach ($fields as $field) {
      $field_attr = $this->types_getattr($field);
      $headers[$field] = $field_attr["caption"] ?
                                 $field_attr["caption"] : $field;
      $headers_attr[$field] = array("style" => 0,
                                    "colspan" => $field_attr["colspan"]);
    }
    $LIST_OBJ->list_begin($attributes, $headers, $headers_attr);
  }
} //show_list_begin

function show_list($fields = "", $to_edit = "", $to_merge = "") {
  if (!$fields)
    $fields = $this->SHOW_LIST;
  if (!$fields)
    $fields = $this->FIELDS;

  global $LIST_OBJ;

  foreach ($fields as $field) {
    /* Retrieve the content of the field */
    $attributes[$field] = $this->types_getattr($field);
    $attributes[$field]["style"] = $this->show_getstyle($field);
    $attributes[$field]["bgcolor"] = $this->show_getbgcolor($field);
    if (srv_isin($to_merge, $field))
      $attributes[$field]["merged"] = TRUE;
    if ($link = $this->show_getfieldlink($field))
      $attributes[$field]["link"] = $link;

    $more_attr = "";
    if (!srv_isin($to_edit, $field))
      $contents[$field] = $this->types_show($field);
    else {
      $contents[$field] = $this->types_form($field, &$more_attr);
      if ($more_attr)
        $attributes[$field] = array_merge($attributes[$field], $more_attr);
      $attributes[$field]["link"] = "";
    }
  } 
  $i = 1;
  $LIST_OBJ->list_newrow($contents, $attributes, 
                         srv_loadvar("htmlpage", "search_result")."?"
                         .$this->show_getdict(&$i)."&FORM_NB=".($i-1),
                         array("name" => $this->TABLE,
                               "keys" => srv_getfields($this->row,
                                                       $this->KEYS)),
                         $to_edit);
} //show_list

function show_list_end() {
  global $LIST_OBJ;
  $LIST_OBJ->list_end();
  $LIST_OBJ = "";
} //show_list_end


/* Return a URL-style pointer to this object, including its entire content
 * (for use in create.php).
 * $form_nb is the current form number used by the form class (usually 1)
 */
function show_getfields($form_nb, $fields = "", $to_merge = "") {
  if (!$fields)
    $fields = $this->FIELDS;

  $attributes["name"] = $this->TABLE;
  $attributes["keys"] = srv_getfields($this->row, $this->KEYS);
  $attributes["raw"] = 1;
  $temp[] = "FORM_".$form_nb."_ATTR=".urlencode(serialize($attributes));

  foreach ($fields as $field) {
    if (srv_isin($to_merge, $field))
      $temp[] = "FORM_".$field."=".urlencode($this->row[$field]);
    else
      $temp[] = "FORM_".$form_nb."_".$field."=".urlencode($this->row[$field]);
  }

  return implode("&", $temp);
} //show_getfields

/* Return a URL-style pointer to this object, including only its keys (for
 * use in modify.php and delete_result.php). */
function show_getkeys($form_nb) {
  $attributes["name"] = $this->TABLE;
  $attributes["keys"] = srv_getfields($this->row, $this->KEYS);
  $attributes["omitted"] = 1;
  $temp[] = "FORM_".$form_nb."_ATTR=".urlencode(serialize($attributes));

  return implode("&", $temp);
} //show_getkeys

/* Return a URL-style pointer to this object, including something suitable
 * for search_result.php. */
function show_getdict($form_nb, $dict = "") {
  if (!$dict)
    $dict = srv_getfields($this->row, $this->KEYS);
  foreach($dict as $key => $val) {
    $temp[] = "FORM_".$form_nb."_table=".$this->TABLE;
    $temp[] = "FORM_".$form_nb."_ATTR=".urlencode(serialize(array("raw" => 1)));
    $temp[] = "FORM_".$form_nb."_field=".$key;
    $temp[] = "FORM_".$form_nb."_operator=".urlencode("=");
    $temp[] = "FORM_".$form_nb."_value=".urlencode($val);
    $form_nb++;
  }

  return implode("&", $temp);
} //show_getdict


/*
 * Following functions are local to the show_* files ONLY.
 */

/* Return a standard banner. */
function show_getbanner() {
  /* A simple banner with four buttons, search, insert, modify, delete */
  return array(srv_str("show", "banner_main") => array(
    srv_str("show", "banner_search")
      => srv_url(srv_loadvar("htmlpage", "search_result")."?FORM_NB=1&"
                 .$this->show_getdict(1)),
    srv_str("show", "banner_create")
      => srv_url(srv_loadvar("htmlpage", "create")."?FORM_NB=1&"
                 .$this->show_getfields(1, srv_delfields($this->FIELDS, $this->KEYS))),
    srv_str("show", "banner_modify")
      => srv_url(srv_loadvar("htmlpage", "modify")."?FORM_NB=1&"
                 .$this->show_getkeys(1)),
    srv_str("show", "banner_delete")
      => srv_url(srv_loadvar("htmlpage", "delete")."?FORM_NB=1&"
                 .$this->show_getkeys(1))
  ));
} //show_getbanner

/* Return a link that can be used as a "link" attribute for the table
 * helper. */
function show_getfieldlink($field) {
  $attr = $this->types_getattr($field);
  if ($attr["link"])
    return $attr["link"];
  elseif ($temp = $attr["func_link"])
    return $this->$temp($field);

  if (is_array($this->DEP_FIELDS)) {
    $link = srv_loadvar("htmlpage", "search_result");  

    foreach ($this->DEP_FIELDS as $table => $fields) {
      if (srv_isin(srv_makearray($fields), $field)) {
        /* We have found a possible table. */

        srv_include($table);
        $object = new $table;
        if ($dict = $object->search_extractdict($this))
          return $link."?FORM_NB=1&".$object->show_getdict(1, $dict);
      }
    }
  }
} //show_getfieldlink

/* Return a bitmask value for the style attribute of tables. */
function show_getstyle($field) {
  $attr = $this->types_getattr($field);
  return $attr["style"] | 1; /* bold */
} //show_getstyle

/* Can return a color value for the bgcolor attribute of tables. */
function show_getbgcolor($field) {
  $attr = $this->types_getattr($field);
  return $attr["bgcolor"];
} //show_getbgcolor


/*****************************************************************************
 * gate_support : Common methods for all gates
 *****************************************************************************/

/*
 * Public methods. Bear in mind that these functions are provided here as
 * some useful tools for the developers of new gates, but you're not
 * obliged to use them.
 */

/* Return an array of the names of the fields that have changed between
 * $row and $old_row. Should not be used by a class higher than 
 * gate_support. */
function gt_diffrows($old_row, $row) {
  $answer = array();
  foreach($this->FIELDS as $field)
  {
    if ($old_row[$field]!=$row[$field]) 
      $answer[]=$field;
  }
  return $answer;
} //gt_diffrows

/* Encode the value of the field so that it can be understood by the
 * database with the given operator. This function has been tested with
 * PostgreSQL, it is more than expected that you will have to overload
 * it for other databases. Should not be used by a class higher than
 * gate_support. */
function gt_mask($field, $value) {
  $type=$this->types_beststore($field);

  $value = srv_makearray($value);
  list($value, $operator) = $value;
  if (strlen($operator) == 0) $operator="=";

  if (substr($operator,0,1) == "!") {
    $negation="not ";
    $operator=substr($operator,1);
  } else
    $negation="";

  /* Hybrid operator where empty == not taken into account. */
  if ($operator == "=?") {
    if (strlen($value))
      $operator = "=";
    else
      return "true";
  }

  /* Operators used for numbers. */
  if (    srv_isin(array("=", ">", "<", ">=", "<="),$operator)
       && srv_isin(array("float", "int"), $type))
    if (strlen($value))
      return "$negation"."$field $operator $value";
    else
      return "$negation"."$field is NULL";

  /* Operators used for strings (and unknown types). */

  /* Hybrid operator to search a string which "begins with". */
  if ($operator == "=%" && $type == "text") {
    $value = "^$value";
    $operator = "#";
  }

  /* Curious operator allowing to search regular expressions. */
  if ($operator == "#" && $type == "text") {
    $value=strtolower($value);
    $value=ereg_replace("[éèëêe]","\[éëêèe\]",$value);
    $value=ereg_replace("[aàâä]","\[aàâä\]",$value);
    $value=ereg_replace("[iïî]","\[iïî\]",$value);
    $value=ereg_replace("[uüûù]","\[uüûù\]",$value);
    $value=ereg_replace("[oöô]","\[oöô\]",$value);
    $value=ereg_replace("[yÿ]","\[yÿ\]",$value);
    $value=ereg_replace("[cç]","\[cç\]",$value);
    return "$negation"."$field ~* '$value'";
  }

  /* Default return */
  return "$negation"."$field ".$operator." '".$value."'";
} //gt_mask

/* Encode the entire dictionary with the previous function. */
function gt_where($dict) {
  if ($dict) {
    $and="";
    $request =" where ";
    foreach ($dict as $field => $value)
    {
      $mask=$this->gt_mask($field,$value);
      if ($mask==-1) return -1;
      $request.= $and.$mask;
      $and = " AND ";
    }
    return $request;
  }
} //gt_where

/* Translate ZBase vars (in $row) into something understandable by a
 * database server. */
function gt_encode_value($value,$field) {
  switch ($this->types_beststore($field)) {
    case "int":
    case "float":
      if (strlen($value))
        return $value;
      else
        return "NULL";
      break;
    case "text":
    default:
      return "'".addslashes(trim($value))."'";
  }
} //gt_encode_value

/* Encode the entire row with the previous function. */
function gt_encode_row($fields) {
  foreach ($this->FIELDS as $field)
    $answer["$field"] = $this->gt_encode_value($fields[$field], $field);
  return $answer;
} //gt_encode_row


} //class zbase_support

$GLOBALS[setzbase_support] = TRUE;
} //setzbase_support
?>
Return current item: ZBase