511b1d2000-05-16Martin Nilsson // This is a roxen module. Copyright © 2000, Roxen IS. // By Martin Nilsson #include <module.h> inherit "module";
9b03652001-03-07Kenneth Johansson constant cvs_version = "$Id: vform.pike,v 1.21 2001/03/07 13:42:14 kuntri Exp $";
67f1b62001-02-10Martin Nilsson constant thread_safe = 1;
511b1d2000-05-16Martin Nilsson 
b3281f2000-09-10Martin Nilsson constant module_type = MODULE_TAG;
511b1d2000-05-16Martin Nilsson constant module_name = "Verified form"; constant module_doc = "Creates a self verifying form.";
dd6a3b2000-11-29Martin Nilsson // maxlength is excluded so that it gets exported.
2f547a2000-09-14Martin Nilsson constant ARGS=(< "type", "min", "max", "scope", "min", "max", "trim"
dd6a3b2000-11-29Martin Nilsson  "regexp", "glob", "minlength", "case",
2f547a2000-09-14Martin Nilsson  "mode", "fail-if-failed", "ignore-if-false", "ignore-if-failed", "ignore-if-verified", "optional" >);
511b1d2000-05-16Martin Nilsson 
2f547a2000-09-14Martin Nilsson constant forbidden = ({"\\", ".", "[", "]", "^", "$", "(", ")", "*", "+", "|"}); constant allowed = ({"\\\\", "\\.", "\\[", "\\]", "\\^", "\\$", "\\(", "\\)", "\\*", "\\+", "\\|"});
511b1d2000-05-16Martin Nilsson 
67f1b62001-02-10Martin Nilsson Parser.HTML verified; Parser.HTML failed; void create() { verified = Parser.HTML()-> add_containers( ([ "verified" : lambda(Parser.HTML p, mapping m, string c) { return c; }, "failed" : "" ]) ); failed = Parser.HTML()-> add_containers( ([ "failed" : lambda(Parser.HTML p, mapping m, string c) { return c; }, "verified" : "" ]) ); }
2f547a2000-09-14Martin Nilsson class VInputFrame { inherit RXML.Frame; string scope_name; mapping vars;
511b1d2000-05-16Martin Nilsson 
2f547a2000-09-14Martin Nilsson  object var;
511b1d2000-05-16Martin Nilsson 
2f547a2000-09-14Martin Nilsson  array do_enter(RequestID id) { scope_name=args->scope||"vinput";
511b1d2000-05-16Martin Nilsson 
0035372000-08-25Martin Nilsson #ifdef VFORM_COMPAT
2f547a2000-09-14Martin Nilsson  if(args->is) { switch(args->is) { case "int": args->type="int"; break; case "float": args->type="float"; break; case "mail": args->type="email"; break; case "date": args->type="date"; break; case "upper-alpha": args->regexp="^[A-Z]*$"; break; case "lower-aplha": args->regexp="^[a-z]*$"; break; case "upper-alpha-num": args->regexp="^[A-Z0-9]*$"; break; case "lower-alpha-num": args->regexp="^[a-z0-9]*$"; break; } m_delete(args, "is"); } if(args->filter) { args->regexp="^["+args->filter+"]*$"; m_delete(args, "filter"); }
0035372000-08-25Martin Nilsson #endif
511b1d2000-05-16Martin Nilsson 
73866a2001-01-26Martin Nilsson  var = id->misc->vform_objects[args->name];
2f547a2000-09-14Martin Nilsson  switch(args->type) { case "int":
73866a2001-01-26Martin Nilsson  if(!var) var=Variable.Int(args->value||"");
2f547a2000-09-14Martin Nilsson  var->set_range((int)args->min, (int)args->max); break; case "float":
73866a2001-01-26Martin Nilsson  if(!var) var=Variable.Float(args->value||"");
2f547a2000-09-14Martin Nilsson  var->set_range((float)args->min, (float)args->max); break; case "email":
73866a2001-01-26Martin Nilsson  if(!var) var=Variable.Email(args->value||"");
5adf512000-09-17Martin Nilsson  if(args["disable-domain-check"]) var->disable_domain_check();
2f547a2000-09-14Martin Nilsson  break; case "date":
73866a2001-01-26Martin Nilsson  if(!var) var=Variable.Date(args->value||"");
2f547a2000-09-14Martin Nilsson  break;
2cd3212000-12-19Martin Nilsson  case "image":
73866a2001-01-26Martin Nilsson  if(!var) var=Variable.Image( args->value||"", 0, 0, 0 );
2cd3212000-12-19Martin Nilsson  break;
d25fde2000-10-17Per Hedbor // case "upload":
73866a2001-01-26Martin Nilsson // if(!var) var=Variable.Upload( args->value||"", 0, 0, 0 );
d25fde2000-10-17Per Hedbor // break;
2cd3212000-12-19Martin Nilsson  case "password":
73866a2001-01-26Martin Nilsson  if(!var) var=Variable.VerifiedPassword(args->value||"");
2f547a2000-09-14Martin Nilsson  case "text":
2cd3212000-12-19Martin Nilsson  if(!var) var=Variable.VerifiedText(args->value||"");
2f547a2000-09-14Martin Nilsson  case "string": if(!var) var=Variable.VerifiedString(args->value||""); if(args->regexp) var->add_regexp(args->regexp); if(args->glob) var->add_glob(args->glob); if(args->minlength) var->add_minlength((int)args->minlength); if(args->maxlength) var->add_maxlength((int)args->maxlength); if(args->case=="upper") var->add_upper(); else if(args->case=="lower") var->add_lower(); // Shortcuts if(args->equal) var->add_regexp( "^" + replace(args->equal, forbidden, allowed) + "$" ); if(args->is=="empty") var->add_glob(""); break;
2cd3212000-12-19Martin Nilsson  default: RXML.parse_error("There is no type %s.\n", args->type);
2f547a2000-09-14Martin Nilsson  }
511b1d2000-05-16Martin Nilsson 
d25fde2000-10-17Per Hedbor  var->set_path( args->name );
0909d62001-02-11Martin Nilsson  if(!id->real_variables["__clear"] && id->real_variables[args->name] && !(args->optional && id->real_variables[args->name][0]=="") )
d25fde2000-10-17Per Hedbor  { if(args->trim)
0909d62001-02-11Martin Nilsson  id->real_variables[args->name][0] = String.trim_all_whites(id->real_variables[args->name][0]);
d25fde2000-10-17Per Hedbor  var->set_from_form( id );
2f547a2000-09-14Martin Nilsson  }
292cb62000-08-21Marcus Wellhardh 
2f547a2000-09-14Martin Nilsson  mapping new_args=([]); foreach(indices(args), string arg) if(!ARGS[arg]) new_args[arg]=args[arg];
0035372000-08-25Martin Nilsson 
2f547a2000-09-14Martin Nilsson  vars=([ "input":var->render_form(id, new_args) ]);
5adf512000-09-17Martin Nilsson  if(var->get_warnings()) vars->warning=var->get_warnings();
73866a2001-01-26Martin Nilsson  id->misc->vform_objects[args->name] = var;
2f547a2000-09-14Martin Nilsson  return 0; }
511b1d2000-05-16Martin Nilsson 
2f547a2000-09-14Martin Nilsson  array do_return(RequestID id) {
5adf512000-09-17Martin Nilsson  int ok=!var->get_warnings();
2f547a2000-09-14Martin Nilsson  int show_err=1; if(args["fail-if-failed"] && id->misc->vform_failed[args["fail-if-failed"]]) ok=1;
0909d62001-02-11Martin Nilsson  if( (!id->real_variables[args->name] && !id->misc->vform_objects[args->name]) ||
2f547a2000-09-14Martin Nilsson  (args["ignore-if-false"] && !id->misc->vform_ok) ||
0909d62001-02-11Martin Nilsson  id->real_variables["__reload"] || id->real_variables["__clear"] ||
2f547a2000-09-14Martin Nilsson  (args["ignore-if-failed"] && id->misc->vform_failed[args["ignore-if-failed"]]) || (args["ignore-if-verified"] && id->misc->vform_verified[args["ignore-if-verified"]]) ) { ok=0; show_err=0; }
511b1d2000-05-16Martin Nilsson 
2f547a2000-09-14Martin Nilsson  if(ok) { id->misc->vform_verified[args->name]=1; verified_result(id); return 0; }
0035372000-08-25Martin Nilsson 
2f547a2000-09-14Martin Nilsson  id->misc->vform_failed[args->name]=1; if(show_err) failed_result(id);
5adf512000-09-17Martin Nilsson  else { m_delete(args, "warning");
2f547a2000-09-14Martin Nilsson  verified_result(id);
5adf512000-09-17Martin Nilsson  }
2f547a2000-09-14Martin Nilsson  id->misc->vform_ok = 0; return 0; }
511b1d2000-05-16Martin Nilsson 
2f547a2000-09-14Martin Nilsson  void verified_result(RequestID id ) // Create a tag result without error response. { switch(args->mode||"after") { case "complex":
67f1b62001-02-10Martin Nilsson  result = verified->clone()->finish(content)->read();
2f547a2000-09-14Martin Nilsson  break; case "before": case "after": default: result = RXML.get_var("input"); } }
511b1d2000-05-16Martin Nilsson 
2f547a2000-09-14Martin Nilsson  void failed_result(RequestID id) // Creates a tag result with widget and error response. { switch(args->mode||"after") { case "complex":
67f1b62001-02-10Martin Nilsson  result = failed->clone()->finish(content)->read();
2f547a2000-09-14Martin Nilsson  break; case "before": result = content + var->render_form(id, args); case "after": default: result = RXML.get_var("input") + content;
511b1d2000-05-16Martin Nilsson  } }
2f547a2000-09-14Martin Nilsson } class VInput { inherit RXML.Tag; constant name = "vinput"; mapping(string:RXML.Type) req_arg_types = ([ "name":RXML.t_text(RXML.PEnt) ]); class Frame { inherit VInputFrame; } } class TagWizzVInput {
1696152001-01-06Martin Nilsson  inherit RXML.Tag;
2f547a2000-09-14Martin Nilsson  constant name="wizz"; constant plugin_name="vinput";
1696152001-01-06Martin Nilsson  RXML.Tag get_tag() { return TagVInput(); } class TagVInput { inherit VInput; class Frame { inherit VInputFrame; array do_enter(RequestID id) { id->misc->vform_ok = id->misc->wizard->verify?id->misc->wizard->verify_ok:1; if(!id->misc->vform_verified) { id->misc->vform_verified=(<>); id->misc->vform_failed=(<>);
73866a2001-01-26Martin Nilsson  id->misc->vform_objects=([]);
1696152001-01-06Martin Nilsson  id->misc->vform_xml = !args->noxml; } ::do_enter(id); return 0; } array do_return(RequestID id) { ::do_return(id); id->misc->wizard->verify_ok = id->misc->vform_ok; return 0; } }
2f547a2000-09-14Martin Nilsson  } } class TagVForm { inherit RXML.Tag; constant name = "vform"; class TagVInput { inherit VInput; }
511b1d2000-05-16Martin Nilsson 
5b3d432000-09-14Martin Nilsson  class TagVSelect {
511b1d2000-05-16Martin Nilsson  inherit RXML.Tag; constant name = "vselect";
89034d2000-07-17Martin Nilsson  mapping(string:RXML.Type) req_arg_types = ([ "name":RXML.t_text(RXML.PEnt) ]);
511b1d2000-05-16Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) { int ok=1;
0909d62001-02-11Martin Nilsson  if(args->not && id->real_variables[args->name][0]==args->not) ok=0;
511b1d2000-05-16Martin Nilsson  m_delete(args, "not"); if(ok) { id->misc->vform_verified[args->name]=1;
e18b3d2000-08-22Martin Nilsson  result = RXML.t_xml->format_tag("select", args, content);
511b1d2000-05-16Martin Nilsson  } else { id->misc->vform_failed[args->name]=1; id->misc->vform_ok = 0; //Create error message switch(args->mode||"after") { case "complex": // not working... result = parse_html(content, ([]), ([ "failed":lambda(string t, mapping m, string c) { return c; }, "verified":"" ]) ); break; case "before": string error = parse_html(content, ([]), ([ "error-message":lambda(string t, mapping m, string c) { return c; }, "option":"" ]) );
e18b3d2000-08-22Martin Nilsson  result = error + RXML.t_xml->format_tag("select", args, content);
511b1d2000-05-16Martin Nilsson  case "after": default:
02a63f2000-09-05Per Hedbor  error = parse_html(content, ([]), ([ "error-message":lambda(string t, mapping m, string c) { return c; }, "option":"" ]) );
e18b3d2000-08-22Martin Nilsson  result = RXML.t_xml->format_tag("select", args, content) + error;
511b1d2000-05-16Martin Nilsson  } } return 0; } } } class TagReload { inherit RXML.Tag; constant name = "reload"; constant flags = RXML.FLAG_EMPTY_ELEMENT; class Frame { inherit RXML.Frame;
e18b3d2000-08-22Martin Nilsson  array do_return(RequestID id) {
511b1d2000-05-16Martin Nilsson  if(!args->type) args->type = "submit"; args->name="__reload";
e18b3d2000-08-22Martin Nilsson  result = Roxen.make_tag("input", args, id->misc->vform_xml);
511b1d2000-05-16Martin Nilsson  return 0; } } } class TagVerifyFail { inherit RXML.Tag; constant name = "verify-fail"; constant flags = RXML.FLAG_EMPTY_ELEMENT; class Frame { inherit RXML.Frame; array do_return(RequestID id) { id->misc->vform_ok = 0; if(args->name) { id->misc->vform_failed[args->name]=1; id->misc->vform_verified[args->name]=0; } return 0; } } } class TagClear { inherit RXML.Tag; constant name = "clear"; constant flags = RXML.FLAG_EMPTY_ELEMENT; class Frame { inherit RXML.Frame;
e18b3d2000-08-22Martin Nilsson  array do_return(RequestID id) {
511b1d2000-05-16Martin Nilsson  if(!args->type) args->type = "submit"; args->name="__clear";
e18b3d2000-08-22Martin Nilsson  result = Roxen.make_tag("input", args, id->misc->vform_xml);
511b1d2000-05-16Martin Nilsson  return 0; } } } class TagIfVFailed { inherit RXML.Tag; constant name = "if"; constant plugin_name = "vform-failed"; int eval(string ind, RequestID id) { if(!ind || !sizeof(ind)) return !id->misc->vform_ok; return id->misc->vform_failed[ind]; } } class TagIfVVerified { inherit RXML.Tag; constant name = "if"; constant plugin_name = "vform-verified"; int eval(string ind, RequestID id) { if(!ind || !sizeof(ind)) return id->misc->vform_ok; return id->misc->vform_verified[ind]; } } RXML.TagSet internal = RXML.TagSet("TagVForm.internal", ({ TagVInput(), TagReload(), TagClear(),
5b3d432000-09-14Martin Nilsson  TagVSelect(),
511b1d2000-05-16Martin Nilsson  TagIfVFailed(), TagIfVVerified(), TagVerifyFail(), }) ); class Frame { inherit RXML.Frame; RXML.TagSet additional_tags = internal;
73866a2001-01-26Martin Nilsson  private StateHandler.Page_state state;
511b1d2000-05-16Martin Nilsson  array do_enter(RequestID id) { id->misc->vform_ok = 1; id->misc->vform_verified=(<>); id->misc->vform_failed=(<>);
e18b3d2000-08-22Martin Nilsson  id->misc->vform_xml = !args->noxml;
0909d62001-02-11Martin Nilsson 
73866a2001-01-26Martin Nilsson  state = StateHandler.Page_state(id); state->register_consumer("vform");
0909d62001-02-11Martin Nilsson  if(id->real_variables->__state) { state->use_session( StateHandler.decode_session_id(id->real_variables->__state[0]) ); state->decode(id->real_variables->__state[0]); } else state->use_session();
73866a2001-01-26Martin Nilsson  id->misc->vform_objects = state->get() || ([]);
0909d62001-02-11Martin Nilsson 
511b1d2000-05-16Martin Nilsson  return 0; } array do_return(RequestID id) { id->misc->defines[" _ok"] = id->misc->vform_ok; m_delete(id->misc, "vform_ok");
06b1bb2000-11-08Martin Nilsson  if(args["hide-if-verified"] && !sizeof(id->misc->vform_failed) &&
511b1d2000-05-16Martin Nilsson  sizeof(id->misc->vform_verified) &&
06b1bb2000-11-08Martin Nilsson  id->misc->defines[" _ok"] ) {
511b1d2000-05-16Martin Nilsson  m_delete(id->misc, "vform_verified"); m_delete(id->misc, "vform_failed");
73866a2001-01-26Martin Nilsson  m_delete(id->misc, "vform_xml");
511b1d2000-05-16Martin Nilsson  return 0; }
73866a2001-01-26Martin Nilsson  state->alter(id->misc->vform_objects); content = "<input name=\"__state\" type=\"hidden\" value=\"" + state->encode() + "\" />\n" + content; m_delete(id->misc, "vform_objects");
511b1d2000-05-16Martin Nilsson  m_delete(id->misc, "vform_verified"); m_delete(id->misc, "vform_failed");
73866a2001-01-26Martin Nilsson  m_delete(id->misc, "vform_xml");
e18b3d2000-08-22Martin Nilsson  result = RXML.t_xml->format_tag("form", args, content);
511b1d2000-05-16Martin Nilsson  return 0; } } } TAGDOCUMENTATION; #ifdef manual constant tagdoc=([
9b03652001-03-07Kenneth Johansson "vform":({ #"<desc cont='cont'><p><short> Creates a self verifying form.</short> You can use all standard HTML-input widgets in this container as well.</p> <ex type='box'>
5b3d432000-09-14Martin Nilsson <vform> <vinput name='mail' type='email'>&_.warning;</vinput>
9b03652001-03-07Kenneth Johansson  <input type='hidden' name='user' value='&form.userid;' />
5b3d432000-09-14Martin Nilsson  <input type='submit' />
9b03652001-03-07Kenneth Johansson </vform>
5b3d432000-09-14Martin Nilsson <then><redirect to='other_page.html' /></then> <else>No, this form is still not valid</else> </ex> </desc>
9b03652001-03-07Kenneth Johansson  <attr name='hide-if-verified'> <p>Hides the form if it is verified</p> </attr>", ([ "reload":#"<desc tag='tag'><p><short> Reload the page without variable checking.</short> </p></desc> <attr name='value' value='string'><p> The text on the button.</p> </attr>", "clear":#"<desc tag='tag'><p><short> Resets all the widgets to their initial values.</short> </p></desc> <attr name='value' value='string'><p> The text in the button.</p> </attr>", "verify-fail":#"<desc tag='tag'><p><short> If put in a vform tag, the vform will always fail.</short>This is useful e.g. if you put the verify-fail tag in an if tag. </p></desc>", // It's a tagdoc bug that these, locally defined if-plugins does not show up // in the online manual. "if#vform-failed":#"<desc plugin='plugin'><p> If used with empty argument this will be true if the complete form is failed, otherwise only if the named field failed. </p></desc>", "if#vform-verified":#"<desc plugin='plugin'><p> If used with empty arguemnt this will be true if the complete form so far is verified, otherwise only if the named field was successfully verified. </p></desc>", "vinput":({ #"<desc cont='cont'><p><short> Creates a self verifying input widget.</short> </p></desc> <attr name='fail-if-failed' value='name'><p> The verification of this variable will always fail if the verification of a named variable also failed.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='ignore-if-false'><p> Don't verify if the false flag i set.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='ignore-if-failed' value='name'><p> Don't verify if the verification of a named variable failed.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='ignore-if-verified' value='name'><p> Don't verify if the verification of a named variable succeeded.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='name' value='string' required='required'><p> The name of the variable that should be set.</p>
5b3d432000-09-14Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='value' value='anything'><p> The default value of this input widget.</p>
5b3d432000-09-14Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='scope' value='name' default='vinput'><p> The name of the scope that is created in this tag.</p>
5b3d432000-09-14Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='trim'><p> Trim the variable before verification.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='type' value='int|float|email|date|text|string|password' required='required'><p> Set the type of the data that should be input, and hence what widget should be used and how the input should be verified.</p>
5b3d432000-09-14Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='minlength' value='number'><p> Verify that the variable has at least this many characters. Only available when using the type password, string or text.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='maxlength' value='number'><p> Verify that the variable has at most this many characters. Only available when using the type password, string or text.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='is' value='empty'><p> Verify that the variable is empty. Pretty useless... Only available when using the type password, string or text.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='glob' value='pattern'><p> Verify that the variable match a certain glob pattern. Only available when using the type password, string or text.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='regexp' value='pattern'><p> Verify that the variable match a certain regexp pattern. Only available when using the type password, string or text.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='case' value='upper|lower'><p> Verify that the variable is all uppercased (or all lowercased). Only available when using the type password, string or text.</p>
5b3d432000-09-14Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='equal' value='string'><p> Verify that the variable is equal to a given string. Pretty useless... Only available when using the type password, string or text.</p>
5b3d432000-09-14Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='disable-domain-check'><p> Only available when using the email type. When set the email domain will not be checked against a DNS to verify that it does exists.</p>
5adf512000-09-17Martin Nilsson </attr>
5b3d432000-09-14Martin Nilsson 
9b03652001-03-07Kenneth Johansson <attr name='mode' value='before|after|complex'><p> Select how to treat the contents of the vinput container. Before puts the contents before the input tag, and after puts it after, in the event of failed verification. If complex, use one tag <tag>verified</tag> for what should be outputted in the event of successful verification tag <tag>failed</tag> for every other event.</p> <ex type='box'>
5b3d432000-09-14Martin Nilsson <table> <tr><td>upper</td><vinput name='a' case='upper' mode='complex'> <verified><td bgcolor=green></verified> <failed><td bgcolor=red></failed>&_.input:none;</td> </vinput></tr> <tr><td><input type='submit' /></td></tr> </table> </ex>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='min' value='number'><p> Check that the number is at least the given. Only available when using the type int or float.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='max' value='number'><p> Check that the number is at most the given. Only available when using the type int or float.</p>
511b1d2000-05-16Martin Nilsson </attr>
9b03652001-03-07Kenneth Johansson  <attr name='optional'><p> Indicates that the variable should only be tested if it does contain something.</p> </attr>", ([ "&_.input;":#"<desc ent='ent'><p> The input tag, in complex mode. </p></desc>", "&_.warning;":#"<desc ent='ent'><p> May contain a explaination of why the test failed. </p></desc>", "verified":#"<desc cont='cont'><p> The content will only be shown if the variable was verfied, in complex mode. </p></desc>", "failed":#"<desc cont='cont'><p> The content will only be shown if the variable failed to verify, in complex mode. </p></desc>" // Should this subtag exist? // "vselect":"<desc cont>Mail stewa@roxen.com for a description</desc>", ]) })
511b1d2000-05-16Martin Nilsson ]) }) ]); #endif