Useful Components for CGI applications
Gabriel Corneanu
Description
This is a package for developing CGI applications using Delphi (C++ Builder).
One big difference (I would call it advantage) between these components and
other packages is that they are used in extension to the standard Borland components
for CGI applications.
Do you need to generate form elements in Delphi? Do you want to include live
images in your pages? Try this!
History
Version 2.5
- Big changes. Full support (using IProviderSupport interface) for images!
This is independent and should work with (almost) all database engines. It
should be possible to port to Kylix, but there is no Jpeg support yet.
Added a few more fields (HTMLComboField, HTMLProducerField); changed relationship
details between HTMLProducer and the other producers.
HTMLValueFields expression now includes by default (in some cases) the field
names. Of course, you can modify it as you want.
Support for bitmaps in Access databases (it can skip the OLE header).
Fixed small bugs.
Version 2.3
- Generated package and demo projects for C++ Builder 5. They should work
fine.
Version 2.2
- Isolated the midcomp unit; you can use it now on Delphi 5 Pro, removing
the MIDCOMP define.
Version 2.1
- Fixed a small bug, and the demo projects
Version 2.0
- Added internal handling
- Basic documentation.
Version 1.8
- Added (live) Image support (both single and field).
- Added MultiPageProducer.
- Added property editors.
- Registered a property category, for easily usage.
Version 1.5
- Added HTMLProducer and the other custom producers: Static, Hidden, DBField,
and Combo.
- Added HTMLInQuery for MidasProducer.
Version 1.0
- First functional version. Not published.
- Contained only basic field components.
Custom producers
The base component is HTMLProducer. It is a direct descendant of PageProducer,
with some enhancements. For every custom tag (or transparent, <#tagname>)
found in the HTML source, it tries to find one producer that handles the tag.
This means the programmer doesn’t have to write code for OnHTMLTag event. CustomTags
is the property for matching between tags and producers. The special property
editor provided helps you to edit at design time.
The other producers are all descendents of one ancestor, HTMLBase (abstract),
which is also a CustomProducer.
Previous versions (before 2.5) had a hard relationship between HTMLProducer
and the descendants of HTMLBase producers, and you couldn't use standard producers
(like TableProducer) in response to a custom tag. The new approach removed this
limitation. When you open a project buid with old components, you will get some
errors about missing properties. Ignore all of them and then use the CustomTags
property editor to remap the producers with custom tags.
The HTMLBase introduce one common property:
- HTMLCustomParams: extra parameters for the generated tags. It means, for
example, that you can include for the HTMLCombo a custom parameter like
onchange="form.submit()", and it will be included in the generated
HTML: <select name="name" onchange="form.submit">.
The producers are:
- HTMLStatic: it is a simple producer, which includes a text directly into
the HTML result. There is one property, HTMLValue (string), which represents
the result.
- HTMLHidden: it is a producer that generates a hidden field, to be included
into a form. The properties are HTMLName and HTMLValue, which define the HTML
name and value for the hidden field.
- HTMLCombo: is a producer that generates a SELECT field for a HTML form (the
name reflects the Delphi equivalent). It generates options from a dataset,
or from a list specified at design. If both sources are set, the fixed options
are generated first, then the dynamic ones.
Specific properties:
- Dataset: the dataset to read from. If it is not active, it will try
to open it at runtime.
- HTMLValueFields, HTMLVisibleFields: expression for generating the value
of HTML Option tag, respectively the text of the tag. See HTMLFields
for details.
- HTMLStaticOptions: StringList for generating options at design time.
- HTMLSelection: string for determining the initial selection (if set).
- HTMLSize: the HTML size parameter (how many options are visible).
- OneEmpty: generates one empty option (at beginning).
- OnGetChecked: event for better control of the initial selection.
- HTMLDBField: a simple producer like HTMLStatic, but it generates the result
from Dataset and Field properties. It also wraps the result with a font tag,
with parameters from the HTMLTextAttributes property.
- HTMLImage: it generates an image reference in the document. Here it is only
a short description; please see Images for details.
- HTMLName, HTMLValue: the same as above. They are rarely used with images
in HTML.
- HTMLHeight, HTMLWidth: size of the image. If 0, they are not generated
in the output.
- OnGetImageAction: the event for delivering the image. It is a standard
THTTPMethodEvent. You can use the parameters to get the context, and return
the right image.
- Dataset: the dataset to link to. If set, it is possible to use InternalHandling
(see Images).
- HTMLImageField: specify the field in the dataset containing the image
(see Images).
- HTMLImageTable: specify the table in the database containing the image
(see Images). Usually it is initialized automatically
from the dataset or from the image field, but if the dataset is a complex
query you may need to set it manually.
- HTMLImageType: specify the generated tag; iImage (default) means the
result will be IMG tag, and iInputImage means the result will be INPUT
tag with IMAGE type (to be used in HTML forms). See the generated HTML.
- HTMLKeyFields: fields used to generate parameters for image source.
Usually it is a key identifying a record in the above table. See the generated
HTML.
- UseInternalHandling: the magic property. If you want to get the
image from a dataset, and set the right values for the previous 3 properties,
then you can try to let the component deliver the image! See Images.
- JpegQuality: If using iternal handling and the images have to be converted
to jpeg, this quality of the generated image (see TJpegImage)
- HTMLSrcParams: extra parameters you want to add to the image SRC. If
using internal handling, it is ignored.
Custom Fields
The purpose of this group is to help generate HTML form
elements to be used with a DataSetTableProducer. This is a very useful component,
but unfortunately it was no way (until now) to include (for example) another
column for making a selection (check or radio), or an edit field for each row
(now even a Image!). And I couldn’t live without it.
The solution is to add fake fields into the dataset and let them generate the
right HTML! You can enjoy the editor provided with Delphi, and preview the result
(not applicable to Images)! So open the fields editor for a table and click
new field. Choose a HTML... field, and add a column to the TableProducer, linked
to this field.
Warning: the classic (inherited) properties have noeffect on these fields. They
are calculated fields. Don’t use them for other purposes (like normal editing).
Most of the fields have 2 base properties:
- HTMLNameFields: Fields expression for generating the name of the HTML
tag.
- HTMLValueFields: Fields expression for generating the value of the HTML
tag. For HTMLMemo it is the text of the tag; for HTMLText it is the result
text.
Field list:
- HTMLCheckBoxField: generates a HTML check element. It has some extra properties:
- HTMLCheckField: Fields property that dictates the checked status. Must
evaluate to boolean.
- OnGetHTMLChecked: event for more control over the checked status. It
is called after evaluating the previous property.
- HTMLRadioFields: generates a HTML radio element. It has the same properties
as the CheckBoxField. It usually has a fixed expression as HTMLNameFields,
to act as one group. Of course, it is your decision.
- HTMLEditField: generates a HTML edit element (type text). You can specify
HTMLMaxChar and HTMLSize properties.
- HTMLMemoFieds: generates a HTML memo (textarea tag). It also has some specific
properties: HTMLLines, HTMLSize, HTMLWrapKind. They are directly reflected
in the HTML result.
- HTMLText: generates a text (like HTMLDbField). The purpose is to wrap an
existing field with some FONT properties, witch are not available through
the columns of the TableProducer.
- HTMLImageField: generates an image reference in the HTML document. Its behaviour
is similar to the HTMLImage component, but it is intended to show the image
contained into one database field (see the samples) in the context of a TableProducer.
The difference is that you can use other fields for generating name and value
(HTMLNameFields, HTMLValueFields).
- HTMLComboField: generates a HTML select tag. It works like a HTMLCombo component,
but the DataSet property is renamed HTMLOptionDataSet, because it would conflict
with the original property (inherited from TField). It doesn't accept the
same dataset as the one that owns the field, because it would cause an endless
recurrence (it needs to step through all the records in dataset).
- HTMLProducerField: a very simple field that allows you to to take the content
from another producer, specified in HTMLProducer property.
HTMLMultiPage
A single component designed to help present the content of a dataset (possible
search results) in a multi page manner (every page can have a variable number
of rows/columns). It is also a custom producer described in the beginning, so
you can include it in another page generated by an HTMLProducer. Specific properties
and events are:
- DataSet: the dataset to go through.
- HTMLCell: any producer that generates HTML. You can use another HTMLProducer,
but take care not to use the same as the parent (may cause a loop).
- HTMLContentBefore, HTMLContentAfter: other producers to include at the
beginning (or the end) of the content.
- HTMLRowsAttributes, HTMLTableAttributes: some attributes for the generated
HTML table.
- NumCols, NumLines: number of columns and lines (rows) of the HTML table.
- NumLinks: number of links to generate at the bottom of the content.
- PageNumVar: name of the parameter used for the page number. It is also
used as the text of the links.
- OnGetNumRec: event for getting the number of results. If using a table,
you can use RecordCount; for a query, you may need to use another one with
something like "select count(*) from ... where ...".
- OnPreparePage: event for preparing the page content. You can use it for
going to the right record in the dataset.
- OnPrepareCell: event for preparing the cell content. You can take some
extra actions here.
- Note: for every cell, the Next method of the dataset is called. So you
don’t have to do it in the OnPrepareCell event.
This component is not finalized (it doesn’t mean that the
others are prefect, but here it is space for more work). Any ideas/suggestions
are welcome.
HTMLFields
This is a description of the format used by some properties (HTMLValueFields,
HTMLVisibleFields, HTMLNameFields...). Usually the parameters for the tags are
combinations depending on some logic. The rule is simple: any text surrounded
by "%" is considered to be the name of a field and replaced by the value of
that field. You can use any combination, like "N_%ID1%_%ID2%". If no "%" is
found, the output is constant. The "%%" sequence is replaced with "%".
Example: if you have a combo with some persons, you may want to use some id
as the value (the key of a table), and as text a combination of first name and
last name, like <OPTION value="123">JOHN DOE<OPTION>. For this,
you can set HTMLNameFields="%ID%" and HTMLValueFields="%F_Name% %L_Name%". For
radio elements: they are grouped by name, so usually it is a constant, but the
value is a key identifying the row. For checkbox elements, usually it is the
opposite: the name depends on a key, and the value is a constant like "on" or
"true". By default, the property editors suggest a combination of HTML FieldName
and another field.
For HTMLKeyFields, this is not applicable: it expects to find only a list of
existing fields, and you can choose from existing indexes (primary or unique).
It is intended for internal use (see Images). The same for HTMLImageField; it
only wants a name of a field.
Images
Here I’m trying to describe what’s going on with (live)
images. As you know, an image is not part of the HTML document; it is only a
tag containing a link to the actual images. Let’s suppose you have images in
a database, and want to output them in HTML. You can save the image to a file,
and generate a link to it. Outside of a good garbage collection mechanism for
deleting old files, this is not very nice. It is the same problem in JSP, or
ASP. The Web server environment must provide some help.
I didn’t want to use other storage for the files. And I
used another solution: to generate a link back to the same CGI application.
Of course, the next call should return the actual image, not the default content.
If you are carefully, you can do it in Delphi. But I have already built some
support for it, so give it a chance.
How it works: for every image, it is generated a link to the same CGI, but
a different pathinfo. Don’t worry; everything is done internal, and only at
runtime. You only have to write code in the OnGetImageAction event. For this,
you can use some helper functions (declared in HTMLCommon):
- procedure SetResponse(Response:TWebResponse; form:TCustomForm);overload;
- procedure SetResponse(Response:TWebResponse; bmp:TBitmap);overload;
- procedure SetResponse(Response:TWebResponse; ctrl:TWinControl);overload;
- procedure SetResponse(Response:TWebResponse; fld:TField);overload;
All of them build an image from a form, from a bitmap, from
a WinControl or from a Field (it must be a Blob field, and contain a valid image)
and give it as response. As you also know, the usual format for HTML images
is jpeg, so all the images are sent in jpeg format (this is because Delphi has
support for it). But if the field contains a valid GIF image (or a JPEG image),
it is left untouched. I am only looking for some header values, so please report
any error.
Now if you have a simple HTML page, it is ok. But if you
have dynamic pages, like the content of a table (with images), this is not enough.
You can’t know in the event what image to deliver. The only help is if you use
some parameters as part of the request (included in the image source link).
If we are sending the key of the row, than we can handle it.
This is the idea. It is not that hard, but it requires some
work. So I tried to do as much as possible. I think that in 95% you only have
to specify the right properties: keys, image table and field (I’m trying to
build a query, so I need the field name, table name and the row key). In 50%
I can fill some of these automatically: if using a TTable, the HTMLImageTable
is automatically filled with the TableName property and the indexes are scanned
for a primary key (or a unique key), and assigned to HTMLKeyFields property;
if it finds a TGraphicField, the HTMLImageField is also filled (usually they
are just blob fields, so you have to fill it). If using a TQuery and you are
including the image field in result, it might help to find the table from the
Origin property. There are registered property editors for these properties,
but usually you shouldn’t include the image field in the dataset used for generating
the table (we don’t need the image field at this time); in this case you won’t
find the field in the list.
To resume: if the HTMLKeyFields are set, the result is something like <IMG SRC="sameCGI/pathinfo?key1=val1&key2=val2...">.
The HTMLImageTable and HTMLImageField are used at the next call (if using internal
handling). You can specify extra parameters through HTMLSrcParams, but only
if not using internal handling.
Other things: the link to the CGI is well formed, even if you are inside some
other path. Even more, it is a relative link: try /path/Project1.cgi
and /path/Project1.cgi/, and look at the HTML source!
I hope I didn’t forget any important thing. I think that
the samples included are quite useful.
Enhacements
There are several property editors, and a special catogory for all the specific properties. Right click in the Object Inspector and choose arrange/by category.
Known Issues
- First I must say this is not a problem with my components. I found it while
trying to test the demo projects. I don’t know where this is coming from,
but in some cases selecting a blob field in a query with some SQL drivers
may return a limited size of the blob. I found this storing some images (>32K)
in a MySQL table (LONGBLOB field), and then trying to get them with a query,
via ODBC driver. I was surprised to see my pictures truncated. This is not
happening if using a TTable component. I don’t know if this is normal, nor
if it's happening with some other SQL links.
This can appear when using internal handling for image components, because
I use a query to do it.
The only way to avoid it is using a TDatabase for the connection and setting
the parameter BLOB SIZE=xx, where xx (in Kb) is large enought for any blob
in your particular table.
- The internal handling for images is implemented running a SQL statement
using the IProviderSupport interface of the original dataset. This means it
should be independent and work with any database engine that implements this
interface (all Borland solutions do: DBE, ADO, IBExpress). It may be some
errors in the generated sql, so please report them.
- I have tried the package with MySql, MSSQL. When a table and/or field contain
invalid characters in name (like spaces), the SQL is dependent on the server.
For local (Paradox) tables, they must be enclosed in double quotes; MSSQL
requires the "[" and "]" characters. Currenly, it only
generates quotes in the query; so it won't work with MSSQL. It needs some
work here, to generate more appropriate statements.
- After recompiling the package (not first time!), please close and restart
Delphi. There is no way to "unregister fields" in Delphi, so if
you open the new field dialog you will get an error. This is because (I had
a look at the sources) Delphi keeps the registered fields in a list (as pointers),
and, without removing them, after recompiling the pointers are invalid. Look
at RegisterFields procedure.
Gabriel Corneanu
Email: gabrielcorneanu@yahoo.com