This archive, containing the HyperCard stacks"People.acgi" and "Home", and a WWW form "People.html", in addition to this Readme file (Text only and HTML version), is both an example for the potential of Hypercard as a CGI application, and a basic version for a Web-accessible database which can be easily expanded. (If you repeatedly download an incomplete version of the archive, try an ftp-download.)
The project is a simple address database, which allows the input, modification, and deletion of datasets, and the searching of the database via the WWW. In the current setup, input is allowed from every Web surfer coming by (so you could use it as your guest book), but can be restricted to, e.g., clients from certain domains (see below for the necessary modifications), or can be password protected from the Web server (see the manual of your server program for details). Currently, every single dataset is password protected, "belonging" to the person who entered it, and allowing him/her to later modify or delete the entry. (If no password is used on entry, a master password is assigned to prevent tampering with the data). You as the owner of the stack have superuser privileges simply because you can access all data from within HyperCard.
put empty into the last word of it put empty into the last char of itand uncomment (remove the leading --, activating the lines) the two lines after them:
-- put empty into the first word of it -- put empty into the first char of itIf the first entry in your application menu does not contain the word "Hypercard.acgi", then you have either forgotten to rename the program, or have started another version of Hypercard on your computer. In the first case, perform the renaming, in the second, start Hypercard.acgi by dragging this Home stack on the icon of the application.
on makeFoot global Foot --------------------> Replace "webmaster@yourSite.com" with your e-mail address (and "/Default.html", if your home page has a different name or location). -- The first card of stack (named "Main"!) should have a card field "LastEdited" which indicates when the database has been updated. put "<P>[<A HREF=" & quote & "/Default.html" & quote & ">Go Home</A>]<HR><I>Database last edited:" && card field "LastEdited" of card "Main" into Foot
put "<BR><A HREF=" & quote & "mailto:webmaster@yourSite.com" & quote & ">webmaster</A></I></address></BODY></HTML>" after Foot --<------------------ end makeFoot
The program converts returns in the multiline field to <BR> (the Web browser converts them back to returns), and quotes (") and < > to their character entities " < >
Randomly placed quotes can do bad things in Hypertalk, and < > would otherwise be interpreted as HTML tags. If you want to allow the inclusion of HTML tags (which makes it possible to enhance text with <STRONG> or to include a link in the message, but also to submit defective code which messes up the page), remove the various QuickReplace((it),"<","<") etc. in the script of People.acgi.
It is possible and fine to enter character entities into the Web form. They are of the format &entname; with entname the symbolic name of the char, or &#number; with number representing the decimal ASCII number (ISO Latin, of course) of the char (e.g., or   for a non-breaking space). Use & to represent the ampersand (&).
If a new dataset is submitted, the Web data are parsed and distributed to Hypercard variables, a new card is created (chance to check for the number of cards here), and the variables are written to the fields. If no password has been entered, the default password is put into the relevant field.
If a search, modification or deletion is requested, the database is searched to see whether the criterion is met by several entries (in this case, a summary is returned, with each line containing a link to one of the cards, with the primary request as the direct argument of a search request: <A HREF="Hypercard.acgi$Delete?ID"> with ID an identifier number for the dataset; choosing one of the entries triggers the requested action), or fits one entry only. In the later case, the full data of the found card is returned: in reply to a search as ordinary text; in reply to a deletion request, as part of a form which contains the identifier for the dataset in a text field of type "hidden" (so it is not shown to the user and cannot be modified, but is sent back with the conformation of deletion, allowing the identification of the correct dataset), and a field for the password. In reply to a request for modification, the data are inserted into the fields of a form similar to the data entry form, ready to modify; as for the deletion, a password is requested and the identifier included in a hidden field. The password is checked for identity (get field "Secret" if it is Password then (Action) else (Rejection)). The program performs the action and confirms it by replying the new version of the dataset, or the dataset before deletion.
Some error handling is built in and produces a reasonable reply to the client: for the case a dataset has been deleted before the modification/deletion has been submitted; if an ID is requested which is not present in the database, if the direct argument or the path args are other than expected.
if Mode is "NewData" then if the number of cards of this stack >1000 then go last doMenu "Delete Card" end if(or reply: database at limit, and reject data entry)
get the date set the itemDelimiter to "," convert it to dateItems put item 3 of it & "." & item 2 of it & "." & item 1 of it into field "Submitted" -- This produces the date in the German format day.month.year -- In the example, the entry form has a menu to choose between 2,5,7,10 days and 2,3,4,6,8 weeks until expiration if word 2 of X is "days" then put the secs + (word 1 of X * 86400) into field "Expires" else -- weeks put the secs + (word 1 of X * 604800) into field "Expires" end if get field "Expires" convert it to dateItems put item 3 of it & "." & item 2 of it & "." & item 1 of it into field "ExpirationDate" -- Expiration date for display on the Web page (again, German format)I check the whole database on the first request for a summary of each day
if the short date is not card field "LastEdited" then put the short date into card field "LastEdited" put the secs into Actual put true into Update -- No update performed today else put false into Update -- Expired datasets have already been purged today end ifand when the summary is created by scanning the cards, each card is also checked for expiration:
repeat for the number of cards of this stack - 2 if Update is true and field "Expires" < Actual then doMenu "Delete Card" -- Delete expired dataset else put theReply & "<HR>" & OnePerson(FindPerson) into theReply -- Use for summary go next card end if end repeat
This is just a little tested demonstration project. If you find bugs, I am always interested to know them, but if you present a solution for them, I am even happier. That's one advantage of HyperCard: you have access to the complete source code, so you can (and should) have a look by yourself. But comments are always welcome (don't be too critical with the code: if I were a real programmer, I might have done it far better, but then I would probably not have bothered with HyperCard but had used C or Java instead :-).
Last edited: October 23, 2001 by KaiFr