Groundwork Concepts:

Fundamentally, groundwork enables developers to build web-based applications using html template files and CGI's or Apache modules written in C++.

CGI's or Apache modules provide the intelligence of the application and html templates provide skins for it. Groundwork makes it easy for an application consisting of a single set of CGI's or Apache modules to have multiple skins.

For example, an application could have one skin with lots of images on it, another for low-bandwidth clients with very few images, another skin for text-mode browsers, and another high-contrast skin tailored to the visually impaired. A single set of CGI's or Apache modules would drive the application and a set of html templates would be chosen at run-time to provide the skin.

The template file syntax is simple, providing substitution variables and directives for grouping html into segments and including other template files.

Groundwork provides classes for processing templates, generating HTTP headers, handling form submissions, and performing other common web-based programming tasks. Groundwork also abstracts the web-server communication layer, enabling the same code to be compiled as a CGI or Apache module.

The Directory Structure:

In groundwork nomenclature, an individual CGI or Apache module is referred to as a program. An html page that a program can process is called a template. A group of program's is called a module and a group of templates is called an skin. Collectively, the skin and modules are called the application. An application usually has a set of modules and one or more skin per module.

In the model directory structure below, words in bold would be replaced with application-specific values.

Groundwork interprets this directory structure as follows:

When developing with groundwork, the following directory structure guidelines are recommended.

Groundwork determines which application a particular program belongs to at run time by parsing out the path between the DOCUMENT_ROOT and the "modules" directory. As a result, the directory name of an application can changed at any time, provided that it's not hardcoded into a program. The application directory can be nested anywhere under the DOCUMENT_ROOT, even inside another application, as long as it's not nested inside the "modules" or skin directories.

By default, groundwork determines uses the skin directory named by the PATH_INFO environment variable. Groundwork assumes that it should use the templates in the skin directory named by that variable. If no PATH_INFO is passed in, groundwork uses the "default" skin. Thus, in the absence of the variable, template pages in the skin directory named "default" are used.

Skin Variables:

In addition to defining what an application will look like, a skin can also define a set of variables and values to be used when that particular skin is selected. For example, an application might want to access a different database when displaying a particular skin, or it might want to set flags to enable or disable special features of the application when displaying a skin.

When a skin directory is selected, groundwork looks for .var files in that directory. These files can define variable/value pairs which can be interpreted by the application as a skin-specific configuration.

First, groundwork looks in the application directory for a file with the same name as the skin and a .var extension, then as it descends the skin directory tree, looking for an html template corresponding to the program, it looks for .var files in each subdirectory. Each .var file can define new variables and values or override previously encountered values.

For example, if the application directory for a particular application is /home/httpd/html/myapplication, and a particular program is /home/httpd/html/myapplication/modules/module1/submodule2/program1.cgi, and that program is told to use the "lowgraphics" skin, then it will look for the following .var files:

/home/httpd/html/myapplication/lowgraphics.var
/home/httpd/html/myapplication/lographics/module1.var
/home/httpd/html/myapplication/lographics/module1/submodule2.var
/home/httpd/html/myapplication/lographics/module1/submodule2/program1.var

If it finds any or all of these .var files, it will process them.

A Sample Application:

Below is a sample application illustrating the groundwork directory and file structure.

Say we wanted to write an e-commerce system consisting of a catalog, a shopping cart and a checkout sequence. Now lets say we wanted to present 3 different skins to this application: the default skin, a graphically intense version for high-bandwidth connections, a low-graphics version for for low-bandwidth connections and a version for text-only or wireless platforms.

One way to divide up the project would be to have 3 applications: catalog, cart and checkout, each with 3 skins: highgraphics, lowgraphics and textonly.

The catalog might have product line pages and product detail pages. The cart might just have a cart page. The checkout might have a page where it gets your information, a confirmation page and a receipt page.

Here's how you would lay out this application using groundwork.

In the example above, "default -> highgraphics" indicates a symbolic link between "highgraphics" and "default".

Of course, you have to write all those CGI's and Apache modules and HTML templates.

An Example Application:

Below is a sample application that illustrates some of groundwork's functionality.

HTML Template /usr/local/apache/htdocs/example/default/example.html

<html>
<body>
The browser is $(HTTP_USER_AGENT).<br>
The remote IP is $(REMOTE_ADDR).<br>
A variable passed into this cgi is $(variable).<br>
<br>
Here is a list of numbers 0-4 with a sub-list of 0-4 for each list entry.<br>
<ul>
<!-- start list -->
<li>list item: $(number)</li>
<ul>
<!-- start list -->
<li>sublist item: $(number)</li>
<!-- end list -->
</ul>
<!-- end list -->
</ul>
</body>
</html>

Code for /usr/local/apache/htdocs/example/modules/example.cgi

#include <groundwork/groundwork.h>
#include <groundwork/cgimodule.h>
#include <string.h>

class example : public groundwork {
        public:
                        example(void *apistruct);
                int     handleSegment(strstream *container,
                                                char *name,
                                                char *segment);
                        // override groundwork::handleSegment()
        private:
                void    sendList(char *segment);
};

example::example(void *apistruct) : groundwork(apistruct) {}

int example::handleSegment(strstream *container, char *name, char *segment) {

        // if the tag: <!-- start list --> is encountered, then
        // the sendList() method is called to process everything between 
        // the <!-- start list --> and <!-- end list --> directives
        if (!strcmp(name,"list")) {
                sendList(segment);
                return 1;
        }
        return 0;
}

void example::sendList(char *segment) {

        strstream *index;

        for (int i=0; i<5; i++) {

                index=new strstream();
                *index << i << ends;

                // this call invokes parseSegment() which will write out
                // the string pointed to by segment, replacing occurrances of 
                // $(number) with index->str() and call handleSegment() 
                // recursively for any nested segments
                parseSegment(NULL,segment,
                                NULL,
                                "number",index->str(),
                                NULL);

                delete index;
        }
}

extern "C" {

#ifdef APACHEMODULE
        #define NAME test
        #include <groundwork/apachemodule.h>
#else
        #include <groundwork/cgimodule.h>
#endif

MAIN {


        // instantiate an instance of example which will in turn instantiate
        // an instance of groundwork and it's subclasses
        example ex(APISTRUCT);

        // invoke execute() which will send a simple mime header and invoke
        // parseTemplate() on the html page parseTemplate() will replace $(...) 
        // directives with environment variables, variables passed into the cgi 
        // from a form submission, or special paths class variables.  In
        // addition, parseTemplate() will handle <!-- include ... -->
        // directives internally and <!-- start ... --> ... <!-- end ... -->
        // directives by invoking example::handleSegment()
        ex.execute();

        return RETURNVAL;
}

}

If the above cgi were compiled and accessed in a browser using a url like: http://server.domain.com/example/modules/example.cgi?variable=hello then the following output would result:

The browser is Mozilla/4.7 [en] (X11; U; Linux 2.2.14 i586).
The remote IP is 204.0.85.37.
A variable passed into this cgi is hello.

Here is a list of numbers 0-4 with a sub-list of 0-4 for each list entry.

This is of course, a very simple example which demonstrates only a fraction of the capablities of groundwork.

Database Access:

The recommended method for accessing databases from web-based applications is the firstworks project SQL Relay, though any C or C++ compatible database access technology should be compatible with groundwork.