#include "Sender.h"
#ifndef USE_FCGI
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#else
#include "fcgi-cout-redirect.h"
extern fcgicout fcgi_cout;
#endif
#include "string_util.h"
extern EnvObject * userdata;
extern DBGate * dbgate;
extern HTTP * http;
extern Security * security;
extern Configuration * config;
extern string LoopKey;
Sender::Sender () {
string fname;
restore = 0;
has_errors = 0;
sent_errors = 1; // No errors, so don't send them
useCheckUtils = 0;
/* open up the requested page */
fname = config->getBasePageDir() + (security->getDest());
// cout << "Sender is using page " << fname <<"<p>";
parser->newFile (fname);
//update the LoopInfo object
loopinfo->setFile( security->getDest() );
loopinfo->clearFile();
//set up the last namespace
userdata->appendToNameSpace ("last", &(http->data));
}
// This constructor is used when there is an error
Sender::Sender (Error & err) {
string fname;
restore = 0;
has_errors = 1;
sent_errors = 0; // We need to send the errors
last_errors = err;
useCheckUtils = 0;
/* Set the destination to be the source page */
security->setDest(security->getLastPage());
/* open up the requested page */
fname = config->getBasePageDir() + (security->getDest());
// cout << "Sender is using page " << fname <<"<p>";
parser->newFile (fname);
//set up the last namespace
userdata->appendToNameSpace ("last", &(http->data));
}
Sender::~Sender () {
}
void Sender::callFunction (SGMLToken * t, Error & err) {
string command = t->command();
char test[255];
strcpy (test, command.c_str());
if (command == "SBREF") {
processSBREF ( (SGMLContainer *) t, err);
} else if (command == "DATREF") {
processDATREF ( (SGMLContainer *) t, err);
} else if (command == "SHOWERRORS") {
processSHOWERRORS ( (SGMLTag *) t, err);
} else if (command == "REPLACE") {
processREPLACE ( (SGMLTag *) t, err);
} else if (command == "INPUT") {
processINPUT ( (SGMLTag *) t, err);
} else if (command == "TEXTAREA") {
processTEXTAREA ( (SGMLContainer *) t, err);
} else if (command == "FUN") {
processFUN ( (SGMLTag *) t, err);
} else if (command == "DATSELECT" || command == "SBSELECT") {
processDATSELECT ( (SGMLTag *) t, err);
} else if (command == "DATFORM" || command == "SBFORM") {
processDATFORM ( (SGMLContainer *) t, err);
} else if (command == "BODY") {
processBODY ( (SGMLContainer *) t, err);
} else if (command == "IMG") {
processIMG ( (SGMLTag *) t, err);
} else if (command == "OPTION") {
processOPTION ( (SGMLTag *) t, err);
} else if (command == "A") {
processA ( (SGMLContainer *) t, err);
} else if (command == "FRAME") {
processFRAME ( (SGMLTag *) t, err);
} else {
BaseSBModule::callFunction (t, err);
}
}
/*
@usehtml
@seealso _SBFORM_
@tagname SBSELECT
@datasource
@usage <SBSELECT NAME="name"
@usage (ARRAY="array list" [SPLIT="delimiter"] |
@usage FOR="number,number" [STEP="number"] |
@usage SQL="SQL statement" [DB="connection_name"] |
@usage VALUE="string or list" [SPLIT="delimiter"])
@usage [NONE="none_option" [NONEVAL="none_value"]]
@usage [REQUIRED] [MULTIPLE]
@usage ([DEFAULT="value"] | [DEFARRAY="array"])
@usage [KEY="columnname"] [OPTION="columnname"]>
@IFLAG NAME The name of the resulting selection object
@FLAG ARRAY Array name or list for select options
@IFLAG FOR Starting and ending numbers for select options
@IFLAG STEP Size to skip between the FOR range (default 1)
@IFLAG SQL SQL statement for select options
@IFLAG DB DB connection name
@IFLAG VALUE A single value or a list for select options
@FLAG SPLIT String used to separate elements in the value or array list
@IFLAG NONE Additional option string not available in the datasource
@IFLAG NONEVAL Additional option value associated with NONE
@flag REQUIRED If given, a non-empty selection is required
@FLAG MULTIPLE If given, multiple values may be selected by the user (results recorded in an array)
@IFLAG DEFAULT Single option selected by default
@IFLAG DEFARRAY Array containing default selecte doptions for a mult-select list
@FLAG KEY Provides an alternate column name to be used for the displayed options
@FLAG OPTION Provides an alternate column name to be used for the internal select option
@ex <accumulate name="colorval"
@ex value="1,2,3" split=",">
@ex <accumulate name="colorname"
@ex value="red,blue,green" split=",">
@ex <accumulate name="selcolors"
@ex value="1,3" split=",">
@ex
@ex <sbselect name="color" array="colorname,colorval"
@ex split="," NONE="black" NONEVAL=0
@ex defarray="selcolors" MULTIPLE>
@ex
@ex <!-- This creates the following:
@ex <select name=color MULTIPLE>
@ex <option value="0">black
@ex <option value="1" SELECTED>red
@ex <option value="2">blue
@ex <option value="3" SELECTED>green
@ex </select>
@ex The resulting values are placed in the array
@ex "color" in the CHECK and ACTION sections.
@ex -->
@ex
@ex <sbselect name="favorite_color" array="colorname"
@ex NONE="black" default="red">
@ex
@ex <!-- This creates the following:
@ex <select name=favorite_color>
@ex <option value="black">black
@ex <option value="red" SELECTED>red
@ex <option value="blue">blue
@ex <option value="green">green
@ex </select>
@ex The resulting values are placed in the variable
@ex $this{favorite_color} in the CHECK and ACTION sections.
@ex -->
@sections HTML
@index Creates a single selection or multi-selection list from a datasource
Creates a single selection or multi-selection list from a list of
values, arrays or an SQL statement. On an error, the selected values
are automatically restored. If the datasource has only one column
that column is used for both the options and the displayed strings for
the selection list. If two columns are returned, the first column is
used for the displayed strings and the second column is used for the
internal option values. For multi-column datasources, the column used
for the option values can be changed with OPTION (which defaults to 1)
and the column used for the displayed strings can be changed with KEY
(which defaults to 0).
<p>
If the MULTIPLE flag is not used, a single select list is generated. A
single default value can be specified with the DEFAULT flag. In the
CHECK and ACTION sections, the value selected by the user is placed in
the variable $this{name}, where name is given by the NAME flag.
<p>
If the MULTIPLE flag is used, a multi-select list is generated. A
set of default values can be specified by naming an array in the
DEFARRAY flag. In the CHECK and ACTION sections, the values
selected by the user are placed in the array $name, where name
is given by the NAME flag.
<P>
There are times when a datasource does not provide a "not selected" or
"none" option (for example an SQL statement selecting people's names).
Such an option can be added with the NONE flag. The value in
NONE is used as both the option value and the displayed string. If
NONEVAL is also given, the NONEVAL is used for the option value, and
NONE is used for the displayed string.
<P>
All other standard HTML flags (like SIZE, ONCHANGE, etc.) are
added to the generated SELECT tag.
*/
void Sender::processDATSELECT ( SGMLTag * t, Error & err) {
AssocArray defaults;
AssocArray * flags = &(t->flags());
string name;
string key, option;
Result * db_results = NULL;
AssocArrayPtrDLList * results;
AssocArray * temp = NULL;
AssocArray::iterator i;
if (! hasDataSource (t)) {
err.addWarning ("SBSELECT tag has no data source");
return;
}
if (! (*flags).contains("NAME")){
err.addWarning ("SBSELECT tag has no name");
return;
}
// Append the loop key to the name
name = interpretString((*flags)["NAME"]) + LoopKey;
// Accumulate the default values
if (restore){
if (flags->contains("MULTIPLE")){
int length = userdata->arrayGetLength(name);
int i;
for(i=0;i<length;i++){
defaults[userdata->arrayGetVal(name,i)] = "1";
}
} else {
defaults[(http->data)[name]] = "1";
}
} else { // Not a restore, so use specified defaults
if (flags->contains("DEFAULT")){
defaults[interpretString((*flags)["DEFAULT"])] = "1";
}
if (flags->contains("DEFARRAY")){
string array_name = interpretString((*flags)["DEFARRAY"]);
int length = userdata->arrayGetLength(array_name);
int i;
for(i=0;i<length;i++){
defaults[userdata->arrayGetVal(array_name,i)] = "1";
}
}
}
if (flags->contains("MULTIPLE")){
if (!multipleItems.contains(name)){
multipleItems[name] = "1";
cout << "<input type=hidden name=\"" << name <<
"_multiple\" value=Y>";
}
}
cout << "<select name=\"" << name << "\"";
if ( flags->contains("SIZE") ) {
cout << " size=\"" << interpretString((*flags)["SIZE"]) << "\"";
}
// Dump the rest of the tags
i = flags->begin();
while (i != flags->end()) {
if ((i->first != "NAME") && (i->first != "VALUE") &&
(i->first != "SQL") && (i->first != "SIZE")){
cout << " " << i->first;
if (i->second.length() > 0) {
cout << "=\"" << i->second << "\"";
}
}
i++;
}
cout << ">\n";
results = getList (t, &db_results);
// Use column 0 for the key and 1 for the option if available
key = "0";
if (! results->empty()){
if (atoi(((* results->front())["LENGTH"]).c_str()) > 1){
option = "1";
} else {
option = "0";
}
}
// If KEY or OPTION are specified, it overwrites the default
// key and option columns
if (flags->contains("KEY")){
key = (*flags)["KEY"];
}
if (flags->contains("OPTION")){
option = (*flags)["OPTION"];
}
// If NONE and NONEVAL are defined, add the NONE value to the beginning
if (flags->contains("NONE")){
string none_key = interpretString((*flags)["NONE"]);
AssocArray * new_option = new AssocArray();
(*new_option)[key] = none_key;
if (flags->contains("NONEVAL")){
(*new_option)[option] = interpretString((*flags)["NONEVAL"]);
} else {
(*new_option)[option] = none_key;
}
results->push_front(new_option);
}
if (! results->empty() ) {
while ( !results->empty()) {
temp = results->front();
results->pop_front();
cout << "<option value=\"" << (*temp)[option] << "\" ";
if (defaults.contains((*temp)[option])){
cout << "SELECTED";
}
cout << ">" << (*temp)[key] << "\n";
delete(temp);
}
cout <<"</select>";
}
delete (results);
if (db_results != NULL){
delete (db_results);
}
}
/*
@usehtml
@SEEALSO _ERROR_
@usage <SHOWERRORS [HEADER="html"] [PREITEM="html"]
@usage [POSTITEM="html"] [FOOTER="html"]>
@flag HEADER HTML to proceed the set of errors (defaults to <font size+1><ul>)
@FLAG PREITEM HTML to proceed each individual error (defaults to <li>)
@FLAG POSTITEM HTML placed after each individual error (defaults to </li>)
@FLAG FOOTER HTML placed after the set of errors (defaults to </ul>)
@sections HTML
@index Formats and displays error messages
By default, errors occuring in the CHECK or ACTION sections appear at
the bottom of the HTML page. If SHOWERRORS is used in the HTML
section, the error list will instead be placed there in the format
given by the SHOWERRORS flags.
@ex <HTML>
@ex <BODY>
@ex <showerrors header="<table border=1>"
@ex footer="<table>"
@ex preitem="<tr><td>"
@ex postitem="</td></tr>">
@ex <sbref screen="welcome.sb">
@ex Press to make an error
@ex </sbref>
@ex </BODY>
@ex </HTML>
@ex <CHECK>
@ex <ERROR text="This is an error">
@ex <ERROR text="This is another error">
@ex </CHECK>
*/
void Sender::processSHOWERRORS (SGMLTag * t, Error & err) {
if (!sent_errors){
sent_errors = 1;
StringDLList *e = &(last_errors.fatalErrors());
StringDLList::iterator i;
AssocArray * flags = &(t->flags());
if (flags->contains("HEADER")) {
cout << (*flags)["HEADER"];
} else {
cout << "<font size=+1>\n";
cout << "<ul>\n";
}
for(i=e->begin();i!=e->end();i++){
if (flags->contains("PREITEM")) {
cout << (*flags)["PREITEM"];
} else {
cout << "<li>";
}
cout <<(*i) << "\n";
if (flags->contains("POSTITEM")) {
cout << (*flags)["POSTITEM"];
} else {
cout << "</li>";
}
}
e = &(last_errors.warnings());
for(i=e->begin();i!=e->end();i++){
if (flags->contains("PREITEM")) {
cout << (*flags)["PREITEM"];
} else {
cout << "<li>";
}
cout << (*i) << "\n";
if (flags->contains("POSTITEM")) {
cout << (*flags)["POSTITEM"];
} else {
cout << "</li>";
}
}
if (flags->contains("FOOTER")) {
cout << (*flags)["FOOTER"];
} else {
cout << "</ul>\n" << "</font>\n";
}
}
}
/*
@usehtml
@SEEALSO _SBFORM_
@usage <INPUT [TYPE=(CHECKBOX | RADIO | SUBMIT | BUTTON |
@usage RESET | FILE | TEXT)]
@usage NAME=name [STATIC] [VALUE=value]
@usage [!VALUE=value] [MAX=number] [MIN=number]
@usage [(CHECK=EvalExp | !CHECK=EvalExp)]
@usage [DATATYPE=datatype] [MULTIPLE] [REQUIRED]
@usage [REDIRECT=screen]
@usage [DESC=description]>
@FLAG TYPE Type of input element
@IFLAG NAME Name of input element
@FLAG STATIC Suppresses appending the loop key onto the element name
@IFLAG VALUE Default value of input element
@FLAG !VALUE Value set if value is empty upon submit
@FLAG MAX Max length (or value if DATATYPE is NUMBER)
@FLAG MIN Min length (or value if DATATYPE is NUMBER)
@EFLAG CHECK Boolean Eval expression to state if a radio button or checkbox is checked
@EFLAG !CHECK Alternative boolean Eval expression to state if a radio button or checkbox is checked
@FLAG DATATYPE Allows automatic client or server side type checking
@FLAG MULTIPLE Allows multiple input elements with the same name
@FLAG REQUIRED If given, the value must be non-empty when submitted
@FLAG REDIRECT Changes destination screen for SUBMIT buttons
@IFLAG DESC Description used in error messages
@sections HTML
@index Creates a form element and automatic server and client side type checking.
Creates a form element and automatic server and client side type checking
(if applicable).
<P>
As in standard HTML, the name of the INPUT element is given by NAME.
However, if an INPUT is in a _LOOP_, a loop key is appended to the name
in the form "_loop name_iteration" (multiply embedded loops cause
multiple loop keys). This allows inputs to be easily supported inside
of _LOOP_S. The appending of loop keys can be suppressed
with the STATIC flag.
<P>
The type of input box is given by the TYPE flag. If the TYPE is TEXT
a DATATYPE can be specified. DATATYPES supported for client side type
checking (via JavaScript) include:
<P>
<center>
<table border=1>
<tr><th colspan=3>Client-side Datatypes</th></tr>
<tr><th>Datatype</th><th>Format</th><th>Example</th></tr>
<tr><td>DATE</td><td>MM/DD/YY, MM/DD/YYYY</td><td>2/1/1972</td></tr>
<tr><td>EMAIL</td><td>username@host.domain (extra hosts supported)</td><td>auser@steelblue.com</td></tr>
<tr><td>FLOAT</td><td>Digits with zero or one decimal points</td><td>3.14</td></tr>
<tr><td>FUTUREDATE</td><td>Date, but in the future</td><td>11/2/2010</td></tr>
<tr><td>MONEY</td><td>Digits possibily beginning with $ containing zero or one a decimal point</td><td>$20.00</td></tr>
<tr><td>NUMBER</td><td>Digits</td><td>512</td></tr>
<tr><td>PASTDATE</td><td>Date, but in the past</td><td>3/4/1980</td></tr>
<tr><td>PERCENT</td><td>## or %##</td><td>%50</td></tr>
<tr><td>PHONE</td><td>Numbers, parentheses and dashes</td><td>(717) 123-4567</td></tr>
<tr><td>SSN</td><td>######### or ##-##-####</td><td>12-34-5678</td></tr>
<tr><td>SHORT_TIME</td><td>HH:MM (HH between 1 and 12)</td><td>5:30</td></tr>
<tr><td>TIME</td><td>HH:MM or HH:MM (AM|PM)</td><td>2:20 PM</td></tr>
</TABLE>
</center>
<P>
For client side type checking the configuration option "CheckUtil"
should be set to point to the CheckUtils.js JavaScript file. You can
create a new datatype for client side checking by adding the function
checkTYPE in CheckUtils.js and by looping over the elements in the
object TYPEgroup in the function checkFormDatatypes() (where "TYPE" is
the name of the new datatype in all capital letters).
<P>
Server side type checking includes:
<P>
<center>
<table border=1>
<tr><th colspan=3>Server-side Datatypes</th></tr>
<tr><th>Datatype</th><th>Format</th><th>Example</th></tr>
<tr><td>DATE</td><td>MM/DD/YY, MM/DD/YYYY</td><td>2/1/1972</td></tr>
<tr><td>EMAIL</td><td>username@host.domain (extra hosts supported)</td><td>auser@steelblue.com</td></tr>
<tr><td>TIME</td><td>HH:MM or HH:MM (AM|PM)</td><td>2:20 PM</td></tr>
</TABLE>
</center>
<P>
You can create a new datatype for server side checking by adding the
function Check::checkTYPE in Check.cpp and Check.h, and by adding the
call to checkTYPE in Check::processINPUT().
<P>
The maximum and minimum lengths of the value can be enforced
with the flags MAX and MIN respectively. If the datatype is NUMBER,
MAX and MIN are used to determine the maximum and minimum values. In
addition, the value must be non-empty upon submission if the REQUIRED flag
is specified. However, if the value is empty upon submission, an
alternative value can be supplied with the !VALUE flag. (This can be used
to supply a value for checkboxes if they are not checked).
<P>
If an error occurs in type checking, an appropriate message is
displayed. By default, the description for the input box is
determined by the NAME flag. However, an alternative description
can be given with the DESC flag.
<P>
If the TYPE is CHECKBOX or RADIO, the use of CHECKED is not suggested.
Instead, a boolean Eval expression can be specified in either the
CHECK or !CHECK flag. The input element is checked if CHECK is true or
!CHECK is false.
<P>
In the CHECK, ACTION, and following HTML section, the value of the
input box is stored in $this{name}. The values of input boxes in
loops (without the STATIC flag) can be retrieved with $this{name} in a
respective replayed loop (see _LOOP_). However, if multiple
checkboxes or text boxes share the same name (unless they are in
LOOPS) the MULTIPLE flag should be specified. If MULTIPLE is given,
the values are placed in an array $name, where "name" is given by the
flag NAME.
<P>
If the TYPE is FILE, the filename specified in the input box is placed
in $name{filename}, the content type is placed in $name{contenttype},
and the file contents are placed in $name{data}, where "name" is given
by the flag NAME.
<P>
If the TYPE is SUBMIT, an alternative destination screen can be given
with the flag REDIRECT. If the form subission is successful, the
screen given by REDIRECT will be displayed instead of the SCREEN
given in the SBFORM tag (see _SBFORM_).
<P>
If an error occurs in the CHECK or ACTION sections of a SteelBlue
page, the form is redisplayed, and input values are properly
restored to those values chosen by the user at the time of submission.
@ex <input name="user_email" datatype="email"
@ex type="text" size=30 max=30 REQUIRED>
@ex
@ex <input name="likes_spam" check="$likes_spam eq 'Y'"
@ex type="checkbox" value="Y" !value="N">
@ex
@ex <input name="color" check="$color eq 'red'"
@ex type="radio" value="red" MULTIPLE> Red <br>
@ex <input name="color" check="$color eq 'blue'"
@ex type="radio" value="blue" MULTIPLE> Blue
@ex <!-- The results of the above checkboxes are placed
@ex in the array "color" containing the checked
@ex values in the CHECK and ACTION sections -->
*/
void Sender::processINPUT (SGMLTag * t, Error & err) {
string onchange;
AssocArray * flags = &(t->flags());
string name = "";
string inputtype = upcase((*flags)["TYPE"]);
string value = "";
if (flags->contains("VALUE")){
value = interpretString((*flags)["VALUE"]);
}
int checked = 0; // Used to store if we are checked for radio and cboxes
AssocArray::iterator i = flags->begin();
// dump the name with the loop key appended
if (flags->contains("NAME")){
if (flags->contains("STATIC")){ // Don't append loop key if static
name = interpretString((*flags)["NAME"]);
} else {
name = interpretString((*flags)["NAME"]) + LoopKey;
}
} else {
//err.addWarning ("INPUT tag without NAME flag");
}
if (flags->contains("MULTIPLE")){
if (!multipleItems.contains(name)){
multipleItems[name] = "1";
cout << "<input type=hidden name=\"" << name <<
"_multiple\" value=Y>";
}
}
if (restore){
if ((inputtype != "CHECKBOX") && (inputtype != "RADIO")
&& (inputtype != "SUBMIT") && (inputtype != "BUTTON")
&& (inputtype != "RESET") && (inputtype != "CANCEL")){
/* in an input box, just replace the value */
value = (http->data)[name];
} else if (inputtype == "CHECKBOX"){
if (value == ""){
// A checkbox is checked if the value is "", but the restore is not
checked = (http->data)[name].length() > 0;
} else {
// First check to see if it was a singleton item which is defined
if (value == (http->data)[name]){
checked = 1;
} else {
// Now check with the array of restore values
int length = userdata->arrayGetLength(name);
int i;
for(i=0;i<length;i++){
if (value == userdata->arrayGetVal(name,i)){
checked = 1;
}
}
}
}
} else if (inputtype == "RADIO"){
if (value == (http->data)[name]){
checked = 1;
}
}
} else {
/* For checkboxes and radio buttons, we evaluate the value of CHECK
to see if it must be checked */
if (flags->contains("CHECK")){
string check = ((*flags)["CHECK"]);
checked = evalBoolString(check,err,t->linebegin());
}
if (flags->contains("!CHECK")){
string check = ((*flags)["!CHECK"]);
checked = !evalBoolString(check,err,t->linebegin());
}
if (flags->contains("CHECKED")){
checked = 1;
}
}
cout << "<INPUT ";
if (flags->contains("NAME")) {
cout << " NAME=\"" << name <<"\"";
}
while (i != flags->end()) {
if ( (i->first != "ONCLICK") && (i->first != "NAME")
&& (i->first != "VALUE") && (i->first != "ONCHANGE")
&& (i->first != "CHECKED")) {
cout << " " << i->first;
if (i->second.length() > 0) {
cout << "=\"" << i->second << "\"";
}
}
i++;
}
// Process the ONCLICK flag
if (flags->contains("ONCLICK") || flags->contains("SET") || flags->contains("CLICK") || flags->contains("REDIRECT")) {
cout << " ONCLICK=\"" ;
string * js = new string();
makeOnclick (t, js);
cout << (*js) <<"\"";
delete js;
}
// Figure out what we need to do about datachecking
if (flags->contains("DATATYPE")) {
string type = upcase ((*flags)["DATATYPE"]);
string group = ("DF_" + currentFormName + "." + type + "group");
//Check and see if this is a new type for this sender
if (! checkGroups.contains(group) ) {
checkGroups[group] = "set";
javascript += group + " = new Group();\n" ;
}
// now set up the error checking
javascript += group + ".add(document." + currentFormName + "."
+ name + ");\n";
if (useCheckUtils){
onchange += "check" + type + "(document." + currentFormName + "."
+ name + ");";
}
}
if (flags->contains("REQUIRED")){
string group = ("DF_" + currentFormName + ".REQUIREDgroup");
if (! checkGroups.contains(group) ){
checkGroups[group] = "set";
javascript += group + " = new Group();\n";
}
// now set up the error checking
javascript += group + ".add(document." + currentFormName + "."
+ name + ");\n";
}
if (flags->contains("DESC")) {
javascript += "document." + currentFormName + "."
+ name + ".desc = \"" + interpretString((*flags)["DESC"])
+ "\";\n";
}
// print out the ONCHANGE stuff
cout << " ONCHANGE=\"" << onchange << (*flags)["ONCHANGE"] << "\" ";
// Process the VALUE flag
if ((value.length() > 0) || flags->contains("VALUE")){
cout << " VALUE=\"" << escapeHTMLValue(value) << "\"";
}
if (checked){
cout << " CHECKED ";
}
cout << ">";
}
/*
@usehtml
@SEEALSO _SBFORM_
@container
@usage <TEXTAREA NAME=name [VALUE=value]
@usage [MAX=num] [MIN=num] [REQUIRED]
@usage [MULTIPLE] [DESC=description]>
@usage </TEXTAREA>
@IFLAG NAME Name of input element
@IFLAG VALUE Default value of input element
@FLAG MAX Max length
@FLAG MIN Min length
@FLAG REQUIRED If given, the value must be non-empty when submitted
@FLAG MULTIPLE Allows multiple input elements with the same name
@IFLAG DESC Description used in error messages
@sections HTML
@index Creates a textarea input box and automatic server and client side length checking
Creates a textarea input box and automatic server and client
side length checking (if applicable).
<P>
As in standard HTML, the name of the TEXTAREA element is given by NAME.
However, if an TEXTAREA is in a _LOOP_, a loop key is appended to the name
in the form "_loop name_iteration" (multiply embedded loops cause
multiple loop keys). This allows textareas to be easily supported inside
of _LOOP_S.
<P>
The default value of the textarea should be placed in the TEXTAREA
container. The maximum and minimum lengths of the submitted value can
be enforced with the flags MAX and MIN respectively. In addition, the
value must be non-empty upon submission if the REQUIRED flag is
specified.
<P>
In the CHECK, ACTION, and following HTML section, the value of the
textarea is stored in $this{name}. The values of textareas in loops
(without the STATIC flag) can be retrieved with $this{name} in a
respective replayed loop (see _LOOP_). However, if multiple textareas
share the same name (unless they are in LOOPS) the MULTIPLE flag
should be specified. If MULTIPLE is given, the values are placed in
an array $name, where name is given by the flag NAME.
<P>
If an error occurs in type checking, an appropriate message is
displayed. By default, the description for the textarea is
determined by the NAME flag. However, an alternative description
can be given with the DESC flag.
<P>
If an error occurs in the CHECK or ACTION sections of a SteelBlue
page, the form is redisplayed, and textarea values are properly restored
to those values chosen by the user at the time of submission.
@ex <TEXTAREA name="NOTES"
@ex DESC="Important Notes" COLS=60 ROWS=5
@ex MIN=20>Place notes here</TEXTAREA>
*/
void Sender::processTEXTAREA (SGMLContainer * t, Error & err) {
string onchange;
AssocArray * flags = &(t->flags());
string name = "";
AssocArray::iterator i = flags->begin();
// dump the name with the loop key appended
if (flags->contains("NAME")){
name = interpretString((*flags)["NAME"]) + LoopKey;
} else {
err.addWarning ("TEXTAREA tag without NAME flag");
}
if (flags->contains("MULTIPLE")){
if (!multipleItems.contains(name)){
multipleItems[name] = "1";
cout << "<input type=hidden name=\"" << name <<
"_multiple\" value=Y>";
}
}
cout << "<TEXTAREA ";
if (flags->contains("NAME")) {
cout << " NAME=\"" << name <<"\"";
}
while (i != flags->end()) {
if ( (i->first != "ONCLICK") && (i->first != "NAME")
&& (i->first != "VALUE") && (i->first != "ONCHANGE")) {
cout << " " << i->first;
if (i->second.length() > 0) {
cout << "=\"" << i->second << "\"";
}
}
i++;
}
// Add typechecking
if (currentFormName != ""){
string group = ("DF_" + currentFormName + ".TEXTAREAgroup");
//Check and see if this is a new type for this sender
if (! checkGroups.contains(group) ) {
checkGroups[group] = "set";
javascript += group + " = new Group();\n" ;
}
// now set up the error checking
javascript += group + ".add(document." + currentFormName + "."
+ name + ");\n";
if (useCheckUtils){
onchange += "checkTEXTAREA(document." + currentFormName + "."
+ name + ");";
}
if (flags->contains("MAX")){
javascript += "document."+ currentFormName + "." + name
+ ".max = " + (*flags)["MAX"] + ";\n";
}
if (flags->contains("MIN")){
javascript += "document."+ currentFormName + "." + name
+ ".min = " + (*flags)["MIN"] + ";\n";
}
if (flags->contains("REQUIRED")){
string group = ("DF_" + currentFormName + ".REQUIREDgroup");
if (! checkGroups.contains(group) ){
checkGroups[group] = "set";
javascript += group + " = new Group();\n";
}
// now set up the error checking
javascript += group + ".add(document." + currentFormName + "."
+ name + ");\n";
}
if (flags->contains("DESC")) {
javascript += "document." + currentFormName + "."
+ name + ".desc = \"" + interpretString((*flags)["DESC"])
+ "\";\n";
}
}
// print out the ONCHANGE stuff
cout << " ONCHANGE=\"" << onchange << (*flags)["ONCHANGE"] << "\" ";
cout << ">";
if (restore){
cout << (http->data)[name];
} else {
recurseOnTokens(t->contents(),err);
}
cout << "</TEXTAREA>";
}
/*
@NODOC
*/
void Sender::processGeneric (SGMLToken * t, Error & err) {
if (t->type() == SGML_CONTAINER_TYPE) {
processContainer( (SGMLContainer *) t, err);
} else if (t->type() == SGML_TEXT_TYPE) {
processText ( (SGMLText *) t, err);
} else if (t->type() == SGML_TAG_TYPE) {
processTag ((SGMLTag *) t, err);
} else if (t->type() == SGML_COMMENT_TYPE) {
processComment ((SGMLComment *) t, err);
}
}
/* dump the container as-is with the exception of the NAME flag.
The name floag needs special attention. If we have a loop key,
we need to append it to the name field as it is outputed to
the client
@NODOC
*/
void Sender::processContainer (SGMLContainer * t, Error & err) {
AssocArray * flags = &(t->flags());
AssocArray::iterator i = flags->begin();
// bogus code to avoid warnings
if ( !t ) {
err.addWarning ("processContainer called with bad argument");
}
cout << "<" << t->command();
// dump the name with the loop key appended
if (flags->contains("NAME")) {
cout << " NAME=\"" << (*flags)["NAME"] + LoopKey <<"\"";
}
while (i != flags->end()) {
cout << " " << i->first;
if (i->second.length() > 0 && (i->first) != "NAME" ) {
cout << "=\"" << i->second << "\"";
}
i++;
}
cout << ">";
recurseOnTokens (t->contents(), err);
cout << "</" << t->command() << ">";
}
/* dump the tag as-is with the exception of the NAME flag.
The name floag needs special attention. If we have a loop key,
we need to append it to the name field as it is outputed to
the client
@NODOC
*/
void Sender::processTag (SGMLTag * t, Error & err) {
AssocArray * flags = &(t->flags());
AssocArray::iterator i = flags->begin();
// bogus code to avoid warnings
if ( !t ) {
err.addWarning ("processTag called with bad argument");
}
cout << "<" << t->command();
// dump the name with the loop key appended
if (flags->contains("NAME")) {
cout << " NAME=\"" << (*flags)["NAME"] + LoopKey <<"\"";
}
while (i != flags->end()) {
cout << " " << i->first;
if (i->second.length() > 0 && (i->first) != "NAME") {
cout << "=\"" << i->second << "\"";
}
cout << " ";
i++;
}
cout << ">";
}
/*
@NODOC
*/
void Sender::processText (SGMLText * t, Error & err) {
int i;
int l;
string s;
// bogus code to avoid warnings
if ( !t ) {
err.addWarning ("processText called with bad argument");
}
// If we are not in debug mode, only print if it's not all whitespace
if ((*config)["debug"].length() > 0) {
cout << t->text();
} else {
s = t->text();
l = s.length();
for (i=0;i<l;i++) {
if (!isspace(s[i])) {
cout << t->text();
break;
}
}
}
}
/*
@NODOC
*/
void Sender::processComment (SGMLComment * t, Error & err) {
// bogus code to avoid warnings
if ( !t ) {
err.addWarning ("processText called with bad argument");
}
cout << "<!--" << t->comment() << "-->";
}
/* This function looks at a token and decides creates the javascript
to set the cookie as indicated by the SET flag.
It will put the string in the String *s variable that is passed
in. This is done for efficiency reasons, as returning a String
will mean 2 extra string object creations...ick.
5/19 added the CLICK flag support */
void Sender::makeOnclick (SGMLTag * t, string * s) {
AssocArray flags = t->flags();
static string staticset = string ("SET"); // static for efficiency
(*s) = "";
if (flags.contains(staticset)) {
string set = flags[staticset];
string result;
// they can put multiple vars in here
while (set.find(";") != string::npos) {
// string stemp = string (set.before(";"));
string stemp = set.substr (0, set.find(";"));
interpretString(stemp, result); //interpret what is before ';'
(*s) += ";document.cookie='" + result + "';";
// set = set.after(";");
set = set.substr ( set.find(";")+1);
}
interpretString (set, result);
(*s) += ";document.cookie='" + result + "';";
}
// now take care of any CLICK stuff
if (flags.contains ("CLICK")) {
string result;
interpretString (flags["CLICK"], result);
(*s) += ";document.cookie='click=" + result + "';";
}
// add on redirect
if (flags.contains("REDIRECT")){
string result;
string sbref = (*config)["sburl"];
if (sbref.length() < 1) {
sbref = config->getDATURL();
}
interpretString (flags["REDIRECT"], result);
(*s) += ";" + currentFormName + ".action='" + sbref + "?" +
result + "+" + security->getTicket() + "+" +
security->getDest() + "';";
}
// now add on the actual ONCLICK stuff
if (flags.contains ("ONCLICK") ) {
string result;
interpretString (flags["ONCLICK"], result);
// (*s) += flags["ONCLICK"];
(*s) += result;
}
}
/*
@NODOC
Important note: CLICK and SET is set on both the command line and in
the cookie for DATREF. For SBREF it is only set on the command line.
When variables other than "click" are set in the cookie, for DATREFS
they will stick around forever until reset. For SBREFS set items
are only set for the one action.*/
void Sender::processDATREF (SGMLContainer * t, Error & err) {
string mouseover;
string mouseout;
AssocArray * flags = &(t->flags());
AssocArray::iterator i = flags->begin();
string anchor = interpretString ((*flags)["ANCHOR"]);
if ( !(*flags).contains("SCREEN")) {
err.addWarning ("DATREF tag without SCREEN flag");
}
cout << "<a href=\"" << config->getDATURL();
cout << "?" << interpretString ((*flags)["SCREEN"]);
cout << "+" << security->getTicket() << "+"
<< security->getDest();
// now cout any vars we want set here
if (flags->contains("CLICK")) {
cout <<"+"<< http->toHTML("click") << "+"
<< http->toHTML( interpretString( ((*flags)["CLICK"])).c_str());
}
if (flags->contains("SET")) {
string set;
set = (*flags)["SET"];
while ( set.find(";") != string::npos ) {
string next_pair = set.substr(0, set.find(";"));
string tag = next_pair.substr (0, (next_pair.find ("=")));
string value = next_pair.substr (next_pair.find("=") +1);
cout << "+" << http->toHTML(tag.c_str()) << "+" << http->toHTML(interpretString (value).c_str());
set = set.substr(set.find(";")+1);
}
}
// Send the anchor if given
if (anchor != ""){
cout << "#" << anchor;
}
//finally close the quote for the href
cout << "\" ";
while (i != flags->end()) {
if (i->first != "SCREEN" && i->first != "ONCLICK" && i->first != "ONMOUSEOVER" && i->first != "ONMOUSEOUT") {
cout << " " << i->first;
if (i->second.length() > 0) {
cout << "=\"" << i->second << "\"";
}
}
i++;
}
cout << " ONCLICK=\"" ;
//set the crazy DAT ticket format
cout << "document.cookie = 'ticket=" << (security->getUserInfo()).username << "|||" <<
(security->getUserInfo()).ticket << "|||" << "0" << "|||" << "';";
string * js = new string();
makeOnclick (t, js);
cout << (*js) <<"\"";
delete (js);
if (flags->contains("ONMOUSEOVER")){
cout << " ONMOUSEOVER=\"" << interpretString((*flags)["ONMOUSEOVER"]) << "\"";}
if (flags->contains("ONMOUSEOUT")){
cout << " ONMOUSEOUT=\"" << interpretString((*flags)["ONMOUSEOUT"]) << "\"";}
cout << ">";
recurseOnTokens (t->contents(), err);
cout << "</a>";
}
/* Important note: CLICK and SET is set on both the command line and in
the cookie for DATREF. For SBREF it is only set on the command line.
When variables other than "click" are set in the cookie, for DATREFS
they will stick around forever until reset. For SBREFS set items
are only set for the one action.*/
/*
@usehtml
@SEEALSO _REDIRECT_
@container
@usage <SBREF SCREEN="filename"
@usage [CLICK=value]
@usage [SET="tag1=value1;tag2=value2;..."]>
@usage Link Text
@usage </SBREF>
@IFLAG SCREEN Destination SteelBlue filename (relative to BasePages) of the link
@IFLAG CLICK Value to be set to $this{click}
@IFLAG SET Pairs of tags and values to be set in the this namespace
@sections HTML
@index Creates a link to another SteelBlue page.
Creates a link to another SteelBlue page. The CHECK and ACTION
sections of the calling page are first processed before the
HTML section of the destination page (given in SCREEN) is executed.
<P>
Values in the $this namespace in the CHECK, ACTION, and destination
HTML section can be set using the CLICK and SET flags. The CLICK flag
sets $this{click} with the given value. The SET flag supplies a set
of tag and value pairs in the form "tag1=value1;tag2=value2;...".
This results in $this{tag1} being set to "value1", $this{tag2} being
set to "value2" and so on. Note that SET and CLICK options are
implemented in the URL, and therefore can be changed by the user.
<P>
Other tags are passed to the resulting A tag, however ONMOUSEOVER,
ONMOUSEOUT, and ONCLICK first have variable interpretation
@ex <SBREF screen="welcome.sb">
@ex Back to welcome page
@ex </SBREF>
*/
void Sender::processSBREF (SGMLContainer * t, Error & err) {
AssocArray * flags = &(t->flags());
AssocArray::iterator i = flags->begin();
string anchor = interpretString ((*flags)["ANCHOR"]);
if ( !(*flags).contains("SCREEN")) {
err.addWarning ("SBREF tag without SCREEN flag");
}
string sbref = (*config)["sburl"];
if (sbref.length() < 1) {
sbref = config->getDATURL();
}
cout << "<a href=\"" << sbref;
cout << "?" << interpretString ((*flags)["SCREEN"]) << "+"
<< security->getTicket() << "+"
<< security->getDest();
// now cout any vars we want set here
if (flags->contains("CLICK")) {
cout <<"+"<< http->toHTML("click") << "+" << http->toHTML( (interpretString((*flags)["CLICK"]).c_str()) );
}
if (flags->contains("SET")) {
string set;
set = (*flags)["SET"];
while ( set.length() > 0 ) {
string next_pair = set.substr(0, set.find(";"));
string tag = next_pair.substr (0, (next_pair.find ("=")));
string value = next_pair.substr (next_pair.find("=") +1);
cout << "+" << http->toHTML(tag.c_str()) << "+" << http->toHTML(interpretString (value).c_str());
if (set.find(";") != string::npos) {
set = set.substr(set.find(";")+1);
} else {
set = "";
}
}
}
// Send the anchor if given
if (anchor != ""){
cout << "#" << anchor;
}
//finally close the quote for the href
cout << "\" ";
while (i != flags->end()) {
if (i->first != "SCREEN" && i->first != "ONCLICK" && i->first != "ONMOUSEOVER" && i->first != "ONMOUSEOUT") {
cout << " " << i->first;
if (i->second.length() > 0) {
cout << "=\"" << i->second << "\"";
}
}
i++;
}
if (flags->contains("ONMOUSEOVER")){
cout << " ONMOUSEOVER=\"" << interpretString((*flags)["ONMOUSEOVER"]) << "\"";}
if (flags->contains("ONMOUSEOUT")){
cout << " ONMOUSEOUT=\"" << interpretString((*flags)["ONMOUSEOUT"]) << "\"";}
if (flags->contains("ONCLICK")){
cout << " ONCLICK=\"" << interpretString((*flags)["ONCLICK"]) << "\"";}
cout << ">";
recurseOnTokens (t->contents(), err);
cout << "</a>";
}
/*
@nodoc
*/
void Sender::processBODY (SGMLContainer * t, Error & err) {
AssocArray * flags = &(t->flags());
AssocArray::iterator i = flags->begin();
cout << "<BODY ";
while (i != flags->end()) {
cout << " " << i->first;
if (i->second.length() > 0) {
cout << "=\"" << i->second << "\"";
}
i++;
}
cout << ">\n";
sendCheckUtils();
recurseOnTokens (t->contents(), err);
if (useCheckUtils){
// dump the javascript we have made so far
cout << "<script language='JavaScript1.1'>\n" <<
javascript << "\n</script>\n";
}
cout << "</BODY>\n";
}
void Sender::sendCheckUtils () {
if (config->canLoadJS()) {
if (config->getCheckUtil() != ""){
useCheckUtils = 1;
cout << "<script language='JavaScript1.1' src='" <<
config->getCheckUtil() << "'>\n" <<
"alert('Cannot find " << config->getCheckUtil() <<
"')\n</script>\n";
}
} else {
if ((*config)["checkutilfile"] != ""){
useCheckUtils = 1;
/* dump the whole file */
cout << "<script language='JavaScript1.1'>";
ifstream js;
char buff [5000];
js.open( (*config)["checkutilfile"].c_str() );
while (js.getline (buff,5000)) {
cout << buff << "\n";
}
cout << "</script>";
}
}
}
/*
@usehtml
@sections HTML
@index Creates a standard IMG tag with string substitution
Creates a standard IMG tag, however string substitution
is conducted upon the flags first.
*/
void Sender::processIMG (SGMLTag * t, Error & err) {
AssocArray * flags = &(t->flags());
AssocArray::iterator i = flags->begin();
cout << "<IMG ";
while (i != flags->end()) {
cout << " " << i->first;
if (i->second.length() > 0 && i->second != "SRC") {
cout << "=\"" << interpretString(i->second) << "\"";
} else if (i->first == "SRC") {
cout << "=\"" << interpretString (i->second) << "\"";
}
i++;
}
cout << ">";
}
/*
@usehtml
@sections HTML
@index Creates a standard OPTION tag with string substitution
Creates a standard OPTION tag, however string substitution
is conducted upon the flags first.
*/
void Sender::processOPTION (SGMLTag * t, Error & err) {
AssocArray * flags = &(t->flags());
AssocArray::iterator i = flags->begin();
cout << "<OPTION ";
while (i != flags->end()) {
cout << " " << i->first;
if (i->second.length() > 0) {
cout << "=\"" << interpretString(i->second) << "\"";
}
i++;
}
cout << ">";
}
/*
@usehtml
@sections HTML
@index Creates a standard A tag with string substitution
Creates a standard A tag, however string substitution
is conducted upon the flags first.
*/
void Sender::processA (SGMLContainer * t, Error & err) {
AssocArray * flags = &(t->flags());
AssocArray::iterator i = flags->begin();
cout << "<A ";
while (i != flags->end()) {
cout << " " << i->first;
if (i->second.length() > 0 && i->second != "HREF") {
cout << "=\"" << interpretString(i->second) << "\"";
} else if (i->first == "HREF") {
cout << "=\"" << interpretString (i->second) << "\"";
}
i++;
}
cout << ">";
recurseOnTokens (t->contents(), err);
cout <<"</A>";
}
/*
@usehtml
@SEEALSO _REDIRECT_
@tagname SBFORM
@container
@usage <SBFORM SCREEN=filename NAME=name
@usage [CLICK=value]
@usage [SET="tag1=value1;tag2=value2;..."]>
@usage Form elements
@usage </SBFORM>
@IFLAG SCREEN Destination SteelBlue filename (relative to BasePages) of the form (required in HTML section)
@FLAG NAME Name of form
@IFLAG CLICK Value to be set to $this{click}
@IFLAG SET Pairs of tags and values to be set in the this namespace
@sections ALL
@index Creates a form that submits to another SteelBlue page.
Creates a form that submits to another SteelBlue page. The CHECK and
ACTION sections of the calling page are first processed before the
HTML section of the destination page (given in SCREEN) is executed.
<P>
If an SBFORM container is placed in a CHECK or ACTION section, the
contents of the SBFORM are only executed if the associated SBFORM
(with the same NAME) was submitted in the HTML section.
<P>
Values in the $this namespace in the CHECK, ACTION, and destination
HTML section can be set using the CLICK and SET flags. The CLICK flag
sets $this{click} with the given value. The SET flag supplies a set
of tag and value pairs in the form "tag1=value1;tag2=value2;...".
This results in $this{tag1} being set to "value1", $this{tag2} being
set to "value2" and so on. Note that SET and CLICK options are
implemented in the URL, and therefore can be changed by the
user.
@ex <HTML>
@ex <BODY>
@ex <SBFORM name="myform" screen="welcome.sb">
@ex <input name="editbox"><br>
@ex <input type="submit">
@ex </SBFORM>
@ex <SBREF screen="welcome.sb">Back to welcome</SBREF>
@ex </BODY>
@ex </HTML>
@ex
@ex <CHECK>
@ex <!-- This error only appears if the form is submitted -->
@ex <SBFORM name="myform">
@ex <error text="You entered $this{editbox}">
@ex </SBFORM>
@ex </CHECK>
*/
void Sender::processDATFORM (SGMLContainer * t, Error & err) {
string onsubmit = "";
AssocArray * flags = &(t->flags());
AssocArray::iterator i = flags->begin();
if ( !(*flags).contains("SCREEN")) {
err.addWarning ("SBFORM tag without SCREEN flag");
}
if ( !(*flags).contains("NAME")) {
err.addWarning ("SBFORM tag without NAME flag");
}
// Set this to be the current form
currentFormName = (*flags)["NAME"];
javascript += "DF_" + (*flags)["NAME"] + " = new DatForm();\n";
// Check to see if we need to restore
if (has_errors){
string temp = "form_" + currentFormName;
if ((http->data).contains(temp)){
restore = 1;
} else {
restore = 0;
}
}
cout << "<form action=\"";
if ((t->command() == "DATFORM") || ((*config)["sburl"] == "")) {
cout << (*config)["daturl"] ;
} else {
cout << (*config)["sburl"] ;
}
cout << "?" << (*flags)["SCREEN"] << "+" << security->getTicket() << "+"
<< security->getDest();
// now cout any vars we want set here
if (flags->contains("CLICK")) {
cout <<"+"<< http->toHTML("click") << "+"
<< http->toHTML((char *)(*flags)["CLICK"].c_str());
}
if (flags->contains("SET")) {
// string tag = ((*flags)["SET"]).before ("=");
string tag = ((*flags)["SET"]).substr (0, ((*flags)["SET"]).find ("="));
// string value =((*flags)["SET"]).after ("=");
string value = ((*flags)["SET"]).substr (((*flags)["SET"]).find("=") +1);
cout << "+" << http->toHTML( (char *) tag.c_str()) << "+" << http->toHTML( (char *) value.c_str());
}
//finally close the quote for the href
cout << "\" METHOD=POST";
while (i != flags->end()) {
if ( (i->first != "SCREEN") && (i->first != "ONSUBMIT") ) {
cout << " " << i->first;
if (i->second.length() > 0) {
cout << "=\"" << i->second << "\"";
}
}
i++;
}
// set the onsubmit datachecking
if (flags->contains("ONSUBMIT")) {
//this may need fixing...
// put in whatever the user wants submitted
onsubmit = (*flags)["ONSUBMIT"];
}
/* DONE ON COMMAND LINE NOW
//check for any SET flags
if ( flags->contains ("SET") ) {
string js = "";
makeOnclick ( t, &js);
onsubmit += js;
}
*/
// Only run canSubmit if we can do client side typechecking
if (useCheckUtils){
onsubmit += "return ( canSubmit ( DF_" + (*flags)["NAME"] + "));";
}
cout << " ONSUBMIT=\"" + onsubmit + "\"";
cout << ">\n";
cout << "<input name=\"form_" << (*flags)["NAME"]
<< "\" value=\"form\" type=\"hidden\">";
recurseOnTokens (t->contents(), err);
cout << "</form>";
}
/*
@usehtml
@SEEALSO _DECLARE_ _ASSIGN_
@usage <REPLACE VALUE="string"
@usage [ELSE="string" [BOOL="Eval Boolean"]]
@usage [EVAL]>
@iflag VALUE String to be interpreted and displayed
@iflag ELSE String to be displayed if BOOL evaluates to false or VALUE is empty and BOOL not specified
@eflag BOOL If true, VALUE is displayed, else ELSE is displayed
@flag EVAL If given, VALUE and ELSE are passed through Eval interpretation
@sections HTML
@index Evaluates a string either through variable substitution or Eval interpretation and prints the result.
Evaluates a string either through variable substitution
or Eval interpretation and prints the result.
<P>
If no other flags are given, the VALUE flag is passed through variable
substitution and the result is printed. If VALUE is empty,
the ELSE flag is printed.
<P>
If BOOL is given, the BOOL flag is passed through Eval interpretation.
If the result is true (i.e. other than "0"), the VALUE flag is printed,
otherwise the ELSE flag is printed.
<P>
IF EVAL is given, the contents of VALUE and ELSE are passed through
Eval interpretation instead of string substitution before printing.
@ex The following prints 23 + 2 = 25:
@ex <assign name="$people{count}" value="23">
@ex <replace value="$people{count} + 2"> =
@ex <replace value="$people{count} + 2" EVAL>
*/
void Sender::processREPLACE (SGMLTag * t, Error & err) {
string s;
int eval = 1;
if ( !(t->flags().contains("VALUE")) ) {
err.addWarning ("REPLACE tag without VALUE flag");
}
// if there is an else flag, try to calculate the bool flag
if ( (t->flags()).contains("ELSE") ) {
if ( (t->flags()).contains("BOOL") ) {
eval = evalBoolString ( (t->flags())["BOOL"], err, t->linebegin()) ;
} else {
//if there is no bool flag, use the value itself as the boolean
eval = evalBoolString ( (t->flags())["VALUE"], err, t->linebegin());
}
}
if (eval) {
s = (t->flags())["VALUE"];
} else {
if ( (t->flags()).contains("ELSE") ) {
s = (t->flags())["ELSE"];
}
}
if ((t->flags()).contains("EVAL")){
evalString(s,s,err,t->linebegin());
} else {
s = interpretString(s);
}
cout << s;
}
/*
@usehtml
@usage <FRAME (SRC="url" | SCREEN="filename")
@usage [CLICK=value]
@usage [SET="tag1=value1;tag2=value2;..."]>
@IFLAG SCREEN SteelBlue filename (relative to BasePages) for the frame
@FLAG SRC URL for the frame
@IFLAG CLICK Value to be set to $this{click}
@IFLAG SET Pairs of tags and values to be set in the this namespace
@sections HTML
@index Creates a frame tag for either a standard URL or a SteelBlue page.
Creates a frame tag for either a standard URL or a SteelBlue
page.
<P>
If SRC is used, a standard URL is used as the frame destination. If
SCREEN is used, a SteelBlue page is used as the destination.
<P>
If SCREEN is used, values in the $this namespace in the destination
HTML section can be set using the CLICK and SET flags. The CLICK flag
sets $this{click} with the given value. The SET flag supplies a set
of tag and value pairs in the form "tag1=value1;tag2=value2;...".
This results in $this{tag1} being set to "value1", $this{tag2} being
set to "value2" and so on.
<P>
Please note that the CHECK and ACTION section is executed once for
every FRAME tag with a SCREEN flag.
@ex <HTML>
@ex <FRAMESET rows="50%,50%">
@ex <FRAME SRC="http://www.tcg-inc.com/">
@ex <FRAME SCREEN="welcome.sb">
@ex </FRAMESET>
@ex </HTML>
*/
void Sender::processFRAME (SGMLTag * t, Error & err) {
AssocArray * flags = &(t->flags());
AssocArray::iterator i = (*flags).begin();
// did they use all the args?
if (! ((*flags).contains("SCREEN") || (*flags).contains("SRC")) ) {
err.addWarning ("FRAME tag found without a SCREEN or a SRC flag");
}
string screen = interpretString ((*flags)["SCREEN"]);
cout << "<FRAME ";
while (i != (*flags).end()) {
if (i->first != "SCREEN") {
cout << " " << i->first;
if (i->second.length() > 0) {
cout << "=\"" << i->second << "\"";
}
}
i++;
}
if (screen != ""){
string sbref = (*config)["sburl"];
if (sbref.length() < 1) {
sbref = config->getDATURL();
}
cout << " SRC=\"" << sbref << "?" ;
cout << screen << "+" << security->getTicket() << "+"
<< security->getDest();
// now cout any vars we want set here
if (flags->contains("CLICK")) {
cout <<"+"<< http->toHTML("click") << "+" << http->toHTML( (interpretString((*flags)["CLICK"]).c_str()) );
}
if (flags->contains("SET")) {
string set;
set = (*flags)["SET"];
while ( set.length() > 0 ) {
string next_pair = set.substr(0, set.find(";"));
string tag = next_pair.substr (0, (next_pair.find ("=")));
string value = next_pair.substr (next_pair.find("=") +1);
cout << "+" << http->toHTML(tag.c_str()) << "+" << http->toHTML(interpretString (value).c_str());
if (set.find(";") != string::npos) {
set = set.substr(set.find(";")+1);
} else {
set = "";
}
}
}
//finally close the quote for the SRC
cout << "\" ";
}
cout << ">";
}
/*
@nodoc
*/
void Sender::processFUN (SGMLTag * t, Error & err) {
userdata->doFun (t, this, err);
}
Error Sender::send (void) {
Error err;
SGMLTokenList & stl = parser->htmlTokens();
if (parser->errors().numFatalErrors() > 0 ||
parser->errors().numWarnings() > 0){
outputErrors(parser->errors(),security->getDest());
}
// tell the userdata object wo we are
userdata->setExec (this);
sendHeader();
// cout << flush;
try {
recurseOnTokens ( stl, err );
}
catch (ExecutorAbort ea){
// Return gracefully
}
// If we didn't send the last errors, send them now...
if (!sent_errors){
sent_errors = 1;
outputErrors(last_errors,security->getDest());
}
sendFooter();
// cout << flush;
return (err);
}
/* look at each token, and make it 'go' */
void Sender::recurseOnTokens (SGMLTokenList & tokenlist , Error & err) {
SGMLTokenList::iterator i;
i = tokenlist.begin();
while (i != tokenlist.end() ) {
// cout.flush();
callFunction ( (*i), err);
i++;
}
}
void Sender::execute (SGMLTokenList & tokenlist, Error & err) {
try {
recurseOnTokens (tokenlist, err);
}
catch (ExecutorAbort ea){
// Return gracefully
}
}
void Sender::sendHeader (void) {
printf ("Content-type: text/html\n\n");
// printf ("Set-Cookie: click=;\n\n");
printf ("<HTML>");
}
void Sender::sendFooter (void) {
printf ("</HTML>\n");
}