heitml Source Display

File: /usr/local/httpd/htdocs/heitml2.0/lib/formbase.hei

// Basic Form Components
// $Id: formbase.hei,v 1.87 2000/08/29 19:20:23 heitml Exp $
// Copyright (C) 1996-2000 by H.E.I. Informationssysteme GmbH and suppliers.
// This file is part of heitml.  All licensing conditions of the
// heitml package apply.  See the license file contained in the heitml
// distribution.
<include std.hei>//
<include javasc.hei>//
<include browcomp.hei>//
<include panel.hei>//
// <include formfields.hei>//
// Use of globals:
//   gl._curpanel               current panel
//   gl._curselect              current select
// /Use
// Form environments
// Abstract base class for panels (sets of controls).
// A panel is either a form or a sub-panel of another panel.
// A panel may contain fields, buttons and sub-panels.
defclass _panelbase;
inherit component, panel;

// Attribute members:
//   array buttons            panel's buttons
//   array fields             panel's fields
//   array subpanels          panel's sub-panels
//   object encpanel  panel that has this as sub-panel, possibly null
//   object form              panel's form
//   string panelid           unique panel id (must be unique within the form)
//   state                    panel state: display, invisible, enabled, disabled, readonly
//   bool erremitted          are panel's errors already emitted?
//   tuple errmsg             map field names to error message strings
//   tuple ff                 map field names to current form data,
//                              is panel's initial actual value attribute,
//                              !isnull (errmsg[name]) => !isdecl (ff[name])
//   tuple clvalue            map field names to client values, i.e.
//                              string(s) sent by client
//   bool isinitial           is ff just the inital field values 
//   string onsubmit          JavaScript event function

// Define context F form, T table, possibly m multipart/form-data.
defenv _evalcontext;
if    (tolower (default (this._param.method, "")) == "post")
       && (tolower (default (this._param.enctype, 
"")) == "multipart/form-data");
      context = 
      context = 
_addcontext context=context priv="T"defbody; /_addcontext;

// Return control name for field name NAME.
def control_name name;
return this.panelid + '.' + name;

// Register a sub-panel.
def regsubpanel obj; arappend (this.subpanels, obj); /def;

// Register a button OBJ.
def regbutton obj; arappend (this.buttons, obj); /def;

// Register a field OBJ.
// If VALUE is not null, it overrides the field's initial value.
def regfield obj value;
if !isdecl(this.ff[obj.name]) && ! (isdecl(this.clvalue) && isdecl(this.clvalue[obj.name]));
        this.ff[obj.name] = value;
    this.isinitial = this.isinitial && this.ff[obj.name] == value;
    arappend (this.fields, obj);

  // Define and return this.clvalue[NAME].
  // CLFF is this.ff[NAME] transformed into internal field state.
  // CLIV is this.ivalue[NAME] transformed into internal field state.
  // Update this.isinitial accordingly.
  def clvalue name clff cliv;
    if isdecl (this.ff[name]);
      this.clvalue[name] = clff;
    elsif !isdecl (this.clvalue[name]);
      this.clvalue[name] = cliv;
    this.isinitial = this.isinitial && (this.clvalue[name] == cliv);
    return this.clvalue[name];

// Report error ERRMSG for field NAME, or panel if NAME is "".
def error errmsg name;
// EXTEND one name cannot have multiple errors
    this.errmsg[name] = default (this.errmsg[name], errmsg);

// To be called if a field does not emit a control.
// Ensure that the association of NAME with VALUE is available for
// the processing agent.
def save_value name value;
<input type="hidden" name=<? this.control_name (name) "P"> <? value "Pn">><hei

// Return whether to perform a mandatroy check, called during processing
// MANDATORY is the field's idea.
// NOTE this is called during form processing not during form display
def field_mandatory field mandatory;
return mandatory;

// Return field state.
// STATE is the field's idea, may be null.
def field_state field state;
//    ? state; ? field.state;
return default (state, this.state);

// Return BUTTON state.
// STATE is the button's idea, may be null.
def button_state button state;
return default (state, this.state);

// Update this.clvalue from MSG, return pressed button if any.
def update msg;
    subbtn = null;
// update sub-panels
forin i this.subpanels;
      subbtn = default (this.subpanels[i].update(msg), subbtn);
// update panel state
if !isnull(ff[this.panelid]);
// this.clvalue = tuassign (emptytuple, ff[this.panelid]);
// search for button
forin i this.buttons;
        btn = this.buttons[i];
if !isempty (ff[this.panelid][btn.name]) && (isnull (btn.value) || 
                    (ff[this.panelid][btn.name] == btn.value));
return btn;
return subbtn;

def clearffenabled;
// Delete all enabled form fields 
forin i this.fields;
if this.fields[i].state!="display"; tudelfield (this.ff[this.fields[i].name]); /if;
    this.errmsg = emptytuple;

// Reset this panel and its sub-panels to their initial values.
def reset;
    this.ff = emptytuple;
    this.clvalue = emptytuple;
forin i this.subpanels;

// Submit this panel's and its sub-panels' clvalue into the respective ff.
// Button BTN triggered the submission.
// Return null for success, else string.
def submit btn;
    res = null; this.ok=true;
    this.errmsg = emptytuple;
    in = ff[this.panelid];
// clearffenabled(); 
// EXTEND button should be able to determine fields to check
if !isempty(in);
forin i this.fields;      // filter through check() into ff
        field = this.fields[i];
if field.state!="display";
          field.fcheck (in this.ff this.clvalue scratch);
          val = field.check (default (in[field.name]));
          if !isdecl (this.errmsg[field.name]);
             this.ff[field.name] = val; TuDelField (this.clvalue[field.name]);
             TuDelField (this.ff[field.name]);

