# This source code file is part of the "mod_survey" package. # # Copyright (C) 2004 Joel Palmius # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program (probably in a file named "LICENSE.txt" or the like); # if not, write to: # # Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #!/usr/bin/perl package Survey::Display; use strict; use Survey::Document; use Survey::Component::Component; use Survey::Component::Env; use Survey::Component::Geo; use Survey::Component::Constant; use Survey::Component::Calculated; use Survey::Component::Lickert; use Survey::Component::Choice; use Survey::Component::List; use Survey::Component::Matrix; use Survey::Component::Boolean; use Survey::Component::Text; use Survey::Component::Memo; use Survey::Component::Newline; use Survey::Component::Custom; use Survey::Component::Comment; use Survey::Component::Ifroute; use Survey::Component::Caseroute; use Survey::Component::Randomroute; use Survey::Component::Route; use Survey::Component::Cati; use Survey::Component::MailCopy; use Survey::Component::Sequence; use Survey::Language; use CGI qw/:standard/; use CGI::Cookie; sub new { # added in CRU patch (MJ/20020802) my ($crap, $doc, $arg, $sys, $retrieve, $user) = @_; my $self = {}; my ($tid, @timestamp); $self->{DOCUMENT} = $doc; $self->{SESSION} = $doc->{SESSION}; $self->{ARGUMENT} = $arg; $self->{SYSTEM} = $sys; # added in CRU patch (MJ/20020802) $self->{RETRIEVE} = $retrieve; $self->{USER} = $user; $self->{ERROR} = 0; $self->{ERRORCODE} = 0; $self->{DEBUG} = $doc->{DEBUG}; bless($self); $self->{MAXLOOP} = 50; # added in CRU patch (MJ/20020802) if ($doc->GetOption("DBITABLE")) { my ($dsn) = $doc->GetOption("DBIDSN"); my ($usr) = $doc->GetOption("DBIUSER"); my ($psw) = $doc->GetOption("DBIPASSWD"); if (!($self->{DBH} = DBI->connect($dsn, $usr, $psw, { PrintError => 0, AutoCommit => 1, RaiseError => 0 }))) { $self->{ERROR} = lprint("Could not connect to DBI database (error was ") . $DBI::errstr . ")"; $self->{ERRORCODE} = 99; } } if ($ENV{"_SURVEY_USEDBI"}) { eval "use Survey::Component::Import"; if ($@) { $self->{ERROR} = lprint("Could not include IMPORT") . " " . $@; $self->{ERRORCODE} = 99; } } if (!$self->{ERROR} && ($doc->GetOption("UNIQUE") ne "no")) { my ($sb) = $doc->GetOption("SYSBASE"); # Uniqueness based on auth, "yes" for backwards compatibility if (($doc->GetOption("UNIQUE") eq "yes") || ($doc->GetOption("UNIQUE") eq "auth")) { my ($ru) = $ENV{REMOTE_USER}; if ($ru && (-e $sb . $ru)) { $self->{ERROR} = "\'" . $ru . "\' " . lprint("has already answered this survey"); $self->{ERRORCODE} = 4; return $self; } } # IP-based uniqueness checking if ($doc->GetOption("UNIQUE") eq "ip") { my ($ru) = $ENV{REMOTE_ADDR}; if ($ru && (-e $sb . $ru)) { $self->{ERROR} = lprint("This survey has already been answered from this computer"); $self->{ERRORCODE} = 99; return $self; } } # Cookie-based uniqueness checking if ($doc->GetOption("UNIQUE") eq "cookie") { # TODO } } # Refuse to display if previous part was not answered (JP/20020524) if (($doc->GetOption("MULTIPAGE") > 1) && (!$arg->ArgByName("ismultipagesequence"))) { $self->{ERROR} = lprint("This is a part of a MULTIPAGE sequence. Answer the previous page first."); $self->{ERRORCODE} = 5; return $self; } @timestamp = localtime(time); $tid = $timestamp[2] * 3600 + $timestamp[1] * 60 + $timestamp[0]; $self->{DEBUG}->AddDebugMsg("DISPLAY", "Creating key..."); if ($doc->GetOption("CHECKKEY") eq "no") { if ($doc->GetOption("MULTIPAGE") == 1) { $sys->CreateKey(); } } else { $sys->CreateKey(); } # added in CRU patch (MJ/20020806) if ($self->{RETRIEVE}) { $self->{BASETABLE} = $doc->GetOption("DBITABLE"); $self->{BASETABLE} =~ s/$self->{USER}//; } if (!$sys->Error()) { $self->{DEBUG}->AddDebugMsg("DISPLAY", "Created key " . $sys->GetKey()); $self->PrintHead(); if ($doc->GetOption("ACCESSFUN")) { print "
GetOption("ACCESSFUN") . "\">\n"; } else { print " \n"; } $self->PrintTags(); } else { $self->{ERROR} = lprint("A system error (") . $sys->Error() . lprint(") has occured"); $self->{ERRORCODE} = 1; } @timestamp = localtime(time); $tid = ($timestamp[2] * 3600 + $timestamp[1] * 60 + $timestamp[0]) - $tid; $self->{DEBUG}->AddDebugMsg("DISPLAY", "Display took " . $tid . " seconds."); return ($self); } sub ParsePerl { my ($self, $out, $doc, $ses) = @_; my ($ml) = $self->{MAXLOOP}; my ($rep) = 0; my ($start) = -1; my ($end) = 0; while (($rep < $ml) && (($start = index($out, "{\\")) >= 0) && (!$self->{ERROR})) { my ($contents) = ""; $rep++; $end = index($out, "\\}"); if ($end > $start) { my ($substr) = substr($out, $start + 2, $end - $start - 2); $substr =~ s/\x0d//g; my (@ops) = split(/\x0a/, $substr); my ($cell, $reval); $reval = "\$output = \"\";\n"; foreach $cell (@ops) { $cell =~ s/^[\x09\ ]+//g; $cell =~ s/^print\ /\$output\ \.\=\ /; if ($cell) { $reval .= $cell . "\n"; } } my ($output); $reval .= "\$output;\n"; my ($compartment) = new Safe("Tempo"); my(%cook) = fetch CGI::Cookie; my($cookies) = {}; my($c); foreach $c (keys %cook) { $cookies->{$c} = $cook{$c}->value; } $Tempo::self = $self; $Tempo::cookies = $cookies; $Tempo::document = $self->{DOCUMENT}; $Tempo::argument = $self->{ARGUMENT}; $Tempo::session = $self->{SESSION}; if ($ENV{_SURVEY_DISABLE_SAFE} eq 1) { $compartment->deny_only(qw(:dangerous)); } else { $compartment->permit(qw(time localtime crypt :browse)); } $output = $compartment->reval($reval); if ($@) { $self->{ERROR} = lprint("Security exception: " . $@); $self->{ERRORCODE} = 99; } my ($contents) = $output; $contents = substr($out, 0, $start) . $contents; $contents .= substr($out, $end + 2); $out = $contents; } else { $self->{ERROR} = lprint("Found \"{\\\" without \"\\}\""); $self->{ERRORCODE} = 99; } } return $out; } sub ParseVariables { my ($self, $out, $doc, $ses) = @_; my ($ml) = $self->{MAXLOOP}; my ($rep) = 0; my ($start) = -1; my ($end) = 0; while (($rep < $ml) && (($start = index($out, "{\$")) >= 0) && (!$self->{ERROR})) { my ($contents) = ""; $rep++; $end = index($out, "\$}"); if ($end > $start) { my ($substr) = substr($out, $start + 2, $end - $start - 2); if (defined($ses->getValue("SUBMITTED_$substr"))) { $contents = $ses->getValue("SUBMITTED_$substr"); $contents = substr($out, 0, $start) . $contents; $contents .= substr($out, $end + 2); $out = $contents; } else { $self->{ERROR} = lprint("The variable") . " \"$substr\" " . lprint("has not been submitted"); $self->{ERRORCODE} = 99; } } else { $self->{ERROR} = lprint("Found \"{\$\" without \"\$}\""); $self->{ERRORCODE} = 99; } } return $out; } sub ParseSession { my ($self, $out, $doc, $ses) = @_; my ($ml) = $self->{MAXLOOP}; my ($rep) = 0; my ($start) = -1; my ($end) = 0; while (($rep < $ml) && (($start = index($out, "{_")) >= 0) && (!$self->{ERROR})) { my ($contents) = ""; $rep++; $end = index($out, "_}"); if ($end > $start) { my ($substr) = substr($out, $start + 2, $end - $start - 2); if (defined($ses->getValue($substr))) { $contents = $ses->getValue($substr); $contents = substr($out, 0, $start) . $contents; $contents .= substr($out, $end + 2); $out = $contents; } else { $self->{ERROR} = lprint("The session variable") . " \"$substr\" " . lprint("is not defined."); $self->{ERRORCODE} = 99; } } else { $self->{ERROR} = lprint("Found \"{_\" without \"_}\""); $self->{ERRORCODE} = 99; } } return $out; } sub ParseCaptions { my ($self, $out, $doc, $ses) = @_; my ($ml) = $self->{MAXLOOP}; my ($rep) = 0; my ($start) = -1; my ($end) = 0; while (($rep < $ml) && (($start = index($out, "{\%")) >= 0) && (!$self->{ERROR})) { my ($contents) = ""; $rep++; $end = index($out, "\%}"); if ($end > $start) { my ($substr) = substr($out, $start + 2, $end - $start - 2); if (defined($ses->getValue("CAPTION_$substr"))) { $contents = $ses->getValue("CAPTION_$substr"); $contents = substr($out, 0, $start) . $contents; $contents .= substr($out, $end + 2); $out = $contents; } else { $self->{ERROR} = lprint("The variable") . " \"$substr\" " . lprint("have not produced a caption"); $self->{ERRORCODE} = 99; } } else { $self->{ERROR} = lprint("Found \"{\%\" without \"\%}\""); $self->{ERRORCODE} = 99; } } return $out; } sub ParseSelections { my ($self, $out, $doc, $ses) = @_; my ($ml) = $self->{MAXLOOP}; my ($rep) = 0; my ($start) = -1; my ($end) = 0; while (($rep < $ml) && (($start = index($out, "{\!")) >= 0) && (!$self->{ERROR})) { my ($contents) = ""; $rep++; $end = index($out, "\!}"); if ($end > $start) { my ($substr) = substr($out, $start + 2, $end - $start - 2); my ($var, $sel) = split(/\//, $substr, 2); if (defined($ses->getValue("SUBMITTED_$var"))) { my ($submitted) = $ses->getValue("SUBMITTED_$var"); my (@cases) = split(/\,/, $sel); my ($cell, %values); foreach $cell (@cases) { my ($val, $cap) = split(/\:/, $cell, 2); $values{$val} = $cap; } $contents = $values{$submitted}; $contents = substr($out, 0, $start) . $contents; $contents .= substr($out, $end + 2); $out = $contents; } else { $self->{ERROR} = lprint("The variable") . " \"$var\" " . lprint("has not been submitted"); $self->{ERRORCODE} = 99; } } else { $self->{ERROR} = lprint("Found \"{\!\" without \"\!}\""); $self->{ERRORCODE} = 99; } } return $out; } sub ParseDynamics { my ($self, $out) = @_; my ($doc) = $self->{DOCUMENT}; my ($ses) = $doc->{SESSION}; $out = $self->ParseSession($out, $doc, $ses); $out = $self->ParsePerl($out, $doc, $ses); $out = $self->ParseSelections($out, $doc, $ses); $out = $self->ParseCaptions($out, $doc, $ses); $out = $self->ParseVariables($out, $doc, $ses); # Progress bar, patch from Martin Ertl, 2004-10-28 $self->ParseProgress(\$out, $doc, $ses); return $out; } # Progress bar, patch from Martin Ertl, 2004-10-28 # (accepted fully minus white-space, some changes in indentation # /JP 2004-10-28 ) sub ParseProgress { my $this = shift; my $OutPtr = shift; my $Doc = shift; my $Ses = shift; # this sub is called more than once on a single survey page # -> calculate values only on first call if (exists $this->{__progressCache__}) { my $fraction = $this->{__progressCache__}->{fraction}; my $maxWidth = $this->{__progressCache__}->{maxWidth}; $$OutPtr =~ s//$fraction/g; $$OutPtr =~ s//$maxWidth/g; return; } # get the number of still answered questions from the session my $numAnswered = $Ses->getValue("__numAnswered__") || 0; # get a string to identify current file my $currentFile = $ENV{SCRIPT_NAME}; # check if current file is already on the stack # if this is true delete everything above and set # $numAnswered to the value where current file was found on the stack for (my $i = 0 ; $i < $numAnswered ; $i++) { my $stackFile = $Ses->getValue("__progressStack" . $i . "__", 1, __LINE__); if ($currentFile eq $stackFile) { my $temp = $numAnswered; $numAnswered = $i; while ($i < $temp) { $Ses->setValue("__progressStack" . $i . "__", ""); $i++; } last; } } # save some values to session $Ses->setValue("__numAnswered__", $numAnswered + 1); $Ses->setValue("__progressStack" . $numAnswered . "__", $currentFile); # get the progress information from PROGRES-parameter of SURVEY tag my $progresOption = $Doc->GetOption("PROGRES"); my ($questionsLeft, $maxWidth) = split(/\//, $progresOption); # calculate the progress my $divisor = $numAnswered + $questionsLeft; my $fraction = ($divisor) ? $numAnswered / $divisor : 0; $fraction *= $maxWidth; # set some variables to remember the values on next call of this sub $this->{__progressCache__}->{fraction} = $fraction; $this->{__progressCache__}->{maxWidth} = $maxWidth; # set variables in the survey file $$OutPtr =~ s//$fraction/g; $$OutPtr =~ s//$maxWidth/g; } sub ParseStyles { my ($self, $out) = @_; $out =~ s/\{b\}/\/g; $out =~ s/\{\/b\}/\<\/b\>/g; $out =~ s/\{i\}/\/g; $out =~ s/\{\/i\}/\<\/i\>/g; $out =~ s/\{u\}/\/g; $out =~ s/\{\/u\}/\<\/u\>/g; return $out; } sub ParseSpecial { my ($self, $out) = @_; $out =~ s/\{\-\}/\=/g; $out =~ s/\{\:\}/\;/g; $out =~ s/\{\'\}/\"/g; $out =~ s/\{\[\}/\/g; $out =~ s/\{\A\}/\&/g; return $out; } sub ParseColors { my ($self, $out) = @_; $out =~ s/\{red\}/\/g; $out =~ s/\{green\}/\/g; $out =~ s/\{blue\}/\/g; $out =~ s/\{yellow\}/\/g; $out =~ s/\{purple\}/\/g; $out =~ s/\{cyan\}/\/g; $out =~ s/\{black\}/\/g; $out =~ s/\{white\}/\/g; $out =~ s/\{\/red\}/\<\/span\>/g; $out =~ s/\{\/green\}/\<\/span\>/g; $out =~ s/\{\/blue\}/\<\/span\>/g; $out =~ s/\{\/yellow\}/\<\/span\>/g; $out =~ s/\{\/purple\}/\<\/span\>/g; $out =~ s/\{\/cyan\}/\<\/span\>/g; $out =~ s/\{\/black\}/\<\/span\>/g; $out =~ s/\{\/white\}/\<\/span\>/g; return $out; } sub ParseLinks { my ($self, $out) = @_; my ($start) = -1; my ($end) = 0; while (($start = index($out, "{\¤")) >= 0) { my ($contents) = ""; $end = index($out, "\¤}"); if ($end > $start) { my ($substr) = substr($out, $start + 2, $end - $start - 2); if (!-e $substr) { my ($path, $file) = $self->{DOCUMENT}->{FILE} =~ m/(.*)\/(.*)/; my ($delim) = "/"; if (!$ENV{"_SURVEY_SENSIBLE"}) { $delim = "\\"; } $file = $path . $delim . $substr; if (-e $file) { $substr = $file; } } if (!-r $substr) { $self->{ERROR} = lprint("Could not open") . " $substr " . lprint("for reading"); $self->{ERRORCODE} = 99; } else { open(FIL, $substr); my (@fil) =| GetOption("CAPTWIDTH") . "\"";
# modified to include the MULTICHOICE tag
# P. Sweatman Nov 01
if ($doc->GetTagParam($tagno, "TYPE") eq "CHOICE")
{
my ($eno) = $doc->GetTagParam($tagno, "ELEMENTS");
if ($doc->GetTagParam($tagno, "OTHERFIELD") ne "-1") { $eno++; }
$out .= " rowspan=\"" . $eno . "\"";
}
$out .= "> GetTagParam($tagno, "CAPTSTYLE") . "\">";
$out .= $doc->GetTagParam($tagno, "CAPTION");
$out .= " | \n";
}
else
{
$out .= $sp;
$out .= "