#!/usr/bin/perl # CEP - CGI Encyclopedia in Perl # # Version 0.4 12/1/97 # # Okay, what a grandiose name! This CGI Perl module is designed to # support listings of definitions and cross-references for topical # information, and also to automatically generate various kinds of # HTML listings from that information. # # The CEP should have its down directory in which to run, and it # will always chdir to that directory on startup. All data files, # templates, schema files, supporting Perl modules, and # everything all live there. # # This Perl CGI program uses the CGI.pm module by Lincoln Stein. # # As this is a CGI module, it is build to accept various CGI field # arguments. Some of these will come from the schema file, but # the constant ones are listed here. Notice that they all start # with _ to distinguish them from actual data fields. Note that # every schema must have a primary key (this is sorta like a # 1-table relational model), and that is simply the first field # listed in the schema. Typically, that field will be "name". # Note that all data field names should be lower-case. # # _add If present, then this is CGI request # is part of an update transaction of # some sort. A form with database # values will be returned. If pattern # match or a primary key value is given # then the form will be populated with # that item if it exists. See also # _store, below. Note that _add, if # present, overrides other instructions. # # _alpha value is a case-insensitive string, # each of them a first letter or other # char for primary keys. So, if you want # all the definitions that begin with 'd' # or 'e' you would send ?_alpha=de # # _diagnose perform a consistency diagnostic check # of the database, checking internal # cross-reference and external links. This # can be very time-consuming. # [NYI - this will have to be a separate # program, I think, otherwise it will # bloat this one too much.] # # _format value is an output report format, one # of 'index', 'short', or 'full'. Only # first letter is used, case-insensitive. # The default is 'full'. # # _get value is a file name to retrieve, no # directory prefix accepted. Depending # on the file extension, the file may # be a template (.phtml), straight text, # straight html, or image. # # _key exact value of a primary key, only # case-insensitive, output delivered as # specified by _format or _add. # # _search pattern match in data, value is a # regular expression to use for matching. # Results returned according to _format # or _add if present. # # _searchfield If set, then apply pattern matching to # primary key only, by default matching # is done against all non-empty visible # fields (s,S,t,T,u,U,r,R) # # _store if present along with _add, and all # non-optional schema fields are received # non-empty, then actually re-write the # data files (backing up previous using # simple numbering scheme?) # # _total similar to _alpha, only sends ALL # the data in the specified format. # # _usersearch include a user search form in the # heading, for searching the data. # Default value is 0 (false). # # _usertab include a quick-reference set of # alphabetic tabs in the heading, for # browsing the data. # Default value is 1 (true). # # _userlink include links section template, # default on in basic_mode 0, off # otherwise. # # # Note that the design of this module limits it: only one # CEP database may live in a directory. # # The most important file for a CEP database is the schema # file. This file consists of lines, each defining a field # in the CEP database (note that the database may or may # not be stored in a single file, I haven't decided yet.) # Each line consists of three strings separated by colons: # # - field CGI form name (identifier) # - field user pretty-print name (string) # - field type, one of the following: # s - short string (default) # S - short string optional # p - primary key URL (special) # e - enumerated short text # E - enumerated short text optional? # r - cross-ref primary key name(s) # R - cross-ref primary key name(s) optional # t - multi-line string # T - multi-line string optional # u - a link URL string, name=href # U - a link URL string optional, name=href # i - an image URL string [NYI] # I - an image URL optional [NYI] # # When CEP starts up, it reads the schema into a pair of # hash tables: %fieldNames and and %fieldTypes. The keys # of either of these hashes can be used to grab stuff out # of the query on updates, or out of restored objects on # loading from a file. # # Note the format for the u and U types, if an # equals sign appears in the string, then the LHS is # a human-readable name for the link, and the RHS is # the link URL. # # For data storage, we use the CGI::save and CGI::new # methods, just like my guestbook and other CGI scripts # do. (This method is not especially fast, though, so # I may need to change it later.) # # # # Constants - set these as necessary $CEP_HOME = $ENV{'HOME'} . '/cgi-bin/cep/'; $CEP_SCHEMA = 'schema.txt'; $CEP_FILE_PREFIX = 'cep-'; $CEP_FILE_SUFFIX = '.dat'; $CEP_FILE_NEW_SUFFIX = '.new'; $CEP_FILE_BAK_SUFFIX = '.bak'; $CEP_ADD = '_add'; $CEP_ALPHA = '_alpha'; $CEP_FORMAT = '_format'; $CEP_GET = '_get'; $CEP_KEY = '_key'; $CEP_SEARCH = '_search'; $CEP_SEARCHFIELD= '_searchfield'; $CEP_STORE = '_store'; $CEP_TOTAL = '_total'; $CEP_USERSEARCH = '_usersearch'; $CEP_USERTAB= '_usertab'; $CEP_USERLINK = '_userlink'; $CEP_FULL_SET= 'abcdefghijklmnopqrstuvwxyz!'; sub geturl { my $q = shift; my $ret = $q->script_name(); # if you need to fix idiocy from server, substitute here! # $ret =~ s/www[.]erols/cgibin.erols/; return $ret; } ################################################################# # templates - these are the HTML+Perl fragments that are used # to build the output HTML pages. Note that # a lot of domain-specific stuff is bound up in # these template files (e.g. URLs, titles, # format, colors, names, links) and therefore # these files will have different contents for # every use of the cep.pl program. Try to # restrict domain-specific stuff to these # files and to the schema, keep it out of this # perl source code! %templates = ( 'header', 'fixed_top.phtml', 'index', 'search_index.phtml', 'links', 'links.phtml', 'notfound', 'notfound.phtml', 'footer', 'fixed_bottom.phtml', 'addstart', 'add_start.phtml', ); # Operational parameters - set as necessary, can be empty $CEP_BASE_IMG_URL = 'http://www.erols.com/ziring/'; # globals used throughout the program $headerSentYet = 0; # libraries we use use CGI; # fundamental subroutines used everywhere sub ensureHTMLHeader { if (!($headerSentYet)) { print $query->header(-type => 'text/html'); } $headerSentYet = 1; } sub punt { my @args = @_; my $lin; ensureHTMLHeader(); print $query->start_html(-title => 'CEP Error'); print "
\n";
for $lin (@args) {
print $lin,"
\n";
}
print "
In phase ",$phase,".\n";
print $query->end_html();
exit 0;
}
# urlenc - URL-encode a string, return it
#
sub urlenc {
my($toencode) = @_;
$toencode=~s/([^a-zA-Z0-9 _\-.])/uc sprintf("%%%02x",ord($1))/eg;
$toencode =~ tr/ /+/;
return $toencode;
}
# print_template -
# given a file, read it through, substituting for
# embedded Perl constructs. The constructs we
# support are:
#
# %%expr%% eval expr, substitute
# result as a string,
# but if expr is just a
# identifier, then treat
# it as a parameter of
# the supplied CGI::
# object.
#
# Expressions can set the dynamically scoped scalar
# $enable to 0 to disable output temporarily, and
# can set it to 1 to re-enable output. This only
# lasts for the current template.
#
# return 1 on success or 0 on error or missing file.
#
sub print_template {
my ($fname, $q) = @_;
my $ret;
my $fline;
my $exm;
my $substval;
local $enable;
$ret = 0;
$enable = 1;
if (open(TFILE, $fname)) {
while( \n";
print "Could not add record to the dictionary: ",$errmsg;
print " \n";
}
}
else {
print " \n";
print "Error: required field missing, database not changed.\n";
print " \n";
print "Sorry, mandatory field '",$fieldNames{$offender},"' ($offender) is empty.";
print " \n";
print "Please hit the BACK button on your browser and fix it!";
print " \n";
}
}
#################################################################
# Phase 6 - output fixed links section of page as a template
#
#
$phase = 6;
if ($query->param($CEP_USERLINK)) {
if (print_template($templates{'links'}, $query) == 0) {
print "\n"," Could not output link section, phase $phase. Sorry";
}
}
#################################################################
# Phase 7 - output fixed bottom section of page as a template
#
#
$phase = 7;
if (print_template($templates{'footer'}, $query) == 0) {
print "\n"," Could not output regular page footer, phase $phase. Sorry";
print "\n","\n";
while ($qret = retrieve_next_record($rset,'','')) {
print_record($fmt, $qret, $cnt);
$cnt += 1;
}
if ($cnt == 0) { print_template($templates{'notfound'},$query); }
else {
print "
\n";
}
elsif ($basic_mode == 2) {
$rset = $CEP_FULL_SET;
$kpat = $query->param($CEP_KEY);
if (!defined($kpat) || ($kpat eq '')) {
$kpat = '';
$spat = $query->param($CEP_SEARCH);
}
else { $spat = ''; }
print "\n\n";
print "\n";
while ($qret = retrieve_next_record($rset, $kpat, $spat)) {
print_record($fmt, $qret, $cnt);
$cnt += 1;
}
if ($cnt == 0) { print_template($templates{'notfound'},$query); }
print "
\n";
}
elsif ($basic_mode == 3) {
my $addme = $query->param($CEP_ADD);
if ($addme eq ' ') {
print_template($templates{'addstart'},$query);
}
else {
my $add1 = ($addme =~ m/^([a-z])/i)?($1):('!');
my $qret = retrieve_next_record($add1,$addme,'');
if (!($qret)) {
$qret = new CGI;
$qret->param($fieldNamePrimary, $query->param($CEP_ADD));
}
# print_add_form uses the schema heavily
print_add_form($qret);
}
}
elsif ($basic_mode == 4) {
# first check that all non-optional fields are non-empty,
# lower-case field type means mandatory!
my $ok = 1;
my $offender;
KEYS: foreach $fn (@fieldKeys) {
if ($fieldTypes{$fn} eq lc($fieldTypes{$fn})) {
if (!($query->param($fn))) {
$ok = 0;
$offender = $fn;
last KEYS;
}
}
}
if ($ok) {
my $add1 = lc(substr($query->param($fieldNamePrimary),0,1));
$add1 = '!' if (!($add1 =~ m/^[a-z]/));
my $errmsg = update_record($add1, $query);
if (!($errmsg)) {
print "The following record added to the dictionary under ",uc($add1),":\n";
print "\n";
print_record('full',$query,-1);
print "
\n";
print "