if isdecl (this.errmsg) && len (this.errmsg) != 0;
      res = 
"Bad form fields"; this.ok = false;
forin i this.subpanels;     // submit sub-panelsTuDelField (this.ff[field.name])
if !isnull (this.subpanels[i].submit (btn));
        res = 
"Bad form fields"; this. ok = false;
return res;

// Process this panel and its sub panels.
// Return null on success, string on failure.
// EXTEND rename to process() and add argument ff when heitml can
// EXTEND call shadowed inherited code
def process_panel;
// process sub-panels
forin i this.subpanels;
      tmp = this.subpanels[i].process_panel();
if !isnull (tmp); return tmp; /if;
// process this panel
// EXTEND field.process() necessary?
return null;

// Each panel in a form needs a unique PANELID.
// The panel edits the tuple VALUE, i.e. reads initial values from
// it and stores the edited values into it.
// STATE is a default for the panel's fields, see field_state().
// Caution: The edited tuple is sticky, i.e. it is fixed when the
// panel is created.  Further calls do not switch the edited tuple.
defenv dopanel;
//    this.panelid = panelid;
if !isdecl(this._bid); this._bid=stdmakeuniq("p");/if;
    this.panelid = this._bid;
//    this.state = state;
    this.state = default (this.state, 
// this.ivalue = tuassign (emptytuple, this.ff);
// this.isinitial = true;
// leave alone this.clvalue, this.ff and this.errmsg, they are
// set by reset(), submit()
if !isempty(gl._curpanel); gl._curpanel.regsubpanel(this); /if;
    this.clvalue = default (this.clvalue, emptytuple);
    this.ff = default (value, this.ff, emptytuple);
    this.errmsg = default (this.errmsg, emptytuple);
    this.erremitted = false;
    this.subpanels = array(0);
    this.fields = array(0);
    this.buttons = array(0);
_dopanel; defbody; /_dopanel;

// Emit unemitted errors for panel and its subpanels delimited by DELIM
// DELIM must not contain "%" WHY ?
def panelerrors js=null html=null delim="<br>" @rest ...;
inherit component;

def jsemiterrors panel;
if !htmlbool(panel.erremitted)&& isdecl(panel.errmsg);
forin i panel.errmsg;
if !isempty(panel.errmsg[i]);
? panel.errmsg[i] "H"?"\n";
forin i panel.subpanels;
        this.jsemiterrors (panel.subpanels[i], false); 

def emiterrors panel delim first;
if !htmlbool(panel.erremitted) && isdecl(panel.errmsg);
forin i panel.errmsg;
if !first; ? delim "A"/if;
? panel.errmsg[i] "H";
// EXTEND link that moves focus to bad field
        first = false;
forin i panel.subpanels;
        this.emiterrors (panel.subpanels[i], delim, false);
      panel.erremitted = true;

def clearerrors panel;
forin i panel.subpanels;
        this.clearerrors (panel.subpanels[i]);

if htmlbool(js);
assign m; this.jsemiterrors (gl._curpanel); /assign;
if !isempty(trim(m)); js> alert(<? m "J"></js; /if;
if htmlbool(html) || (!htmlbool(html) && !htmlbool(js));
if !isempty(rest) ><\font <? rest "Pn">>< /if;
emiterrors (gl._curpanel, delim, true); 
if !isempty(rest) ><\/font>< /if;
  this.clearerrors (gl._curpanel);

// Abstract base class for sesform and sleform
// Adds form and field checking and processing and error handling
// Provides means to process the control data set: check() updates the
// form state and filters each field's clvalue[field.name] through
// field.check() into this.ff[field.name].  After successful check(),
// process() calls the pressed button's process().
defclass _formbase;
inherit _panelbase, simpleform;

// Attribute members:
//   string _bid              see control_name(), interactive
//   bool success             form successfully submitted?
//   object btn               the submit button object
//   string enctype           attribute for HTML form element
//   string method            attribute for HTML form element

// oncheck hook called after checking form fields
def oncheck msg; return null; /def;     

// Check MSG sent by client and update form state.
// Return null if MSG is valid, else string.
// Implements callback interactive.check().
// Update and submit or reset panel if necessary, define this.btn.
def check msg;
    this.success=false; this.passwdok=null;
    this.btn = update (msg); 
if !isnull(this.btn) && this.btn.type == "reset";
      this.btn.panel.reset(); this.ok=false;
return null;
    tmp = submit (this.btn);
    ot = this.oncheck(msg);
if isnull(ot); return tmp; else this.ok=false; return ot; /if;

// Process MSG sent by client.
// Return null on success, string on failure.
// Implements callback interactive.process().
// Process panels, then button, stopping on errors.
def _formbase_process msg;
if !isnull(this.btn) && this.btn.type == "reset"return null; /if;
    tmp = process_panel();
if !isnull (tmp); return tmp; /if;
if isnull(this.btn); return null; /if;    // no button, nothing to process
    tmp = this.btn.process (this.btn.panel.ff);
if isnull(tmp); this.success = true; return null; /if;
if isbool (tmp); this.success = tmp; return null; /if;
error (tmp, "");
return tmp;

defenv doformbase value state method enctype action onsubmit name rest;
if state=="invisible"return; /if;
if !isdecl(this.ok); this.ok=true; /if;
    this.btn = default (this.btn, null);
    this.success = default (this.success, false);
dosimpleform  value state method enctype action onsubmit name rest;
if !this.erremitted;  // If error messages are not yet shown do it now
<br><panelerrors html color="red" size="+1";
    TuDelField (this.errmsg);
    TuDelField (this.erremitted);
    TuDelField (this.passwdok);