1 # Copyright 2002-2007 Interchange Development Group and others
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version. See the LICENSE file for details.
8 # $Id: auto_wizard.coretag,v 1.20 2007-03-30 23:40:54 pajamian Exp $
10 UserTag auto-wizard Order name
11 UserTag auto-wizard AddAttr
12 UserTag auto-wizard HasEndTag
13 UserTag auto-wizard Version $Revision: 1.20 $
14 UserTag auto-wizard Routine <<EOR
16 use vars qw/$Session $Tag $CGI $Tmp $Scratch $Values $ready_safe/;
50 @overall_opt{@wanted_opts} = @wanted_opts;
53 my ($opt, $already, $default) = @_;
55 ? ($opt->{already_title} ||= "You already did that survey!" )
56 : ($opt->{thanks_title} ||= $default || "Thanks for completing the survey!");
61 my ($opt, $already) = @_;
64 $opt->{already_message} ||=
65 "We only want to collect information once from each person. Thank you.";
66 $tm = $opt->{already_message};
69 $opt->{thanks_message} ||= "Your survey is complete. Thank you.";
70 $tm = $opt->{thanks_message};
73 $opt->{intro_text} .= "<h1>$tm</h1>" if $already;
76 sub title_and_message {
77 my ($opt, $already) = @_;
78 my $tt = thanks_title($opt, $already);
79 my $tm = thanks_message($opt, $already);
90 my ($wizname, $set) = @_;
91 my $surv = $Vend::Session->{surveys} ||= {};
93 $surv->{$wizname} = $set;
96 if ($Vend::Session->{logged_in} and ! $Vend::admin) {
97 if (! defined $surv->{$wizname}) {
99 function => 'check_file_acl',
100 location => "survey/$wizname",
102 $surv->{$wizname} = $Tag->userdb($o);
106 function => 'set_file_acl',
107 location => "survey/$wizname",
108 mode => $surv->{$wizname},
114 return $surv->{$wizname};
117 sub survey_log_generate_final {
118 my ($wizname, $opt, $ary) = @_;
121 or die "bad call to generate_final routine, output options not hash ref ($opt)";
123 or die "bad call to generate_final routine, output not array ref ($ary)";
125 my $done = already($wizname);
127 push @$ary, title_and_message($opt, $done);
130 $opt->{intro_text} .= '<h1>' . thanks_title($opt, 1) . '</h1>';
133 # $opt->{survey_counter} ||= "logs/survey/$wizname.cnt";
134 # $opt->{survey_file} ||= "logs/survey/$wizname.txt";
135 # push @$ary, "\tsurvey_file: $opt->{survey_file}";
136 # push @$ary, "\tsurvey_counter: $opt->{survey_counter}";
141 sub gen_email_header {
142 my ($wizname, $ref, $opt, $fnames) = @_;
143 my $subject = errmsg($opt->{email_subject} || "Response to %s", $wizname);
144 my $from_addr = $opt->{email_from};
145 my $cc_addr = $opt->{email_cc};
146 for(qw/ EMAIL_SURVEY EMAIL_INFO EMAIL_SERVICE /) {
147 next unless $from_addr = $::Variable->{$_};
150 $from_addr ||= $Vend::Cfg->{MailOrderFrom} || $Vend::Cfg->{MailOrderTo};
156 $tpl .= "Cc: $cc_addr\n" if $cc_addr;
160 sub gen_email_template {
161 my ($wizname, $ref, $opt, $fnames) = @_;
162 my $tpl = gen_email_header($wizname, $ref, $opt, $fnames);
165 {code?}Sequence: {code}
166 {/code?}Username: {username}
167 IP Address: $CGI::remote_addr
168 Host: $CGI::remote_host
170 --------------------------------------------
173 my @fields = grep /\S/, split /\s+/, $opt->{output_fields};
179 $tpl .= "$_: {$_}\n";
181 $tpl .= "--------------------------------------------\n";
186 my ($wizname, $ref, $opt, $fnames) = @_;
187 #::logDebug("Called email_output");
188 return unless $opt->{output_email};
190 #::logDebug("email_output has an address of $opt->{output_email}");
191 ## Check and see if already sent
192 if(! $opt->{output_repeated} and already($wizname)) {
193 #::logDebug("email_output already done, repeated=$opt->{output_repeated} already=" . ::uneval($Vend::Session->{surveys}));
197 #::logDebug("email_output is continuing");
198 my $tpl = $opt->{email_template};
199 if(! $tpl or $tpl !~ /\S/) {
200 $tpl = gen_email_template($wizname, $ref, $opt, $fnames);
203 $opt->{email_template} =~ s/\s+$//;
204 $opt->{email_template} =~ s/^\s+//;
205 if($opt->{email_template} !~ /[\r\n]/) {
206 $tpl = interpolate_html(Vend::Util::readfile($opt->{email_template}));
209 $tpl = $opt->{email_template};
211 if($tpl !~ /^[-\w]+:/) {
212 $tpl = join "\n", gen_email_header($wizname, $ref, $opt, $fnames), $tpl;
216 #::logDebug("email_output tpl=$tpl");
218 my @fields = grep /\S/, split /\s+/, $opt->{output_fields};
223 my $outref = { %$opt };
225 $outref->{ip_address} = $CGI::remote_addr;
226 $outref->{host_name} = $CGI::remote_host;
227 $outref->{username} = $Vend::username || 'anonymous';
228 $outref->{date} = POSIX::strftime("%Y-%m-%d %H:%M:%S", localtime());
231 $outref->{$_} = $Values->{$_};
233 my $out = tag_attr_list($tpl, $outref);
236 $status = $Tag->email_raw({}, $out)
237 or ::logError("Failed to send survey email output:\n$out");
238 #::logDebug("email_output status=$status");
242 sub survey_log_to_file {
243 my ($wizname, $ref, $opt, $fnames) = @_;
245 if(! $opt->{output_repeated} and already($wizname)) {
246 return template_attr($wizname, $ref, $opt, $fnames);
249 my $fn = $ref->{survey_file};
250 my $cfn = $ref->{survey_counter};
251 my $sqlc = $ref->{survey_counter_sql};
254 $fn = $::Variable->{SURVEY_LOG_DIR} || 'logs/survey';
255 $fn .= "/$wizname.txt";
258 if(! $cfn and ! $sqlc) {
262 $cfn =~ s:(.*/):$1.:;
265 my @fields = grep /\S/, split /\s+/, $opt->{output_fields};
270 my $string = join "\t",
271 'code', 'ip_address', 'username', 'date', @fields;
273 $Tag->write_relative_file($fn, $string);
276 my @o = $Tag->counter({file => $cfn, sql => $sqlc});
277 push @o, $CGI::remote_addr;
278 push @o, $Vend::username || 'anonymous';
279 push @o, POSIX::strftime("%Y-%m-%d %H:%M:%S", localtime());
282 my $result = $Values->{$_};
283 $result =~ s/\r?\n/\r/g;
289 email_output($wizname, $ref, $opt, $fnames);
290 already($wizname => 1) unless $opt->{output_repeated};
291 return template_attr($wizname, $ref, $opt, $fnames);
294 my %survey_genfinal = (
295 survey_log => \&survey_log_generate_final,
297 my ($wizname, $opt, $ary) = @_;
298 push @$ary, title_and_message($opt, already($wizname));
299 if($opt->{continue_template}) {
300 push @$ary, "template: <<EOF";
301 push @$ary, $opt->{continue_template};
307 my ($wizname, $opt, $ary) = @_;
308 my $line = "final: ";
309 $line .= thanks_title(
311 $Vend::Session->{surveys}{$wizname},
312 errmsg("Finished with %s", $wizname),
316 if($opt->{continue_template}) {
317 push @$ary, "template: <<EOF";
318 push @$ary, $opt->{continue_template};
326 my ($wizname, $ref, $opt, $fields) = @_;
329 if(ref($fields) eq 'hash') {
330 %attr = { %$fields };
333 $attr{TITLE} = $ref->{_page_title} || "Finished with $wizname...";
334 $attr{PROMPT} = $ref->{prompt};
335 $attr{ANCHOR} = $ref->{anchor} || 'Go';
336 $attr{EXTRA} = $ref->{extra} || '';
337 $attr{EXTRA} = " $attr{EXTRA}" if $attr{EXTRA};
338 $attr{URL} = wizard_url($ref, $opt, $fields);
339 #::logDebug("generated ATTR is: " . uneval(\%attr));
340 my $template = $ref->{template} || <<EOF;
345 <A HREF="{URL}"{EXTRA}>{ANCHOR}</A>
348 return tag_attr_list($template, \%attr);
352 my ($ref, $opt, $fields) = @_;
365 $form->{$_} = $ref->{$_};
368 $form->{href} = $opt->{output_href} || $ref->{href} || $ref->{page};
369 if($opt->{output_parm}) {
370 my $ref = Vend::Util::scalar_to_hash($opt->{output_parm}) || {};
372 $form->{$_} = $ref->{$_};
375 $form->{form} = 'auto';
377 $form->{$_} = $Values->{$_};
382 my @pairs = split /[\s,\0]+/, $ref->{remap};
384 my ($k, $v) = split /=/, $_;
385 next unless $k and $v;
386 my $val = delete($form->{$k}) || $save->{$k};
392 return $Tag->area($form);
395 my %survey_auto = qw/
402 ## $$dest = $sub->($wizname, $ref, $opt, \@vals);
404 ## $wizname name of wizard/survey
405 ## $ref copy of final stanza of auto_wizard, hash ref with keys, can modify
406 ## %opts Options auto_wizard was created with, can modify
407 ## @vals Fields names collected in the wizard, can modify
409 my %survey_action = (
410 survey_log => \&survey_log_to_file,
412 my ($wizname, $ref, $opt, $fnames) = @_;
413 my $url = wizard_url($ref, $opt, $fnames);
414 email_output($wizname, $ref, $opt, $fnames);
415 my $status = $Tag->deliver( { type => 'text/html', location => $url });
419 my ($wizname, $ref, $opt, $fnames) = @_;
420 $ref->{wizard_name} = $wizname;
421 email_output($wizname, $ref, $opt, $fnames);
422 return template_attr($wizname, $ref, $opt, $fnames);
427 my ($wizname, $opt, $script) = @_;
428 #Debug("script in: $script");
430 $script =~ s/\r\n/\n/g;
431 $script =~ s/\r/\n/g;
432 my @lines = split /\n/, $script;
437 my $qip; # question in progress
438 my $iip; # item in progress
439 my $fip; # final in progress
440 my $bip; # breaks in progress
441 my $blip; # break labels in progress
442 my $began; # We have begun
451 #Debug("found db_id=$opt->{db_id}");
452 my ($t, $k) = split /:+/, $opt->{db_id}, 2;
454 my $met = $Tag->meta_record($k, undef, $t)
456 my($structure) = delete $met->{ui_data_fields};
457 delete $met->{extended};
459 #Debug("display type=$opts{display_type} met=" . ::uneval($met) );
460 $met->{row_template} = $opt->{row_template}
461 if $opt->{row_template};
462 my $ids = $t . '::' . $k . '::';
463 $structure =~ s/\r\n?/\n/g;
464 my $string = "\n\n$structure";
466 while ($string =~ s/\n+(?:\n[ \t]*=(.*))?\n+[ \t]*(\w[:.\w]+)/\n$2/) {
469 $string =~ s/^[\s,\0]+//;
470 $string =~ s/[\s,\0]+$//;
471 $string =~ s/[,\0\s]+/ /g;
472 my @fields = split /\s+/, $string;
473 my @out = "$k: $met->{label}";
475 my $fields_line = join "\t", @fields;
478 push @out, "$i: $break{$_}";
481 push @out, "\tdb_id: $ids$_";
484 $opts{output_fields} ||= join " ", @fields;
485 my $otype = $opts{output_type} || 'default';
486 my $sub = $survey_genfinal{$otype} || $survey_genfinal{default};
487 $sub->($k, \%opts, \@out);
492 #Debug("Found some lines, number=" . scalar @lines);
493 #Debug("display type=$opts{display_type}");
517 if(/^(\w+):\s*(.*)/) {
522 _page_name => 'begin',
531 if(/^(\d+)[:.]\s*(.*)/) {
532 my $pn = $1; my $title = $2;
534 my $lastpage = $ref->{_page_name};
542 _break_labels => $blip,
543 _page_title => $title,
547 if(/^final[:.]\s*(.*)/) {
553 my $lastpage = $ref->{_page_name};
554 $ref = { _page_name => 'final', _page_title => $title};
561 unless (/^([A-Za-z]\w+)(?:=([^:]+))?\s*:\s*(.*)/s) {
562 $Tag->warnings(qq{Unrecognized "$_" in middle of script.});
569 $ref->{_modifier} ||= {};
570 $ref->{_modifier}{$thing} = $modifier;
572 $ref->{$thing} = $value;
577 if(/^(itl|perl)(?:_condition)?:\s*(.*)$/s) {
578 if(! $ref->{_condition}) {
579 $ref->{_condition_type} = $1;
580 $ref->{_condition} = $2;
584 "%s_condition: cannot set twice in wizard %s screen %s",
593 elsif(/^opt:\s*(.*)$/s) {
596 my ($n, $v) = split /=/, $option, 2;
597 my $o = $ref->{_options} ||= [];
603 unless (/^([A-Za-z]\w+)(?:=([^:]+))?\s*:\s*(.*)/s) {
604 $Tag->warnings(qq{Unrecognized "$_" in middle of script.});
613 ## This redoes the loop
614 if($thing eq 'name') {
618 elsif($thing eq 'break') {
620 $break =~ s/,/)/g;
621 $ref->{_breaks} ||= ($bip = []);
622 $ref->{_break_labels} ||= ($blip = []);
625 elsif($thing eq 'db_id') {
626 my ($t, $survey, $name) = split /:+/, $value, 3;
628 my $key = $survey . '::' . $name;
629 my $meta = $Tag->meta_record($key, undef, $t);
633 $ref->{$_}{$thing} = $meta->{$_};
636 $ref->{name}{$thing} = $thing;
637 #::logDebug("meta record is " . ::uneval($meta));
646 push @$blip, "$iip=$break";
649 $ref->{label}{$iip} = $value if $value;
654 $ref->{_modifier} ||= {};
655 $ref->{_modifier}{$thing} ||= {};
656 $ref->{_modifier}{$thing}{$iip} = $modifier;
658 $ref->{$thing} ||= {};
659 $ref->{$thing}{$iip} = $value;
662 unless (/^([A-Za-z]\w+)(?:=([^:]+))?\s*:\s*(.*)/s) {
663 $Tag->warnings(qq{Unrecognized "$_" in beginning section of script.});
669 $ref->{$thing} = $value;
673 $wizname ||= 'default';
674 my $wiz_ary = $Session->{auto_wizard} ||= {};
675 $wiz_ary->{$wizname} = \@pages;
676 #Debug("Wizard $wizname=" . ::uneval(\@pages));
681 my ($wizname, $opt, $body) = @_;
684 $wizname ||= $CGI->{wizard_name};
686 if($opt->{scratch}) {
687 $Tag->tmp($opt->{scratch});
688 $::Scratch->{$opt->{scratch}} ||= '';
689 $dest = \$::Scratch->{$opt->{scratch}};
692 $Tmp->{auto_wizard} ||= '';
693 $dest = \$Tmp->{auto_wizard};
695 return $$dest if $opt->{show} and ! $opt->{run};
697 if($opt->{compile} eq 'auto') {
698 $Session->{auto_wizard} ||= {};
699 undef $opt->{compile} if $wizname && $Session->{auto_wizard}{$wizname};
700 $opt->{show} = 1 unless defined $opt->{show};
704 if($opt->{compile}) {
706 $n = compile_wizard(@_)
710 "Wizard %s failed to compile.",
716 #Debug("compiler returned wizname=$n");
721 if(! defined $opt->{run}) {
723 $opt->{show} = 0 if ! defined $opt->{show};
726 my $title_var = $opt->{title_scratch} || 'page_title';
727 my $banner_var = $opt->{banner_scratch} || 'page_banner';
730 $wizname ||= $CGI->{wizard_name} || 'default';
731 #Debug("wizname=$wizname");
733 return unless $wiz = $Vend::Session->{auto_wizard}{$wizname};
734 #Debug("we have a wiz! wizname=$wizname");
737 my $fin = $wiz->[-1];
740 return "Bad wizard!" unless ref($_) eq 'HASH';
743 my $lastwiz = $#$wiz;
744 my $lastpage = $CGI->{wizard_page} || 0;
748 copyref($beg, \%opts);
750 # Get rid of internal stuff
756 if($CGI->{ui_wizard_action} eq 'Back') {
757 $current_page = $lastpage - 1;
759 elsif($CGI->{ui_wizard_action} eq 'Cancel') {
762 elsif($CGI->{ui_wizard_action} eq 'Next') {
763 $current_page = $lastpage + 1;
766 $current_page = $lastpage;
772 #::logDebug("Getting screens");
774 $optref = $wiz->[$current_page];
775 if(! $condition_done and $optref->{_condition}) {
778 if($optref->{_condition_type} eq 'itl') {
780 $result = interpolate_html($optref->{_condition});
783 $result =~ s/.*\s//s;
785 $current_page += $result;
789 $result = $ready_safe->reval($optref->{_condition});
793 "error during perl conditional: $@\ncode was:\n%s",
795 $optref->{_condition},
800 #::logDebug("did perl conditional, result=$result");
801 $current_page += $result;
806 if($current_page <= 0) {
809 elsif ( ($current_page + 1) == $lastwiz ) {
810 $opts{next_text} = errmsg('Finish')
811 if $survey_auto{$opts{output_type}} or $fin->{auto};
813 elsif ($current_page >= $lastwiz) {
816 $optref = $wiz->[$current_page];
819 unless($current_page <= 1) {
820 delete $opts{intro_text};
821 delete $optref->{intro_text};
827 # ::logDebug("running interpolate of $val");
828 return interpolate_html($val);
832 my $filters = join " ", @_;
833 return $Tag->filter($filters, $val);
837 $Scratch->{$title_var} = $optref->{_page_title};
838 $Scratch->{$banner_var} = $optref->{_page_title};
844 if( $mod = delete $ref->{_modifier}) {
847 if(my $m = $mod->{$_}) {
849 my $sub = $modsub{$m} || $modsub{default};
850 $ref->{$_} = $sub->($ref->{$_}, $m);
857 next unless ref($w->{_name}) eq 'ARRAY';
858 push @vals, @{$w->{_name}};
861 my $otype = $opts{output_type};
862 $otype ||= 'auto_bounce' if $ref->{auto};
863 my $sub = $survey_action{$otype} || $survey_action{default};
864 $$dest = $sub->($wizname, $ref, \%opts, \@vals);
865 return $$dest if $opt->{show};
867 #Debug("finished, page ref=" . uneval($ref));
871 #Debug("we have a wiz=$wizname! current_page = $current_page");
873 #Debug("optref=" . $Tag->uneval(undef, $optref));
875 #::logDebug("prepping to walk optref");
877 ### TODO: Find bad reference when no section title...
879 my $name = $optref->{_name} || die;
880 # $Scratch->{page_title} = $optref->{_page_title};
882 if($optref->{_breaks} and ref($optref->{_breaks}) eq 'ARRAY') {
883 $opts{ui_break_before} = join " ", @{$optref->{_breaks}};
884 $opts{ui_break_before_label} = join ",", @{$optref->{_break_labels}};
887 if(my $o = $optref->{_options}) {
888 for (my $i = 0; $i < @$o; $i += 2) {
889 $opts{$o->[$i]} = $o->[$i + 1];
893 $opts{form_name} ||= 'wizard';
894 $opts{all_errors} = '1';
896 wizard_name => $wizname,
897 wizard_page => $current_page,
904 $opts{mv_cancelpage} ||= 'index';
905 $opts{row_template} ||= $opt->{row_template} || <<'EOF' unless $opts{display_type};
906 {HELP?}<td> </td><td>
907 <span style="color: blue">{HELP}</span>
908 {HELP_URL?}<BR><A HREF="{HELP_URL}">more help</A>{/HELP_URL?}
913 <td class=cdata width="20%" valign=top>
916 <td class=cdata width=500>
921 <td colspan=2><img src="bg.gif" height=1 width=1></td>
924 $opts{ui_wizard_fields} = join " ", @$name;
925 $opts{mv_nextpage} = $Global::Variable->{MV_PAGE};
926 $opts{mv_prevpage} = $Global::Variable->{MV_PAGE} if $current_page != 1;
927 $opts{bottom_buttons} = 1;
929 #::logDebug("walking optref");
930 my $mod = $optref->{_modifier} || '';
933 next if $overall_opt{$_};
934 next unless ref($optref->{$_}) eq 'HASH';
935 $opts{$_} = {} if ref($opts{$_}) ne 'HASH';
936 Vend::Util::copyref($optref->{$_}, $opts{$_});
938 if($mod and $m = $mod->{$_}) {
940 for my $k (keys %$r) {
941 next unless $m->{$k};
942 my @subs = split /\s*,\s*/, $m->{$k};
944 my $sub = $modsub{$_} || $modsub{default};
945 $r->{$k} = $sub->($r->{$k}, $_);
951 $opts{widget} ||= {};
952 if( my $r = delete $opts{type} ) {
954 $opts{widget}{$_} = $r->{$_};
959 # Prevent ui_data_fields from parent corrupting wizard
960 delete $opts{ui_data_fields};
961 delete $opts{extended};
962 #::logDebug("calling table_editor opts=" . ::uneval(\%opts));
963 $$dest = $Tag->table_editor( {all_opts => \%opts });
964 if($$dest !~ /<form\s+/i) {
965 my $msg = errmsg("Auto wizard failed to run wizard %s.", $name);
966 $$dest .= $Tag->error({ show => 1, set => $msg });
969 return $$dest if $opt->{show};