11 my $documentation =<<'START';
13 *******************************************
14 * Form Generating Script 1.1.2 *
15 *******************************************
19 Added a 'do not save' link at the top and bottom of the form.
20 Fixed problem with using single and double quotes in input file.
21 Changed deprecated PHP function mysql_escape_string to
22 mysql_real_escape_string.
24 bugs: There may still be a problem with reserved MySQL words not
25 being caught. There may be other bugs not discovered yet.
27 future plans: I plan on improving the output format in report.php.
28 For now, users can alter this form as needed. Since formscript.pl
29 knows the fields to be used, it makes more sense to list them
30 explicitly than to print them in a foreach loop. I will get to
35 This is a complete rewrite of an earlier Perl script I wrote to generate
36 forms for OpenEMR. It is now all self contained within a single .pl file.
37 To run at the shell command line, type:
39 Perl formscript.pl [filename]
41 where filename is a text file with data relating to your form. If you run
42 without a filename argument, a sample data file will be created in the same
43 directory named 'sample.txt' that you can use to see how to create your own.
45 Basically you enter one database field item per line like this:
47 Social History::popup_menu::smoker::non-smoker
51 Social History::radio_group::smoker::non-smoker
54 where the first item is the field name. spaces within the name will convert to '_'
55 for the sql database field name. If you use a SQL reserved word, the form generation
56 will fail and this program will notify you of the word(s) you used.
58 The '::' is the standard delimiter that I use between items. The second item on the line
59 is the form widget type. You can choose from:
68 scrolling_list_multiples
70 Putting a '+' at the beginning of the field name will let the form know that you want to
71 report negatives. This means the following:
73 +cardiac_review::checkbox_group::chest pain::shortness of breath::palpitations
75 creates a group of checkboxes where if the user chooses the first two boxes, the database will
76 have the following line entered:
78 chest pain, shortness of breath. Negative for palpitations.
80 The remaining items after the fieldname and the widget type are the names for
81 checkboxes or radio buttons or default text
82 for a textfield or text area. You can also start a line with a '#' as the first character and this
83 will be an ignored comment line. If you put html tags on their own lines, they will be integrated
84 into the form. It will be most helpful to look at 'sample.txt' to see how this works.
86 This is 1.1 and is tested to the extent of installing the form and entering data within an encounter.
87 Please send feedback to mail@doc99.com. I will definitely
88 be fixing and improving it.
96 my $info_txt=<<'START';
100 my $do_not_save=<<'START';
102 echo "<a href='".$GLOBALS['webroot'] . "/interface/patient_file/encounter/patient_encounter.php'>[do not save]</a>";
107 my $new_php =<<'START';
109 include_once("../../globals.php");
110 include_once("$srcdir/api.inc");
111 formHeader("Form: FORM_NAME");
114 <link rel=stylesheet href="<?echo $css_header;?>" type="text/css">
116 <body <?echo $top_bg_line;?> topmargin=0 rightmargin=0 leftmargin=2 bottommargin=0 marginwidth=2 marginheight=0>
117 <form method=post action="<?echo $rootdir;?>/forms/FORM_NAME/save.php?mode=new" name="FORM_NAME">
131 my $print_php=<<'START';
133 include_once("../../globals.php");
134 include_once("$srcdir/api.inc");
135 formHeader("Form: FORM_NAME");
138 <link rel=stylesheet href="<?echo $css_header;?>" type="text/css">
140 <body <?echo $top_bg_line;?> topmargin=0 rightmargin=0 leftmargin=2 bottommargin=0 marginwidth=2 marginheight=0>
141 <form method=post action="<?echo $rootdir;?>/forms/FORM_NAME/save.php?mode=new" name="my_form">
152 my $report_php=<<'START';
154 //------------report.php
155 include_once("../../globals.php");
156 include_once($GLOBALS["srcdir"]."/api.inc");
157 function FORM_NAME_report( $pid, $encounter, $cols, $id) {
159 $data = formFetch("form_FORM_NAME", $id);
162 foreach($data as $key => $value) {
163 if ($key == "id" || $key == "pid" || $key == "user" || $key == "groupname" || $key == "authorized" || $key == "activity" || $key == "date" || $value == "" || $value == "0000-00-00 00:00:00") {
166 if ($value == "on") {
169 $key=ucwords(str_replace("_"," ",$key));
170 $output = stripslashes($value);
171 print "<td><span class=bold>$key: </span><span class=text>$output</span></td>";
173 if ($count == $cols) {
179 print "</tr></table>";
185 my $save_php=<<'START';
187 //------------This file inserts your field data into the MySQL database
188 include_once("../../globals.php");
189 include_once("$srcdir/api.inc");
190 include_once("$srcdir/forms.inc");
192 //process form variables here
193 //create an array of all of the existing field names
194 $field_names = array(FIELDNAMES);
195 $negatives = array(NEGATIVES);
196 //process each field according to it's type
197 foreach($field_names as $key=>$val)
199 if ($val == "checkbox")
201 if ($_POST[$key]) {$field_names[$key] = "positve";}
202 else {$field_names[$key] = "negative";}
204 elseif (($val == "checkbox_group")||($val == "scrolling_list_multiples"))
207 if (array_key_exists($key,$negatives)) #a field requests reporting of negatives
209 foreach($_POST[$key] as $pos) #check positives against list
211 if (array_key_exists($pos, $negatives[$key]))
212 { #remove positives from list, leaving negatives
213 unset($negatives[$key][$pos]);
216 $neg = ". Negative for ".implode(', ',$negatives[$key]);
218 $field_names[$key] = implode(', ',$_POST[$key]).$neg;
222 $field_names[$key] = $_POST[$key];
226 //end special processing
228 foreach ($field_names as $k => $var) {
229 $field_names[$k] = mysql_real_escape_string($var);
232 if ($encounter == "")
233 $encounter = date("Ymd");
234 if ($_GET["mode"] == "new"){
235 $newid = formSubmit("form_FORM_NAME", $field_names, $_GET["id"], $userauthorized);
236 addForm($encounter, "FORM_NAME", $newid, "FORM_NAME", $pid, $userauthorized);
237 }elseif ($_GET["mode"] == "update") {
240 sqlInsert("update form_FORM_NAME set pid = {$_SESSION["pid"]},groupname='".$_SESSION["authProvider"]."',user='".$_SESSION["authUser"]."',authorized=$userauthorized,activity=1, date = NOW(), FIELDS where id=$id");
242 $_SESSION["encounter"] = $encounter;
243 formHeader("Redirecting....");
250 my $table_sql=<<'START';
251 CREATE TABLE IF NOT EXISTS `form_FORM_NAME` (
252 id bigint(20) NOT NULL auto_increment,
253 date datetime default NULL,
254 pid bigint(20) default NULL,
255 user varchar(255) default NULL,
256 groupname varchar(255) default NULL,
257 authorized tinyint(4) default NULL,
258 activity tinyint(4) default NULL,
265 my $view_php =<<'START';
268 include_once("../../globals.php");
269 include_once("$srcdir/api.inc");
270 formHeader("Form: FORM_NAME");
273 <link rel=stylesheet href="<?echo $css_header;?>" type="text/css">
275 <body <?echo $top_bg_line;?> topmargin=0 rightmargin=0 leftmargin=2 bottommargin=0 marginwidth=2 marginheight=0>
276 <form method=post action="<?echo $rootdir?>/forms/FORM_NAME/save.php?mode=update&id=<?echo $_GET["id"];?>" name="my_form">
288 my $preview_html =<<'START';
303 my $sample_txt =<<'START';
306 chief_complaints::textarea
308 <h3>past surgical history</h3>
309 +surgical history::checkbox_group::cholecystectomy::tonsillectomy::apendectomy
311 surgical history other::textfield
313 <h3>past surgical history</h3>
314 +medical history::scrolling_list_multiples::asthma::diabetes::hypertension
316 medical history other::textfield
319 +allergies::checkbox_group::penicillin::sulfa::iodine
321 allergies other::textfield
323 <h2>Social History</h2>
325 smoke history::radio_group::non-smoker::smoker
327 etoh history::scrolling_list::none::occasional::daily::heavy use
330 my @reserved = ('ADD','ALL','ALTER','ANALYZE','AND','AS','ASC','ASENSITIVE','BEFORE','BETWEEN','BIGINT','BINARY','BLOB','BOTH','BY','CALL','CASCADE','CASE','CHANGE','CHAR','CHARACTER','CHECK','COLLATE','COLUMN','CONDITION','CONNECTION','CONSTRAINT','CONTINUE','CONVERT','CREATE','CROSS','CURRENT_DATE','CURRENT_TIME','CURRENT_TIMESTAMP','CURRENT_USER','CURSOR','DATABASE','DATABASES','DAY_HOUR','DAY_MICROSECOND','DAY_MINUTE','DAY_SECOND','DEC','DECIMAL','DECLARE','DEFAULT','DELAYED','DELETE','DESC','DESCRIBE','DETERMINISTIC','DISTINCT','DISTINCTROW','DIV','DOUBLE','DROP','DUAL','EACH','ELSE','ELSEIF','ENCLOSED','ESCAPED','EXISTS','EXIT','EXPLAIN','FALSE','FETCH','FLOAT','FOR','FORCE','FOREIGN','FROM','FULLTEXT','GOTO','GRANT','GROUP','HAVING','HIGH_PRIORITY','HOUR_MICROSECOND','HOUR_MINUTE','HOUR_SECOND','IF','IGNORE','IN','INDEX','INFILE','INNER','INOUT','INSENSITIVE','INSERT','INT','INTEGER','INTERVAL','INTO','IS','ITERATE','JOIN','KEY','KEYS','KILL','LEADING','LEAVE','LEFT','LIKE','LIMIT','LINES','LOAD','LOCALTIME','LOCALTIMESTAMP','LOCK','LONG','LONGBLOB','LONGTEXT','LOOP','LOW_PRIORITY','MATCH','MEDIUMBLOB','MEDIUMINT','MEDIUMTEXT','MIDDLEINT','MINUTE_MICROSECOND','MINUTE_SECOND','MOD','MODIFIES','NATURAL','NOT','NO_WRITE_TO_BINLOG','NULL','NUMERIC','ON','OPTIMIZE','OPTION','OPTIONALLY','OR','ORDER','OUT','OUTER','OUTFILE','PRECISION','PRIMARY','PROCEDURE','PURGE','READ','READS','REAL','REFERENCES','REGEXP','RENAME','REPEAT','REPLACE','REQUIRE','RESTRICT','RETURN','REVOKE','RIGHT','RLIKE','SCHEMA','SCHEMAS','SECOND_MICROSECOND','SELECT','SENSITIVE','SEPARATOR','SET','SHOW','SMALLINT','SONAME','SPATIAL','SPECIFIC','SQL','SQLEXCEPTION','SQLSTATE','SQLWARNING','SQL_BIG_RESULT','SQL_CALC_FOUND_ROWS','SQL_SMALL_RESULT','SSL','STARTING','STRAIGHT_JOIN','TABLE','TERMINATED','THEN','TINYBLOB','TINYINT','TINYTEXT','TO','TRAILING','TRIGGER','TRUE','UNDO','UNION','UNIQUE','UNLOCK','UNSIGNED','UPDATE','USAGE','USE','USING','UTC_DATE','UTC_TIME','UTC_TIMESTAMP','VALUES','VARBINARY','VARCHAR','VARCHARACTER','VARYING','WHEN','WHERE','WHILE','WITH','WRITE','XOR','YEAR_MONTH','ZEROFILL','ACTION','BIT','DATE','ENUM','NO','TEXT','TIME','TIMESTAMP');
332 $reserved{uc $_}++ for @reserved;
338 to_file
('sample.txt',$sample_txt) if not -f
'sample.txt';
339 print $documentation."\n";
345 my $check_reserved = uc $form_name;
346 if ($reserved{uc $check_reserved})
348 print "You have chosen an SQL reserved word for your form name: $check_reserved. Please try again.\n";
351 $form_name =~ s/^\s+(\S)\s+$/$1/;
352 $form_name =~ s/\s+/_/g;
353 if (not -d
$form_name)
355 mkdir "$form_name" or die "Could not create directory $form_name: $!";
357 my @field_data; #the very important array of field data
358 chomp, push @field_data, [ split /::/ ] while <>;
359 my %negatives; #key=field name: these are the fields that require reporting of pertinant
360 #negatives. will only apply to checkbox_group and scrolling_list_multiples types
362 #strip outer spaces from field names and field types and change inner spaces to underscores
363 #and check field names for SQL reserved words now
366 if ($_->[0] and $_->[1])
368 $_->[0] =~ s/^\s+(\S)\s+$/$1/;
369 $_->[0] =~ s/\s+/_/g;
370 $check_reserved = $_->[0] =~ m/(\w+)/ ?
uc $1 : q{};
371 push @reserved_used, $check_reserved if $reserved{$check_reserved};
372 $_->[1] =~ s/^\s+(\S)\s+$/$1/;
373 if ($_->[0] =~ /^\+/) #a leading '+' indicates to print negatives
374 { # or not checked values in a checkbox_group or scrolling_list_multiples
375 $_->[0] =~ s/^\+(.*)/$1/;
376 $negatives{$_->[0]}++;
382 print "You have chosen the following reserved words as field names. Please try again.\n";
383 print "$_\n" for @reserved_used;
387 my $text = make_form
(@field_data);
391 $out = replace
($info_txt, 'FORM_NAME', $form_name);
392 to_file
("$form_name/info.txt",$out);
395 $out = replace
($new_php, 'FORM_NAME', $form_name);
396 $out = replace
($out, 'DATABASEFIELDS', $text);
397 to_file
("$form_name/new.php",$out);
400 $out = replace
($print_php, 'FORM_NAME', $form_name);
401 $out = replace
($out, 'DATABASEFIELDS', $text);
402 to_file
("$form_name/print.php",$out);
405 $out = replace
($report_php, 'FORM_NAME', $form_name);
406 $out = replace
($out, 'DATABASEFIELDS', $text);
407 to_file
("$form_name/report.php",$out);
410 $out = replace
($save_php, 'FORM_NAME', $form_name);
411 $out = replace_save_php
($out, @field_data);
412 to_file
("$form_name/save.php",$out);
415 $out = replace
($view_php, 'FORM_NAME', $form_name);
416 $out = replace
($out, 'DATABASEFIELDS', $text);
417 to_file
("$form_name/view.php",$out);
420 $out = replace
($table_sql, 'FORM_NAME', $form_name);
421 $out = replace_sql
($out, @field_data);
422 to_file
("$form_name/table.sql",$out);
425 $out = replace
($preview_html, 'FORM_NAME', $form_name);
426 $out = replace
($out, 'DATABASEFIELDS', $text);
427 to_file
("$form_name/preview.html",$out);
435 $text =~ s/$_/$words{$_}/g for keys %words;
440 sub replace_save_php
#a special case
443 my @fields = map {$_->[0]} grep{$_->[0] and $_->[1]} @_;
446 $_ = "$_='\".\$field_names[\"$_\"].\"'";
448 my $fields = join ',',@fields;
449 $text =~ s/FIELDS/$fields/;
454 if ($_->[0] and $_->[1])
456 push @fields, "'$_->[0]' => '$_->[1]'";
457 if ($negatives{$_->[0]})
461 while ($count < scalar(@
$_))
463 push @temp, "'$_->[$count]' => '$_->[$count]'";
466 push @negatives, "'$_->[0]' => array(".join(', ', @temp).")";
470 $fields = join ', ', @fields;
471 $text =~ s/FIELDNAMES/$fields/;
472 my $negatives = join ', ', @negatives;
473 $text =~ s/NEGATIVES/$negatives/;
477 sub replace_sql
#a special case
480 my @fields = map {$_->[0]} grep{$_->[0] and $_->[1]} @_;
482 $replace .= "$_ TEXT,\n" for @fields;
483 $text =~ s/DATABASEFIELDS/$replace/;
490 my $return = submit
(-name
=>'submit form') . $do_not_save;
491 $return .= "<table>";
495 next if $_->[0] =~ /^#/; #ignore perl type comments
496 if ($_->[0] =~ /^\w/ and $_->[1])
503 my $field_name = shift @
$_;
504 my $field_type = shift @
$_;
505 my $label = $field_name;
507 if ($field_type =~ /^textfield$/)
509 $return .= Tr
(td
($label),td
(textfield
(-name
=>$field_name, -value
=> join @
$_)))."\n";
511 elsif ($field_type =~ /^textarea$/)
513 $return .= Tr
(td
($label),td
(textarea
(-name
=>$field_name, -rows
=>4, -columns
=>40, -value
=> join @
$_)))."\n";
515 elsif ($field_type =~ /^radio_group$/)
517 $return .= Tr
(td
($label),td
(radio_group
(-name
=>$field_name, -values=>$_)))."\n";;
519 elsif ($field_type =~ /^checkbox$/)
521 $return .= Tr
(td
($label),td
(checkbox
(-name
=>$field_name, -value
=>'yes', -label
=> join @
$_)))."\n";
523 elsif ($field_type =~ /^checkbox_group$/)
525 $return .= Tr
(td
($label),td
(checkbox_group
(-name
=>$field_name.'[]', -values=>$_)))."\n";
527 elsif ($field_type =~ /^popup_menu/)
529 $return .= Tr
(td
($label),td
(popup_menu
(-name
=>$field_name, -values=>$_)))."\n";
531 elsif ($field_type =~ /^scrolling_list/)
535 $mult = 'true' if $field_type =~ /multiples$/;
536 $mult2 = '[]' if $field_type =~ /multiples$/;
537 $return .= Tr
(td
($label),td
(scrolling_list
(-name
=>$field_name.$mult2, -values=>$_, -size
=>scalar(@
$_), -multiple
=>$mult)))."\n";
539 unshift @
$_, $field_type;
540 unshift @
$_, $field_name;
542 else #probably an html tag or something
544 $return .= "</table>";
545 $return .= $_->[0]."\n";
546 $return .= "<table>";
549 $return .= "</table>";
550 $return .= submit
(-name
=>'submit form') . $do_not_save;
556 my $filename = shift;
559 open $file, '>', $filename or die "cannot open $filename: $!";
561 close $file or die "cannot close $filename: $!";