File Coverage

blib/lib/DBIx/HTMLinterface.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


"; ";
line stmt bran cond sub pod time code
1             ##############################################################################
2             # DBI web interface Version 0.10 #
3             # Copyright 1999 James Furness furn@base6.com #
4             # Created 01/05/99 Last Modified 01/05/99 #
5             ##############################################################################
6             # COPYRIGHT NOTICE #
7             # Copyright 1999 James Furness . All Rights Reserved. #
8             # #
9             # This module is free software; it may be used freely and redistributed #
10             # for free providing this copyright header remains part of the module. You #
11             # may not charge for the redistribution of this module. Selling this code #
12             # without James Furness' written permission is expressly forbidden. #
13             # #
14             # This module may not be modified without first notifying James Furness #
15             # (This is to enable me to track modifications). In all #
16             # cases this copyright header should remain fully intact in all #
17             # modifications. #
18             # #
19             # This code is provided on an "As Is" basis, without warranty, expressed or #
20             # implied. The author disclaims all warranties with regard to this software, #
21             # including all implied warranties of merchantability and fitness, in no #
22             # event shall the author, James Furness be liable for any special, indirect #
23             # or consequential damages or any damages whatsoever including but not #
24             # limited to loss of use, data or profits. By using this module you agree to #
25             # indemnify James Furness from any liability that might arise from it's use. #
26             # Should this code prove defective, you assume the cost of any and all #
27             # necessary repairs, servicing, correction and any other costs arising #
28             # directly or indrectly from it's use. #
29             # #
30             # This copyright notice must remain fully intact at all times. #
31             # Use of this program or its output constitutes acceptance of these terms. #
32             # #
33             # Parts of this module are based upon mysql-lib.pl by Ron Crisco. #
34             ##############################################################################
35              
36             package DBIx::HTMLinterface;
37              
38             =pod
39              
40             =head1 NAME
41              
42             DBIx::HTMLinterface - Class for creating a CGI interface to a database
43              
44             =head1 SYNOPSIS
45              
46             use CGI;
47             use DBI;
48             use DBIx::HTMLinterface;
49              
50             $cgi = new CGI;
51             $dbh = DBI->connect("DBI:mysql:[DATABASE]:[HOSTNAME]","[USERNAME]","[PASSWORD]") );
52             $DBinterface = new DBIx::HTMLinterface ($cgi, $dbh, "[INFOTABLE NAME]");
53              
54             # Below here is only executed if a HTMLinterface action was not taken, so print a menu
55              
56             print $cgi->header;
57             print "Add\n
";
58             print "Search\n";
59              
60             =head1 DESCRIPTION
61              
62             The C class allows a CGI interface to a database.
63             It enables a CGI interface to a database to be created, supporting record addition,
64             modification, deletion and searching. It provides a user friendly interface with
65             descriptions of fields provided. The field descriptions along with information on
66             whether the field is visible, hidden or excluded are extracted from a table, allowing
67             easy modification and addition of fields and tables without having to edit code.
68              
69             =head2 Features
70              
71             =over 4
72              
73             =item Simple database administration
74              
75             Forms are created automatically on demand, SQL statements are generated as needed and processed.
76             The module contains enough autonomy to potentially run with only wrapper perl code placed around
77             it.
78              
79             =item Full form configuration
80              
81             Forms can be modified to add descriptions and extra information to fields, making it easy to change
82             output without having to edit code.
83              
84             =item Control
85              
86             Extensive callback procedures and configuration options allow output, password protection and logging
87             to be configured as desired.
88              
89             =item Full HTML customisation
90              
91             HTML output and table formats can be customised easily by the user.
92              
93             =back
94              
95             =cut
96              
97             $| = 1; # Flush all buffers
98             require 5.004; # Require at least perl 5.004
99              
100 1     1   2088 use strict;
  1         2  
  1         35  
101 1     1   5 use vars qw($VERSION);
  1         2  
  1         46  
102 1     1   2094 use CGI;
  1         18852  
  1         5  
103 1     1   9681 use DBI;
  0            
  0            
