freeCGI Tutorial: Introduction to OOCGI

Object Oriented (OO) applications promote re-use of ready-made objects in various projects. Let's take a look at a conseptual example first, then show how it could be implementerd. Your company has to get a set of CGIs to survey users on a new product you just released (Whammo Widgets!). You can just write the HTML code with forms, then a small CGI to get the data and save it. You are done. Now what if your boss comes by and informs you that they now have a new line of add-ons to the Whammo Widgets that make them better. These add-ons are only used by a small group of users who demand that extra feature (an 11 on the volume dial if you must).
Now the dilemma begins, how do you survey different customers about their needs? An easy way would be to write more HTML and more CGIs and be happy. But your boss is never happy and will have more products.

This is where OO design is important.


We will now dive right into developing the Object Oriented Common Gateway Interface (OOCGI) Applications. Let's first begin with an object called WhammoCompany and an object to be used for surveys:

Please note that the code provided is in segments to concentrate on important aspect of the design and is far from complete

class Survey
{
  public:
    Survey(const char *pccUsername);
    virtual void display(AStreamOutput *posOut) = 0;

  private:
    char *m__pccUsername;       //Used to customize your surveys to each user
};

class WhammoCompany : public ACGI
{
  public:
    void display(Survey &survey);
};

Nothing too tricky right! You now have a company object and a survey object. Your survey object is pure virtual, which means you will probably have many of them which will override the display() method.

We also know that is a common header and a footer that you use to show logos, banners, company e-mails and such. Let us add that functionality:

class WhammoCompany : public ACGI
{
  public:
    void displayHeader();
    void displaySurvey(Survey &survey);
    void displayFooter();
}

Let's take a look at a sample implementation of WhammoCompany object so far:

void WhammonCompany::displayHeader()
{
  mimeHTML();                          //First ever thing to a browser is a MIME directive
  htmlStartHTML();                     //Start the HTML page
  htmlDoHEAD("Whammo Company Survey"); //Do the HEAD and TITLE in one
  htmlStartBODY(0xF0F0F0,              //White background 
                0x101010,              //Black text
                0xFF0000,              //Red link
                0xFF0000,              //Red visited link (links don't change color after visit)
                0xFF00FF,              //Activated link changes color
                "images/bkground.jpg"  //Background image
               );
  
  
  htmlStartTag("H1", "ALIGN=CENTER");  //<H1 ALIGN=CENTER>
  outStringN("Whammo Company");        //This method is defined in AStreamOutput
  htmlEndTag("H1");                    //</H1>
}

void WhammoCompany::displayFooter()
{
  //Display footer info
  htmlStartTagEx("H5", "ALIGN=CENTER")
   outStringN("For more help please email ");
   htmlDoTagEx("A", "HREF="mailto:webmaster@whammo.com", "our webmaster");
  htmlEndTag("H5");

  //End the BODY and HTML tags
  htmlEndBODY();
  htmlEndHTML();
}

void WhammoCompany::displaySurvey(Survey &survey)
{
  survey->display(this);
}

There is an interesting thing happening here. Each survey object will use this to output itself correctly. Naturally next we should look at the survey object and see how we would implement them. Let's say we have 2 surveys, basic and advanced:

class BasicSurvey : public Survey
{
  void display(AStreamOutput *posOut)
  {
    *posOut << "This is a basic survey.
" << endl; } }; class AdvancedSurvey : public Survey { void display(AStreamOutput *posOut) { *posOut << "This is an advanced survey.
" << endl; } }

The body was inlined for clarity, your display method will be a bit more complex I hope. We now have declared (and implemented) 4 objects. Let us assume that our CGI will be called as follows:
http://www.whammo.com/survey.cgi - to specify which survey the use wants
http://www.whammo.com/survey.cgi?Survey=Basic - for a basic survey
http://www.whammo.com/survey.cgi?Survey=Advanced - for advanced survey

Now see how we can use them in a CGI:
NOTE: the lower case prefix in freeCGI is an abbreviation of the class that the method was inherited from:
Example:
  plGetValueByName() is from APairList and retrieves VALUE from given NAME in a pair; (NAME=VALUE)
  cgiGetFormItems() is from ACGI class and collects and parses all NAME=VALUE pairs using AFormList class

//Include ACGO++
#include "a_acgi.h"

//Inclulde your objects: WhammoCompany, Survey, BasicSurvey and AdvancedSurvey
#include "whammo.h"

int main()
{
  WhammoCompany whammoObject;      //This iwill create the correct association to ostream as would ACGI object

  //Let's display a common header first


  whammoObject.cgiGetFormItems();  //Get all FORM items that were passed to this CGI

  //Now let's figure out what the user wants to do
  //ACGI is made up of 2 objects AHTML and AFormList
  //AHTML is used for output only and AFormList is a FORM item processor
  //We will get the pointer to VALUE of "Survey" or NULL if not found
  const char *pccValue = whammoObject.plGetValueByName("Survey");
  if (pccValue == NULL)
  {
    //a_User was not asked what survey to use
    whammoObject.cgiStartFORM();              //Create a FORM tag with reference to itself
    
    //Quick FORM (this is pretty ugly but you can have a much nicer looking form :)
    whammoObject << "Enter name: <INPUT TYPE=TEXT NAME=\"Username\" VALUE=\"Anon\">" << endl;
    whammoObject << "Survey type: <SELECT NAME=\"Survey\"> <OPTION>Basic<OPTION>Advanced</SELECT>" << endl;
    whammoObject << "<INPUT TYPE=SUBMIT>" << endl;
  }
  else
  {
    if (strstr(pccValue, "Basic") == 0)
    {
      //Basic survey request found
      BasicSurvey survey(whammoObject.plGetValueByName("Username"));
      whammoObject.display(survey);
    }
    else if (strstr(pccValue, "Advanced") == 0)
    {
      //Advanced survey request found
      AdvancedSurvey survey(whammoObject.plGetValueByName("Username"));
      whammoObject.display(survey);
    }
    else
    {
      //Unknown
      whammoObject << "I do not understand!<BR>" << endl;
    }
  }

  //Now show a common footer
  whammoObject.displayFooter();

  return 0;
}

You now have a company object that can be reused as well in other CGIs even if they do not need surveys. Using this ideology you can build objects that can be placed into a company library and used by future CGI applications. I will not go into further discussion on OO, that could be found in many C++ and Smalltalk texts.


Here are some hints on using ACGI: