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.
<includeonce>//
<include 
component.hei>//
<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 = 
"Fm";
    
else;
      context = 
"F";
    
/if;
    
_addcontext context=context priv="T"defbody; /_addcontext;
  
/defenv;

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

  
// 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;
    
/if;
    this.isinitial = this.isinitial && this.ff[obj.name] == value;
    arappend (this.fields, obj);
  
/def;

  
/* 
  // 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;
    /if;
    this.isinitial = this.isinitial && (this.clvalue[name] == cliv);
    return this.clvalue[name];
  /def;
  */


  
// 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);
  
/def;

  
// 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
  
/def;

  
// 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;
  
/def;

  
// 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);
  
/def;

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

  
// 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);
    
/forin;
    
// 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;
        
/if;
      
/forin;
    
/if;
    
return subbtn;
  
/def;

  
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;
    
/forin;
    this.errmsg = emptytuple;
  
/def;

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

  
// 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);
      scratch=emptytuple;
      
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]);
          else 
             this.clvalue[field.name]=in[field.name];/if;
             TuDelField (this.ff[field.name]);
          /if;
*/

        
/if;
      
/forin;
    
/if;
    
if isdecl (this.errmsg) && len (this.errmsg) != 0;
      res = 
"Bad form fields"; this.ok = false;
    
/if;
    
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;
      
/if;
    
/forin;
    
return res;
  
/def;

  
// 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;
    
/forin;
    
// process this panel
    
// EXTEND field.process() necessary?
    
return null;
  
/def;

  
// 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, 
"enabled");
    
// 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;
    this.passwdok=null;
  
/defenv;
/defclass


// 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";
        
/if;
      
/forin;
      
forin i panel.subpanels;
        this.jsemiterrors (panel.subpanels[i], false); 
      
/forin;
    
/if;
  
/def;

  
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;
      
forin i panel.subpanels;
        this.emiterrors (panel.subpanels[i], delim, false);
      
/forin;
      panel.erremitted = true;
    
/if;
  
/def;

  
def clearerrors panel;
      
forin i panel.subpanels;
        this.clearerrors (panel.subpanels[i]);
      
/forin;
      panel.errmsg=emptytuple;
  
/def;

  
_marker;
  
if htmlbool(js);
    
assign m; this.jsemiterrors (gl._curpanel); /assign;
    
if !isempty(trim(m)); js> alert(<? m "J"></js; /if;
  
/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;
  
/if;
  this.clearerrors (gl._curpanel);
  
/_marker; 
/def


// 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;
    
/if;
    tmp = submit (this.btn);
    ot = this.oncheck(msg);
    
if isnull(ot); return tmp; else this.ok=false; return ot; /if;
  
/def;

  
// 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;
  
/def;

  
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;
        
defbody;
        
if !this.erremitted;  // If error messages are not yet shown do it now
            >
<br><panelerrors html color="red" size="+1";
        
/if;
    
/dosimpleform;
    TuDelField (this.errmsg);
    TuDelField (this.erremitted);
    TuDelField (this.passwdok);
  
/defenv;  

/defclass>