104              
105             $VERSION = '0.11';
106              
107             # ------------------------------------------------------------------------
108             # Class constructors/destructors
109             # ------------------------------------------------------------------------
110             =pod
111              
112             =head1 METHODS
113              
114             =head2 Main Methods
115              
116             =over 4
117              
118             =item B<$DBinterface = new DBIx::HTMLinterface (>I I I I<[Suppress paramcheck]>B<);>
119              
120             Constructs a new C object. You must pass a reference
121             to a CGI object which will be used to get the script's parameters and a
122             database handle (Returned from a Cconnect> function) which will
123             be used to communicate with the database. The third parameter defines the
124             name of the I which is used to determine hidden/excluded fields,
125             field names and descriptions as described below in B. After
126             initialisation, the CGI object is checked for a 'HTMLinterface-action' parameter.
127             If this is present, control is taken from the script and the specified action
128             is performed on the specified table. This parameter is set when an action which
129             requires further processing is in progress.
130              
131             The final parameter, suppress paramcheck, is optional and when set to 1 will
132             cause the script NOT to perform the parameter check. You MUST then call the
133             check_params function in your code or forms will not work. Overriding
134             the script in this way is not recommended unless necessary for error handler
135             or security check handler setting.
136              
137             =cut
138             sub new
139             {
140             my $proto = shift;
141             my $class = ref($proto) || $proto;
142             my $cgipkg = shift;
143             my $dbihdl = shift;
144             my $infotbl = shift;
145             my $suppresscheck = shift || 0;
146              
147             my $self = bless {}, $class;
148              
149             $self->{CGI} = $cgipkg; # CGI package
150             $self->{DBH} = $dbihdl; # DBI database handle
151             $self->{ITABLE} = $infotbl; # Info Table name
152             $self->{ERRHDL} = undef; # Error handler
153             $self->{LOGFILE} = undef; # Log file
154             $self->{LOGCALLBACK} = undef; # Logging callback function
155             $self->{PRINTHEADER} = undef; # HTML header
156             $self->{PRINTFOOTER} = undef; # HTML footer
157             $self->{USEGMTTIME} = 1; # Use GMT time or local time
158             $self->{TIMEMOD} = 0; # Add or subtract time
159             $self->{ACCESSCALLBACK} = undef; # Security check callback
160              
161             if ($suppresscheck != 1) {
162             $self->check_params;
163             }
164              
165             return $self;
166             }
167              
168             sub DESTROY { }
169              
170             ##########################################################################
171             # ------------------------------------------------------------------------
172             # User-called functions
173             # ------------------------------------------------------------------------
174              
175             =pod
176              
177             =back
178              
179             =head2 Optional Methods
180              
181             Optional methods which can be called to directly jump to a script function,
182             for example to directly initiate a delete or modify on a record.
183              
184             =over 4
185              
186             =cut
187              
188             =item B B<();>
189              
190             # Check form parameters
191             $DBinterface->check_params;
192              
193             Causes the HTMLinterface-action parameter to be rechecked. If it contains
194             the value 'add','modify','delete' or 'search', the respective function
195             will be called ('exec_add','exec_modify','exec_delete' or 'exec_search').
196             this function is essential to the correct functioning of the interfaces
197             with two and three part forms, and is called automatically when a
198             HTMLinterface object is created, unless the 'suppress paramcheck' parameter
199             is set to 1.
200              
201             =cut
202              
203             sub check_params {
204             my $self = shift;
205              
206             if ($self->{CGI}->param("HTMLinterface-action") eq "add") {
207             $self->exec_add;
208             exit;
209             } elsif ($self->{CGI}->param("HTMLinterface-action") eq "modify") {
210             $self->exec_modify;
211             exit;
212             } elsif ($self->{CGI}->param("HTMLinterface-action") eq "delete") {
213             $self->exec_delete;
214             exit;
215             } elsif ($self->{CGI}->param("HTMLinterface-action") eq "search") {
216             $self->exec_search;
217             exit;
218             }
219             }
220              
221             =item B B<();>
222              
223             # Now set the 'HTMLinterface-table' parameter so the script knows
224             # what table to deal with
225             $cgi->param(-name=>'HTMLinterface-table',-value=>'mytable');
226              
227             # Now call the function
228             $DBinterface->exec_search;
229              
230             Searches the table named in the CGI parameter 'HTMLinterface-table'.
231             The user will be presented with a blank form with the fields of the table.
232             They press submit to search the table (Wildcards can be used). They are then
233             returned a table with a modify and delete button and the fields for each
234             record found.
235              
236             =cut
237              
238             sub exec_search {
239             my $self = shift;
240             my $table = $self->{CGI}->param("HTMLinterface-table");
241             #TODO: Lousy hack?
242             my ($tablename, $name, $label, $lookup, $extrahash, $hidden, $exclude,
243             $additionalwhere) = _getTableInfoHash($self, $table);
244              
245             # Check access privs
246             $self->_checkAccess;
247              
248             if ($self->{CGI}->param('post')) {
249             my ($i, $j, %types, %params, $pri, $cursor, $sql, @row, $val, $numcols,
250             @fielddesc, @fieldtypes, @primary_keys, $content);
251            
252             $self->_printHeader("Search Results", "");
253            
254             $numcols = 0;
255            
256             # Now look up primary key fields and field types...
257             my ($desc_cursor) = $self->_execSql ("describe $table");
258             while (@fielddesc = $desc_cursor->fetchrow) {
259             $numcols++;
260            
261             # Stuff the paramaters into a hash before we delete them
262             $params{$fielddesc[0]} = $self->{CGI}->param($fielddesc[0]);
263             $types{$fielddesc[0]} = $fielddesc[1];
264             if ($fielddesc[3] eq "PRI") {
265             push @primary_keys, $fielddesc[0];
266             }
267             }
268             $desc_cursor->finish;
269             $numcols += 2; # Add Modify and Delete cols
270            
271             # now we execute the SQL, and return a list of matches
272             $cursor = $self->_execSql($self->_selectSql($table, $additionalwhere));
273            
274             # delete the current params so they don't get incorporated in the forms
275             $self->{CGI}->delete_all;
276             $self->_startTable($numcols, "Search Results");
277            
278             # now print header row
279             $self->_printStartTableRow();
280             $self->_printTableHeaderCell("Modify");
281             $self->_printTableHeaderCell("Delete");
282             for ($i=0; $i < $cursor->{NUM_OF_FIELDS}; $i++) {
283             $self->_printTableHeaderCell("$cursor->{NAME}->[$i]");
284             }
285             $self->_printEndTableRow();
286            
287             while (@row = $cursor->fetchrow_array) {
288             $self->_printStartTableRow();
289            
290             # now print the Modify Form
291             print $self->{CGI}->startform;
292             $content = "";
293             # Print the primary keys
294             for ($i=0; $i < $cursor->{NUM_OF_FIELDS}; $i++) {
295             foreach $pri (@primary_keys) {
296             if ($pri eq $cursor->{NAME}->[$i]) {
297             print "{NAME}->[$i]\" VALUE=\"$row[$i]\">";
298             }
299             }
300             }
301             # Print state tracking elements
302             print $self->{CGI}->hidden(-name => 'HTMLinterface-action', value => 'modify');
303             print $self->{CGI}->hidden(-name => 'HTMLinterface-table', value => $table);
304             $self->_printHidden; # Print any hidden elements necessary
305             $self->_printTableCell ($self->{CGI}->submit('Modify'));
306             print $self->{CGI}->endform;
307              
308             # now print the Delete Form
309             print $self->{CGI}->startform;
310             $content = "";
311             # Print the primary keys
312             for ($i=0; $i < $cursor->{NUM_OF_FIELDS}; $i++) {
313             foreach $pri (@primary_keys) {
314             if ($pri eq $cursor->{NAME}->[$i]) {
315             print "{NAME}->[$i]\" VALUE=\"$row[$i]\">";
316             }
317             }
318             }
319             # Print state tracking elements
320             print $self->{CGI}->hidden(-name => 'HTMLinterface-action', value => 'delete');
321             print $self->{CGI}->hidden(-name => 'HTMLinterface-table', value => $table);
322             $self->_printHidden; # Print any hidden elements necessary
323             $self->_printTableCell ($self->{CGI}->submit('Delete'));
324             print $self->{CGI}->endform;
325              
326             # now print the fields
327             for ($i=0; $i < $cursor->{NUM_OF_FIELDS}; $i++) {
328             my $pos = 0;
329             $val = $row[$i];
330             $val =~ s/&/&/g;
331             $val =~ s/
332             $val =~ s/>/>/g;
333            
334             # Don't print the whole of the text fields
335             if ($types{$cursor->{NAME}->[$i]} =~ "text") {
336             my ($search) = "";
337            
338             if ($search = $params{$cursor->{NAME}->[$i]}) {
339             $search =~ s/&/&/g;
340             $search =~ s/
341             $search =~ s/>/>/g;
342            
343             # Make wildcards work in highlight
344             $search =~ s/_/(.)/g;
345             $search =~ s/%/(.*)/g;
346            
347             # This chunk borrowed from plan_search.pl by Richard Smith :p
348            
349             # Find our search string in the field
350             $pos = index(lc($val), lc($search));
351            
352             # Grab the string for 100 characters before it
353             $pos = $pos - 100;
354             if ($pos < 0) {
355             $pos = 0;
356             }
357             }
358             my ($subtext) = substr($val, $pos, 300);
359            
360             # Change the search string to bold in the part of the string we're showing
361             if ($search ne "") { $subtext =~ s/($search)/$1<\/b>/gi; }
362            
363             if (length($val) > 300) { # Show truncation marks if too long
364             if ($pos < 1) {
365             $val = $subtext . "...";
366             } else {
367             $val = "..." . $subtext . "...";
368             }
369             } else {
370             $val = $subtext;
371             }
372             }
373             $self->_printTableCell ("$val  ");
374             }
375             $self->_printEndTableRow();
376             }
377             $self->_endTable();
378             $self->_printFooter;
379             exit;
380             } else {
381             # give them the form
382             $self->_form($table,"search","Search $tablename","Search $tablename\n
  • Use the % character to match any number of characters (Even none).\n
  • Use the _ character to match any one character.\n
  • A % is automatically appended to all strings.\n
  • You can enter just a normal wildcard character with no special meaning by typing a \\ before it, i.e \\% or \\_.
  • Leave this form blank to show EVERYTHING.
" . $self->{CGI}->submit('Submit') . " " . $self->{CGI}->reset('Reset') . "\n","nodefaults","");
383             exit;
384             }
385             }
386              
387             =item B B<();>
388              
389             # Assume $cgi->param has been set to indicate the primary keys
390             # for the table being modified, i.e 'Primary Key Name' = 'Primary
391             # Key Value'
392              
393             # Now set the 'HTMLinterface-table' parameter so the script knows
394             # what table to deal with
395             $cgi->param(-name=>'HTMLinterface-table',-value=>'mytable');
396              
397             # Now call the function
398             $DBinterface->exec_modify;
399              
400             Modifies a record from the table named in the CGI parameter 'HTMLinterface-table'
401             where the CGI parameters which have the same name as a table column. For example
402             for a table called 'data' with an 'ID' column containing the primary keys for
403             that table, set the 'HTMLinterface-table' parameter to 'data' and set the 'ID'
404             parameter to the ID number of the record you want to modify. The user will then
405             be presented with a form containing the data in the table for them to modify.
406             They then press submit to commit the data
407              
408             =cut
409             sub exec_modify {
410             my $self = shift;
411            
412             # Check access privs
413             $self->_checkAccess;
414              
415             # Execute the modify if the user already has the form else give the user the form
416             if ($self->{CGI}->param('post')) {
417             $self->_modifyRecord($self->{CGI}->param("HTMLinterface-table"));
418             } else {
419             $self->_form($self->{CGI}->param("HTMLinterface-table"),"modify","Modify Record","Modify Record","","fill_from_table");
420             }
421             }
422              
423             =item B B<();>
424              
425             # Now set the 'HTMLinterface-table' parameter so the script knows
426             # what table to deal with
427             $cgi->param(-name=>'HTMLinterface-table',-value=>'mytable');
428              
429             # Now call the function
430             $DBinterface->exec_add;
431              
432             Adds a record to the table named in the CGI parameter 'HTMLinterface-table'.
433             The user will be presented with a empty form containing just the defaults for
434             the values of that table (Defined in the SQL). They then press submit to commit
435             the data to the table.
436              
437             =cut
438             sub exec_add {
439             my $self = shift;
440              
441             # Check access privs
442             $self->_checkAccess;
443              
444             if ($self->{CGI}->param('post')) {
445             $self->_insertRecord($self->{CGI}->param("HTMLinterface-table"));
446             } else {
447             $self->_form($self->{CGI}->param("HTMLinterface-table"),"add","Add Record","Add Record","","");
448             }
449             }
450              
451             =item B B<();>
452              
453             # Assume $cgi->param has been set to indicate the primary keys
454             # for the table being modified, i.e 'Primary Key Name' = 'Primary
455             # Key Value'
456              
457             # Now set the 'HTMLinterface-table' parameter so the script knows
458             # what table to deal with
459             $cgi->param(-name=>'HTMLinterface-table',-value=>'mytable');
460              
461             # Now call the function
462             $DBinterface->exec_delete;
463              
464             Deletes a record from the table named in the CGI parameter 'HTMLinterface-table'
465             where the CGI parameters which have the same name as a table column. For example
466             for a table called 'data' with an 'ID' column containing the primary keys for
467             that table, set the 'HTMLinterface-table' parameter to 'data' and set the 'ID'
468             parameter to the ID number of the record you want to delete.
469              
470             This function will output a confirmation page requiring users to confirm the delete
471             or press their browser's back button to cancel. To skip confirmation, set the 'confirm'
472             parameter to 'Y'.
473              
474             =cut
475             sub exec_delete {
476             my $self = shift;
477              
478             # Check access privs
479             $self->_checkAccess;
480              
481             # Delete the record
482             $self->_deleteRecord($self->{CGI}->param("HTMLinterface-table"));
483             }
484              
485             # ------------------------------------------------------------------------
486             # General support functions
487             # ------------------------------------------------------------------------
488             =pod
489              
490             =back
491              
492             =head2 Optional Customisation Methods
493              
494             Optional methods which can be called to alter the behaviour of the script
495             or enable features such as logging.
496              
497             =over 4
498              
499             =cut
500              
501             =item B B<(>IB<);>
502              
503             sub log_callback {
504             my $description = shift;
505             my $sql = shift;
506              
507             open (LOG,">>$logfile")
508             print LOG "$description (Executing $sql)";
509             close(LOG);
510             }
511             $DBinterface = new DBIx::HTMLinterface ($cgi, $dbh, $table, 1);
512             $DBinterface->set_logcallback(\&log_callback);
513             $DBinterface->check_params();
514              
515             Enables logging of SQL changes to the database via the user
516             defined routine. The first parameter passed is a description,
517             such as 'Record added to mytable' and the second parameter is
518             the SQL statement which was used.
519              
520             NOTE: check_params() MUST be called or HTMLinterface will not function correctly.
521              
522             =cut
523             sub set_logcallback {
524             # $self &callback;
525             $_[0]->{LOGCALLBACK} = $_[1];
526             }
527              
528             =item B B<(>IB<);>
529             $DBinterface = new DBIx::HTMLinterface ($cgi, $dbh, $table, 1);
530             $DBinterface->set_logfile("/usr/local/logs/mydb-log");
531             $DBinterface->check_params();
532              
533             Enables logging of SQL changes to the database automatically
534             without providing a callback. The script will open the file
535             specified, with no locking (Althoughthis might be added in
536             future). The file must be writeable to the CGI, on UNIX you
537             normally need to I. However this may
538             differ depending on your system and what operating system
539             you have.
540              
541             NOTE: check_params() MUST be called or HTMLinterface will not function correctly.
542              
543             =cut
544             sub set_logfile {
545             # $self $logfile;
546             $_[0]->{LOGFILE} = $_[1];
547             }
548              
549             # Internal function to log output if logging is enabled
550             sub _logEvent {
551             my $self = shift;
552             my $cmd = shift;
553             my $sql = shift;
554             my $logfile = undef;
555              
556             # If we have a callback, use it
557             if (defined $self->{LOGCALLBACK}) {
558             &{$self->{LOGCALLBACK}} ($cmd, $sql);
559             return;
560              
561             # Else output to a logfile ourselves
562             } elsif (defined $self->{LOGFILE}) {
563             $logfile = $self->{LOGFILE};
564              
565             # Else forget logging
566             } else {
567             return;
568              
569             }
570              
571             # Get and format the time
572             my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
573             if ($sec < 10) { $sec = "0$sec"; }
574             if ($min < 10) { $min = "0$min"; }
575             if ($hour < 10) { $hour = "0$hour"; }
576             if ($mon < 10) { $mon = "0$mon"; }
577             if ($mday < 10) { $mday = "0$mday"; }
578             my (@months) = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
579             my ($cur_date) = "[" . $mday . "/" . $months[$mon] . "/" . $year . ":" . $hour . ":" . $min . ":" . $sec . " +0000]";
580              
581             # Open the logfile for append
582             if (! open(LOG,">>$logfile")) {
583             # Send warnings to the browser and STDERR on failure
584             warn ("Unable to open logfile $logfile for append ($!)");
585             print "WARNING: Unable to open logfile $logfile for append ($!)";
586             return;
587             }
588              
589             # Print to the logfile
590             print LOG "$cur_date $cmd" . ($sql ne "" ? " SQL: '$sql'" : "") . "\n";
591              
592             # Close the logfile
593             close (LOG);
594             }
595              
596             =item B B<(>IB<);>
597              
598             sub errorhandler {
599             my $errstr = shift;
600              
601             print "

Fatal Error

";
602             print $errstr;
603              
604             exit;
605             }
606             $DBinterface = new DBIx::HTMLinterface ($cgi, $dbh, $table, 1);
607             $DBinterface->set_errorhandler(\&errorhandler);
608             $DBinterface->check_params();
609              
610             Transfers error handling in the script from the I procedure
611             to the subroutine passed as the argument. The errorhandling routine
612             should not return, and should terminate the program after the error
613             has been output.
614              
615             NOTE: check_params() MUST be called or HTMLinterface will not function correctly.
616              
617             =cut
618             sub set_errhandler {
619             # $self &errorhandler;
620             $_[0]->{ERRHDL} = $_[1];
621             }
622              
623             =item B
624              
625             sub checkaccess {
626             if ($cgi->param("password") eq "letmein") { # Example security check
627             return; # Valid password - return to allow function to continue
628             } else {
629             die ("Incorrect password"); # Incorrect - die to stop execution
630             }
631             }
632             $DBinterface = new DBIx::HTMLinterface ($cgi, $dbh, $table, 1);
633             $DBinterface->set_accesscallback(\&errorhandler);
634             $DBinterface->check_params();
635              
636             Enables a security check function to approve or deny access. The function is
637             called before changes to the database are made. The function should return to
638             allow an action to complete or die to terminate the program and prevent access.
639              
640             NOTE: check_params() MUST be called or HTMLinterface will not function correctly.
641              
642             =cut
643             sub set_accesscallback {
644             my $self = shift;
645             my $callback = shift;
646              
647             $self->{ACCESSCALLBACK} = $callback;
648             }
649              
650             # Internal function to call the user defined security check
651             sub _checkAccess {
652             my $self = shift;
653             my $callback = $self->{ACCESSCALLBACK};
654              
655             if (ref($callback) eq 'CODE') {
656             &$callback(); # nicer to perl 5.003 users
657             }
658             }
659              
660             # Internal function to output errors and exit the program
661             sub _die {
662             my $self = shift;
663             my $errstr = shift;
664              
665             if (defined $self->{ERRHDL}) {
666             &{$self->{ERRHDL}} ($errstr);
667             }
668              
669             # Call die whether or not the user defined error handler has been called
670             # - the error is fatal and we should not get here if the user defined
671             # handler operates correctly anyway.
672             die $errstr;
673             }
674              
675             =item B B<(>IB<,> I
676              
677             $DBinterface->set_timezone(1, 0); # Set time to GMT +0000
678             $DBinterface->set_timezone(0, -5); # Set time to server time -0500
679             $DBinterface->set_timezone(1, -8); # Set time to GMT -0800
680             $DBinterface->set_timezone(0, 2); # Set time to server time +0200
681              
682             Changes the time zone used for timestamps inserted into database records. The
683             first parameter specifies whether to use GMT time or to use the server time,
684             i.e the computer running this script's internal clock. The second parameter
685             allows time to be added or subtracted in hours.
686              
687             =cut
688             sub set_timezone {
689             # $self $usegmttime;
690             $_[0]->{USEGMTTIME} = $_[1];
691             # $self $timemod;
692             $_[0]->{TIMEMOD} = $_[2];
693             }
694              
695             # ------------------------------------------------------------------------
696             # HTML formatting functions
697             # ------------------------------------------------------------------------
698             =pod
699              
700             =back
701              
702             =head2 Optional HTML Customisation Methods
703              
704             =over 4
705              
706             =item Future Additions
707              
708             In a later version, callbacks to print table cells, start and finish tables,
709             print form fields, print back links and add user defined form parameters to
710             allow state keeping such as password protection etc.
711              
712             =item B
713              
714             sub printheader {
715             my $title = shift;
716             my $headtext = shift;
717              
718             print $cgi->start_html(-title=>"$title_txt");
719              
720             if ($headtext ne "") {
721             print $cgi->h3($headtext);
722             }
723             }
724             $DBinterface->set_printheader(\&printheader);
725              
726             Transfers the header HTML outputting function to a user defined function
727             to allow HTML customisation.
728              
729             =cut
730             sub set_printheader {
731             # $self &printheader;
732             $_[0]->{PRINTHEADER} = $_[1];
733             }
734              
735             # Internal function to start the output in the user's desired style
736             sub _printHeader {
737             my $self = shift;
738             my $title = shift;
739             my $headtext = shift;
740             my ($package, $filename, $line) = caller();
741              
742             print $self->{CGI}->header;
743              
744             if (defined $self->{PRINTHEADER}) {
745             &{$self->{PRINTHEADER}} ($title, $headtext);
746             } else {
747             print $self->{CGI}->start_html(-title=>"$title",
748             -bgcolor=>"#FFFFFF",
749             -text=>"#000077"
750             );
751             if ($headtext ne "") {
752             print $self->{CGI}->h3($headtext);
753             }
754             }
755              
756             print "\n\n\n";
757             }
758              
759             =item B
760              
761             sub printfooter {
762             print $cgi->end_html;
763             }
764             $DBinterface->set_printfooter(\&printfooter);
765              
766             Transfers the footer HTML outputting function to a user defined function
767             to allow HTML customisation.
768              
769             =cut
770             sub set_printfooter {
771             # $self &printfooter;
772             $_[0]->{PRINTFOOTER} = $_[1];
773             }
774              
775             # Internal function to end the output in the user's desired style
776             sub _printFooter {
777             my $self = shift;
778              
779             if (defined $self->{PRINTFOOTER}) {
780             &{$self->{PRINTFOOTER}};
781             } else {
782             print "";
783             }
784             }
785              
786             # Internal function to create a table in the user's desired style
787             sub _startTable
788             {
789             my $self = shift;
790             my $colwidth = shift;
791             my $title = shift;
792              
793             print "
";
794             print "\n"; \n\n"; "; "; "; "; \n"; ";
795             print "

" .

796             "$title
797             }
798              
799             # Internal function to print the start of a table row
800             sub _printStartTableRow
801             {
802             my $self = shift;
803              
804             print "
805             }
806              
807             # Internal function to print a table cell
808             sub _printTableCell
809             {
810             my $self = shift;
811             my $content = shift;
812              
813             print "";
814             print $content;
815             print "
816             }
817              
818             # Internal function to print a table header cell
819             sub _printTableHeaderCell
820             {
821             my $self = shift;
822             my $content = shift;
823              
824             print "";
825             print $content;
826             print "
827             }
828              
829             # Internal function to print the end of a table row
830             sub _printEndTableRow
831             {
832             my $self = shift;
833              
834             print "
835             }
836              
837             # Internal function to print add/modify table rows in the user's desired style
838             sub _printEditTableRow
839             {
840             my $self = shift;
841             my $name = shift;
842             my $form = shift;
843             my $label = shift;
844              
845             print "
";
846             print $name;
847             print "
848             print "";
849             print $form;
850             print "
";
851             print $label;
852             print "
853             }
854              
855             # Internal function to end a table in the user's desired style
856             sub _endTable
857             {
858             my $self = shift;
859              
860             print "
";
861             print "
";
862             }
863              
864             # internal function to print extra form parameters into a query string
865             sub _printHiddenQstring {
866             my $self = shift;
867             my $isFirst = shift;
868             }
869              
870             # internal function to print extra form parameters as form elements
871             sub _printHidden {
872             my $self = shift;
873             }
874              
875             # internal function to print a link to repeat the last action
876             sub _repeatLink {
877             my $self = shift;
878            
879             return $self->{CGI}->url . "?HTMLinterface-action=" . $self->{CGI}->param("HTMLinterface-action")
880             . "&HTMLinterface-table=" . $self->{CGI}->param("HTMLinterface-table")
881             . $self->_printHiddenQstring (0);
882             }
883              
884             # internal function to print a back link
885             sub _backLink {
886             my $self = shift;
887            
888             return $self->{CGI}->url . $self->_printHiddenQstring (1);
889             }
890              
891             # Internal function to generate forms
892             sub _form {
893             my $self = shift;
894             my $table = shift;
895             my $action = shift;
896             my $page_title = shift;
897             my $page_heading = shift;
898             my $nodefaults = shift;
899             my $fill_from_table = shift;
900            
901             $self->_printHeader($page_title, "");
902             print $self->{CGI}->startform;
903             $self->_startTable (2, $page_heading);
904            
905             # Output the actual form...
906             $self->_createForm($table,$nodefaults,$fill_from_table);
907            
908             # Mode and action variables
909             print $self->{CGI}->hidden(-name => 'post', -value => 'true');
910             print $self->{CGI}->hidden(-name => 'HTMLinterface-action', -value => $action);
911             print $self->{CGI}->hidden(-name => 'HTMLinterface-table', -value => $table);
912             $self->_printHidden; # Print any hidden elements necessary
913            
914             print "\n\n

915             print "
";
916             print $self->{CGI}->submit('Submit');
917             print "    ";
918             print $self->{CGI}->reset('Reset');
919             print "
920             print $self->{CGI}->endform;
921             $self->_endTable;
922             $self->_printFooter;
923             }
924              
925             # Internal function to generate the actual form content
926             sub _createForm {
927             my $self = shift;
928             my $table = shift;
929             my $nodefaults = shift;
930             my $fill_from_table = shift;
931              
932             my (@fielddesc, @fields, @fieldtypes, @fielddefaults, @primary_keys, $fill_cursor, $field);
933             my ($tablename, $names, $label, $lookup, $extrahash, $hidden, $exclude,
934             #TODO: Lousy hack?
935             $additionalwhere) = _getTableInfoHash($self,$table);
936            
937             # Get table column info
938             my ($desc_cursor) = $self->_execSql ("describe $table");
939             while (@fielddesc = $desc_cursor->fetchrow) {
940             push @fields, $fielddesc[0];
941             push @fieldtypes, $fielddesc[1];
942             push @fielddefaults, $fielddesc[4];
943             if ($fielddesc[3] eq "PRI") {
944             push @primary_keys, $fielddesc[0];
945             }
946             }
947             $desc_cursor->finish;
948            
949             # Get primary keys and print them out to allow primary key changes without losing what record
950             # we're editing
951             while ($field = shift @primary_keys) {
952             my $name = "primary_key_" . $field;
953             my $val = $self->{CGI}->param("$field");
954             $val =~ s/\\/\\\\/g;
955             $val =~ s/'/\\'/g;
956             print $self->{CGI}->hidden(-name => $name, value => $val);
957             }
958            
959             # Get table values if we're filling from an existing table
960             my ($field_values);
961             if ($fill_from_table ne "") {
962             $fill_cursor = $self->_execSql ($self->_selectSql($table));
963              
964             if (! ($field_values = $fill_cursor->fetchrow_hashref)) {
965             $self->_die("Database error $DBI::errstr while loading form values");
966             }
967             }
968            
969             fieldloop: while ($field = shift @fields) {
970             my ($default) = shift @fielddefaults;
971             my ($type) = shift @fieldtypes;
972             my ($val, $max, $size);
973             my ($itemname, $itemform, $itemlabel);
974            
975             my $item;
976             foreach $item (@$exclude) {
977             if ($field eq $item) {
978             next fieldloop;
979             }
980             }
981            
982             if ($default eq "NULL" || $nodefaults ne "") {
983             $default = "";
984             }
985            
986             $val = $self->{CGI}->param("$field") || $default;
987             ($max) = $type =~ /\((.*)\)/;
988             $size = $max < 50 ? $max : 50;
989            
990             if ((substr($type, 0, 10) eq 'timestamp(') && ($val eq "") && ($nodefaults eq "")) {
991             $val = $self->_currentTime;
992             }
993            
994             # Fill values from table instead of form where asked.
995             if ($fill_from_table ne "") {
996             $val = $field_values->{$field};
997             }
998            
999             # Process hidden fields
1000             foreach $item (@$hidden) {
1001             if ($field eq $item) {
1002             print $self->{CGI}->hidden(-name=>$field,value=>$val);
1003             next fieldloop;
1004             }
1005             }
1006            
1007             if ($$names{$field} eq "") {
1008             $itemname = "$field:";
1009             } else {
1010             $itemname = $$names{$field};
1011             }
1012            
1013             if ( $$lookup{$field} ne "" ) {
1014             # make a select list based on the SQL the caller sent us
1015             if ($nodefaults ne "") {
1016             #TODO: Lousy hack?
1017             $itemform = &_createSelectList($self,$field,$$lookup{$field},"","allowblank");
1018             } else {
1019             #TODO: Lousy hack?
1020             $itemform = &_createSelectList($self,$field,$$lookup{$field},$val);
1021             }
1022            
1023             } elsif ($type =~ "mediumtext") {
1024             $itemform = $self->{CGI}->textarea(-'name'=>$field,
1025             'default'=>$val,
1026             'rows'=>10,
1027             'columns'=>70);
1028            
1029             } elsif ($type =~ "text") {
1030             $itemform = $self->{CGI}->textarea(-'name'=>$field,
1031             'default'=>$val,
1032             'rows'=>5,
1033             'columns'=>50);
1034            
1035             } elsif (substr($type, 0, 5) eq 'enum(') {
1036             # TODO: Too mysql specific?
1037             my $args = substr($type, 5, -1);
1038             my @list = split(/,/, $args);
1039             $itemform = "
1040            
1041             if ($nodefaults ne "" && $val eq "") {
1042             $itemform .= "
1043             }
1044            
1045             my $option;
1046             while ($option = shift @list) {
1047             if ($option =~ /^'(.*)'$/) {
1048             $option = $1;
1049             }
1050            
1051             if ($option eq "$val") {
1052             $itemform .= "
1053             } else {
1054             $itemform .= "
1055             }
1056            
1057             $itemform .= "$option\n";
1058             }
1059            
1060             $itemform .= "\n";
1061            
1062             } elsif ($$extrahash{$field} eq "encryptpassword") {
1063             $itemform = $self->{CGI}->password_field(-'name' => $field,
1064             'value' => '',
1065             'size' => $size,
1066             'maxlength' => $max);
1067             } else {
1068             $itemform = $self->{CGI}->textfield(-'name' => $field,
1069             'value' => $val,
1070             'size' => $size,
1071             'maxlength' => $max);
1072             }
1073            
1074             if ( $$label{$field} ne "" ) {
1075             $itemlabel = $$label{$field};
1076             } else {
1077             $itemlabel = "";
1078             }
1079              
1080             # Now print the HTML
1081             $self->_printEditTableRow ($itemname, $itemform, $itemlabel);
1082             }
1083            
1084             if ($fill_from_table ne "") {
1085             $fill_cursor->finish;
1086             }
1087             }
1088              
1089             # Internal function to generate select lists based on SQL statements
1090             sub _createSelectList {
1091             my $self = shift;
1092             my $field = shift;
1093             my $sql = shift;
1094             my $default = shift;
1095             my $allowblank = shift;
1096              
1097             my (@row);
1098              
1099             my ($cursor) = $self->_execSql ("$sql");
1100              
1101             my ($rettext) = "";
1102              
1103             $rettext .= "
1104             if ($allowblank ne "") {
1105             $rettext .= "
1106             }
1107              
1108             while (@row = $cursor->fetchrow) {
1109              
1110             if ($row[0] eq "$default") {
1111             # if their query returns 2 columns, use the first as the value
1112             if ($row[1] ne "") {
1113             $rettext .= "
1114             } else {
1115             $rettext .= "
1116             }
1117             } else {
1118             # if their query returns 2 columns, use the first as the value
1119             if ($row[1] ne "") {
1120             $rettext .= "
1121             } else {
1122             $rettext .= "
1123             }
1124             }
1125              
1126             # if their query returns 2 columns, use the second as the label
1127             if ($row[1] ne "") {
1128             $rettext .= "$row[1]\n";
1129             } else {
1130             $rettext .= "$row[0]\n";
1131             }
1132              
1133             }
1134              
1135             $rettext .= "\n";
1136              
1137             return $rettext;
1138             }
1139              
1140             sub _currentTime {
1141             my $self = shift;
1142             my $timemod = $self->{TIMEMOD} != 0 ? $self->{TIMEMOD} * 60 * 60 : 0;
1143             my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);
1144              
1145             if ($self->{USEGMTTIME} == 0) {
1146             ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time + $timemod);
1147             } else {
1148             ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time + $timemod);
1149             }
1150              
1151             $sec = $sec < 10 ? "0$sec" : $sec;
1152             $min = $min < 10 ? "0$min" : $min;
1153             $hour = $hour < 10 ? "0$hour" : $hour;
1154             $mon++;
1155             $mon = $mon < 10 ? "0$mon" : $mon;
1156             $mday = $mday < 10 ? "0$mday" : $mday;
1157             $year = $year + 1900;
1158              
1159             return("$year$mon$mday$hour$min$sec");
1160             }
1161              
1162             # ------------------------------------------------------------------------
1163             # SQL formatting and output functions
1164             # ------------------------------------------------------------------------
1165              
1166             # Internal function to execute SQL commands
1167             sub _execSql {
1168             my $self = shift;
1169             my $cmd = shift;
1170             my $sthdl;
1171              
1172             $sthdl = $self->{DBH}->prepare($cmd) || $self->_die("Database error preparing $cmd: " . $sthdl->errstr);
1173             $sthdl->execute || $self->_die("Database error executing $cmd: " . $sthdl->errstr);
1174              
1175             return $sthdl;
1176             }
1177              
1178             # Internal function to quote and escape SQL entries
1179             sub _sqlQuote {
1180             my $self = shift;
1181             my $str = shift;
1182              
1183             # Use DBI's quote method which ensures correct quoting for whatever database system is being used
1184             return $self->{DBH}->quote($str);
1185             }
1186              
1187             # Internal function to return a hash of all data from the info table
1188             sub _getTableInfoHash {
1189             # my ($package, $filename, $line) = caller();
1190             # print $line;
1191             my $self = shift;
1192             my $table = shift;
1193             my $itable = $self->{ITABLE};
1194              
1195             my $sql = "select TableName, NameHash, LabelHash, LookupHash, ExtraHash, Hidden, Exclude, AdditionalWhere from $itable where TableID = '$table'";
1196              
1197             my $cursor = $self->_execSql ($sql);
1198            
1199             my ($entry, $table_name, %namehash, %labelhash, %lookuphash, %extrahash, @hidden, @exclude, $additionalwhere);
1200              
1201             # Don't die on fail - simply return an empty hash and default everything
1202             # TODO: Protect against undefined tables for this
1203             if ($entry = $cursor->fetchrow_hashref) {
1204             my ($hashInfo, $pair, @pairs);
1205              
1206             # Load table name
1207             $table_name = $entry->{"TableName"};
1208              
1209             # Load name hash
1210             $hashInfo = $entry->{"NameHash"};
1211             @pairs = split(/&/, $hashInfo);
1212             foreach $pair (@pairs) {
1213             my ($name, $value) = split(/=/, $pair);
1214             $namehash{$name} = $value;
1215             }
1216              
1217             # Load label hash
1218             $hashInfo = $entry->{"LabelHash"};
1219             @pairs = split(/&/, $hashInfo);
1220             foreach $pair (@pairs) {
1221             my ($name, $value) = split(/=/, $pair);
1222             $labelhash{$name} = $value;
1223             }
1224              
1225             # Load lookup hash
1226             $hashInfo = $entry->{"LookupHash"};
1227             @pairs = split(/&/, $hashInfo);
1228             foreach $pair (@pairs) {
1229             my ($name, $value) = split(/=/, $pair);
1230             $lookuphash{$name} = $value;
1231             }
1232              
1233             # Load extra hash
1234             $hashInfo = $entry->{"ExtraHash"};
1235             @pairs = split(/&/, $hashInfo);
1236             foreach $pair (@pairs) {
1237             my ($name, $value) = split(/=/, $pair);
1238             $extrahash{$name} = $value;
1239             }
1240              
1241             # Load hidden array
1242             $hashInfo = $entry->{"Hidden"};
1243             @hidden = split(/&/, $hashInfo);
1244              
1245             # Load exclude array
1246             $hashInfo = $entry->{"Exclude"};
1247             @exclude = split(/&/, $hashInfo);
1248              
1249             # Load table name
1250             $additionalwhere = $entry->{"AdditionalWhere"};
1251             }
1252              
1253             $cursor->finish;
1254             return ($table_name, \%namehash, \%labelhash, \%lookuphash, \%extrahash, \@hidden, \@exclude, $additionalwhere);
1255             }
1256              
1257             # Internal function to return a hash of just the extra data from the info table
1258             sub _getTableExtraHash {
1259             my $self = shift;
1260             my $table = shift;
1261              
1262             my $cursor = $self->_execSql ('select ExtraHash from ' . $self->{ITABLE} . ' where TableID = ' . $self->_sqlQuote($table));
1263             my ($entry, %hash);
1264              
1265             # Don't die on fail - simply return an empty hash and default everything
1266             # TODO: Protect against undefined tables for this
1267             if ($entry = $cursor->fetchrow_hashref) {
1268             my $hashInfo = $entry->{"ExtraHash"};
1269              
1270             my @pairs = split(/&/, $hashInfo);
1271             my $pair;
1272              
1273             foreach $pair (@pairs) {
1274             my ($name, $value) = split(/=/, $pair);
1275              
1276             $hash{$name} = $value;
1277             }
1278             }
1279              
1280             $cursor->finish;
1281             return \%hash;
1282             }
1283              
1284             # Internal function to execute a SQL modify
1285             sub _modifyRecord {
1286             my $self = shift;
1287             my $table = shift;
1288              
1289             # Run the SQL
1290             my $sql = $self->_updateSql ($table);
1291             my $cursor = $self->_execSql ($sql);
1292             $cursor->finish;
1293              
1294             # Tell the people what we did
1295             $self->_printHeader('Modification Successful', 'Record modified successfully.');
1296             print "
    ";
1297             print "
  • _backLink . "\">Main Menu";
  • 1298             print "";
    1299             $self->_printFooter;
    1300              
    1301             # Log it, if logging is enabled
    1302             $self->_logEvent("Record modified from $table", $sql);
    1303             }
    1304              
    1305             # Internal function to execute a SQL delete
    1306             sub _deleteRecord {
    1307             my $self = shift;
    1308             my $table = shift;
    1309              
    1310             # Require confirmation of the delete
    1311             if ($self->{CGI}->param('confirm')) {
    1312             # Run the SQL
    1313             my $sql = $self->_deleteSql($table);
    1314             my $cursor = $self->_execSql ($sql);
    1315             $cursor->finish;
    1316              
    1317             # Tell the people what we did
    1318             $self->_printHeader('Deletion Successful', 'Record deleted successfully.');
    1319             print "
      ";
    1320             print "
  • _backLink . "\">Main Menu";
  • 1321             print "";
    1322             $self->_printFooter;
    1323              
    1324             # Log it, if logging is enabled
    1325             $self->_logEvent("Record deleted from $table", $sql);
    1326             } else {
    1327             # Ask them to confirm their action
    1328             $self->_printHeader('Confirm Delete', 'Confirm Delete');
    1329             print $self->{CGI}->b('Press back to cancel. Press Confirm to delete.');
    1330             print $self->{CGI}->startform;
    1331             $self->_printHidden; # Print any hidden elements necessary
    1332              
    1333             # Print all the form params as hidden fields
    1334             my @form = $self->{CGI}->param;
    1335             my $name;
    1336             while ($name = shift @form) {
    1337             print $self->{CGI}->hidden (-name=>$name, -value=>$self->{CGI}->param ($name) );
    1338             }
    1339              
    1340             print $self->{CGI}->hidden(-name=>'confirm',-value =>'true');
    1341             print $self->{CGI}->submit('Confirm');
    1342             print "   ";
    1343             print $self->_backLink;
    1344             print $self->{CGI}->endform;
    1345             $self->_printFooter;
    1346             }
    1347             }
    1348              
    1349             # Internal function to execute a SQL insert
    1350             sub _insertRecord {
    1351             my $self = shift;
    1352             my $table = shift;
    1353              
    1354             # Run the SQL
    1355             my $sql = $self->_insertSql ($table);
    1356             my $cursor = $self->_execSql ($sql);
    1357             $cursor->finish;
    1358              
    1359             # Tell the people what we did
    1360             $self->_printHeader('Addition Successful', 'Record added successfully.');
    1361             print "
      ";
    1362             print "
  • _repeatLink . "\">Add Another";
  • 1363             print "
  • _backLink . "\">Main Menu";
  • 1364             print "";
    1365             $self->_printFooter;
    1366              
    1367             # Log it, if logging is enabled
    1368             $self->_logEvent("Record added to $table", $sql);
    1369             }
    1370              
    1371             # _insertSql - internal function to generate insert statements for $table, inserting all values in
    1372             # $self->{CGI}->param which match the table column names.
    1373             sub _insertSql {
    1374             my $self = shift;
    1375             my $table = shift;
    1376              
    1377             # Use a DESCRIBE statement to get the field default values
    1378             my $desc_cursor = $self->_execSql ("describe $table");
    1379             my (@fields, @fielddefaults, @fieldextra, @fielddesc);
    1380             # TODO: Lousy hack?
    1381             my $fieldextra2 = _getTableExtraHash($self,$table); # Get extra info from the infotable
    1382             while (@fielddesc = $desc_cursor->fetchrow) {
    1383             push @fields, $fielddesc[0];
    1384             push @fielddefaults, $fielddesc[4];
    1385             push @fieldextra, $fielddesc[5];
    1386             }
    1387             $desc_cursor->finish;
    1388              
    1389             my $first_time = 1;
    1390             my ($field, $default, $extra);
    1391              
    1392             # Start the SQL statement
    1393             my $sql = "insert into $table values (";
    1394              
    1395             # Step through the fields and add a section to the statement for each
    1396             while ($field = shift @fields) {
    1397             $default = shift @fielddefaults;
    1398             $extra = shift @fieldextra;
    1399              
    1400             # Convert NULL fields to "" unless they are auto incrementing in which case
    1401             # leave them as NULL to allow the auto increment to function
    1402             $default = $default eq "NULL" ? "" : $default;
    1403             if ($extra eq "auto_increment") {
    1404             $default = "NULL";
    1405             }
    1406              
    1407             # Get the value if we have a CGI-specified value, else use the default
    1408             my $val = $self->{CGI}->param("$field") || $default;
    1409              
    1410             # Add commas between statements
    1411             if ($first_time != 1) {
    1412             $sql .= ', ';
    1413             }
    1414              
    1415             # Encrypt passwords if required, then add the value to the statement
    1416             if ($$fieldextra2{$field} eq "encryptpassword") {
    1417             $sql .= "PASSWORD(" . $self->_sqlQuote($val) . ")";
    1418             } else {
    1419             $sql .= $self->_sqlQuote($val);
    1420             }
    1421              
    1422             $first_time = 0;
    1423             }
    1424              
    1425             # Close the SQL statement
    1426             $sql .= ")";
    1427              
    1428             return ($sql);
    1429             }
    1430              
    1431             # _selectSql - internal function to generate select statements for $table, selecting all fields
    1432             # with a where clause based on the values in $self->{CGI}->param that match the table's column names.
    1433             # The second parameter is appended to the statement, if it is present, which can be used for
    1434             # order by clauses etc.
    1435             sub _selectSql {
    1436             my $self = shift;
    1437             my $table = shift;
    1438             my $additional = shift;
    1439              
    1440             # Use a DESCRIBE statement to get the field default values
    1441             my $desc_cursor = $self->_execSql ("describe $table");
    1442             my (@fields, @fielddesc);
    1443             while (@fielddesc = $desc_cursor->fetchrow) {
    1444             push @fields, $fielddesc[0];
    1445             }
    1446             $desc_cursor->finish;
    1447              
    1448             my $first_time = 1;
    1449             my $field;
    1450              
    1451             # Start the SQL statement
    1452             my $sql = "select * from $table ";
    1453              
    1454             # Step through the fields and add a section to the statement for each
    1455             while ($field = shift @fields) {
    1456             my $val = $self->{CGI}->param("$field");
    1457             next if (!$val);
    1458              
    1459             if ($first_time == 1) {
    1460             $sql .= 'where ';
    1461             $first_time = 0;
    1462             } else {
    1463             $sql .= 'and ';
    1464             }
    1465              
    1466             # TODO: might want to do type check here - does it matter?
    1467             # Add the SQL like statement and append a % to the value to allow part searching
    1468             $sql .= "$field like " . $self->_sqlQuote($val . "%") . " ";
    1469             }
    1470              
    1471             # Add any additional data
    1472             $sql .= $additional;
    1473              
    1474             return($sql);
    1475             }
    1476              
    1477             # _updateSql - internal function to generate update statements for $table, inserting all values in
    1478             # $self->{CGI}->param which match the table column names.
    1479             sub _updateSql {
    1480             my $self = shift;
    1481             my $table = shift;
    1482              
    1483             # Use a DESCRIBE statement to get the primary keys and field names
    1484             my $desc_cursor = $self->_execSql ("describe $table");
    1485             my (@fields, @primary_keys, @fielddesc);
    1486             #TODO: Lousy hack?
    1487             my $fieldextra2 = _getTableExtraHash($self, $table); # Get extra info from the infotable
    1488             while (@fielddesc = $desc_cursor->fetchrow) {
    1489             # Skip if this is a password and no change has been requested
    1490             next if ( ($$fieldextra2{$fielddesc[0]} eq "encryptpassword") && ($self->{CGI}->param($fielddesc[0]) eq "") );
    1491              
    1492             push @fields, $fielddesc[0];
    1493             if ($fielddesc[3] eq "PRI") {
    1494             push @primary_keys, $fielddesc[0];
    1495             }
    1496             }
    1497             $desc_cursor->finish;
    1498              
    1499             my $first_time = 1;
    1500             my $field;
    1501              
    1502             # Start the SQL statement
    1503             my $sql = "update $table ";
    1504              
    1505             # Step through the fields and add a section to the statement for each
    1506             while ($field = shift @fields) {
    1507             my $val = $self->{CGI}->param("$field");
    1508             $val = $val eq "NULL" ? "" : $val;
    1509              
    1510             if ($first_time == 1) {
    1511             $sql .= 'set ';
    1512             $first_time = 0;
    1513             } else {
    1514             $sql .= ', ';
    1515             }
    1516              
    1517             # Encrypt passwords if required, then add the value to the statement
    1518             if ($$fieldextra2{$field} eq "encryptpassword") {
    1519             $sql .= $field . "=" . "PASSWORD(" . $self->_sqlQuote($val) . ") ";
    1520             } else {
    1521             $sql .= $field . "=" . $self->_sqlQuote($val) . " ";
    1522             }
    1523             }
    1524              
    1525             $first_time = 1;
    1526             while ($field = shift @primary_keys) {
    1527             my $val = $self->{CGI}->param("primary_key_$field");
    1528              
    1529             if ( $first_time) {
    1530             $sql .= 'where ';
    1531             $first_time = 0;
    1532             } else {
    1533             $sql .= 'and ';
    1534             }
    1535              
    1536             $sql .= "$field = " . $self->_sqlQuote($val) . " ";
    1537             }
    1538             if ($first_time == 1) { # this is very bad - table has no primary keys...
    1539             $self->_die("_updateSql failed - $table has no primary key set");
    1540             }
    1541              
    1542             return($sql);
    1543             }
    1544              
    1545             # _deleteSql - internal function to generate delete statements for $table, with a where clause
    1546             # based on where $self->{CGI}->param which match $table's primary key names.
    1547             sub _deleteSql {
    1548             my $self = shift;
    1549             my $table = shift;
    1550              
    1551             # Use a DESCRIBE statement to get the primary keys
    1552             my $desc_cursor = $self->_execSql ("describe $table");
    1553             my (@primary_keys, @fielddesc);
    1554             #TODO: Lousy hack?
    1555             my $fieldextra2 = _getTableExtraHash($self,$table); # Get extra info from the infotable
    1556             while (@fielddesc = $desc_cursor->fetchrow) {
    1557             if ($fielddesc[3] eq "PRI") {
    1558             push @primary_keys, $fielddesc[0];
    1559             }
    1560             }
    1561             $desc_cursor->finish;
    1562              
    1563             my $first_time = 1;
    1564             my $field;
    1565              
    1566             # Start the SQL statement
    1567             my $sql = "delete from $table ";
    1568              
    1569             while ($field = shift @primary_keys) {
    1570             my $val = $self->{CGI}->param("$field");
    1571              
    1572             if ( $first_time) {
    1573             $sql .= 'where ';
    1574             $first_time = 0;
    1575             } else {
    1576             $sql .= 'and ';
    1577             }
    1578              
    1579             $sql .= "$field = " . $self->_sqlQuote($val) . " ";
    1580             }
    1581              
    1582             return($sql);
    1583             }
    1584              
    1585             1;
    1586              
    1587             __END__