################################################################################
# WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
# $Id$
################################################################################
package WeBWorK::Utils;
use base qw(Exporter);
=head1 NAME
WeBWorK::Utils - useful utilities used by other WeBWorK modules.
=cut
use strict;
use warnings;
#use Apache::DB;
use Date::Format;
use Date::Parse;
use Errno;
use File::Path qw(rmtree);
use constant MKDIR_ATTEMPTS => 10;
our @EXPORT = ();
our @EXPORT_OK = qw(
runtime_use
readFile
readDirectory
formatDateTime
parseDateTime
writeLog
writeCourseLog
writeTimingLogEntry
list2hash
max
dbDecode
dbEncode
decodeAnswers
encodeAnswers
ref2string
sortByName
makeTempDirectory
removeTempDirectory
pretty_print_rh
cryptPassword
);
sub runtime_use($) {
return unless @_;
eval "package Main; require $_[0]; import $_[0]";
die $@ if $@;
}
#sub backtrace {
# my ($style) = @_;
# $style = "warn" unless $style;
# my @bt = DB->backtrace;
# shift @bt; # Remove "backtrace" from the backtrace;
# if ($style eq "die") {
# die join "\n", @bt;
# } elsif ($style eq "warn") {
# warn join "\n", @bt;
# } elsif ($style eq "print") {
# print join "\n", @bt;
# } elsif ($style eq "return") {
# return @bt;
# }
#}
sub readFile($) {
my $fileName = shift;
local $/ = undef; # slurp the whole thing into one string
open my $dh, "<", $fileName
or die "failed to read file $fileName: $!";
my $result = <$dh>;
close $dh;
return $result;
}
sub readDirectory($) {
my $dirName = shift;
opendir my $dh, $dirName
or die "Failed to read directory $dirName. Please check that",
"webserver has permission to read this directory. $!";
my @result = readdir $dh;
close $dh;
return @result;
}
sub formatDateTime($) {
my $dateTime = shift;
# "standard" WeBWorK date/time format (for set definition files):
# %m month number, starting with 01
# %d numeric day of the month, with leading zeros (eg 01..31)
# %y year (2 digits)
# %I hour, 12 hour clock, leading 0's)
# %M minute, leading 0's
# %P am or pm (Yes %p and %P are backwards :)
#return time2str("%m/%d/%y %I:%M%P", $dateTime);
return time2str("%m/%d/%y at %I:%M%P", $dateTime);
}
sub parseDateTime($) {
my $string = shift;
return str2time($string);
}
sub writeLog($$@) {
my ($ce, $facility, @message) = @_;
unless ($ce->{webworkFiles}->{logs}->{$facility}) {
warn "There is no log file for the $facility facility defined.\n";
return;
}
my $logFile = $ce->{webworkFiles}->{logs}->{$facility};
local *LOG;
if (open LOG, ">>", $logFile) {
print LOG "[", time2str("%a %b %d %H:%M:%S %Y", time), "] @message\n";
close LOG;
} else {
warn "failed to open $logFile for writing: $!";
}
}
sub writeCourseLog($$@) {
my ($ce, $facility, @message) = @_;
unless ($ce->{courseFiles}->{logs}->{$facility}) {
warn "There is no course log file for the $facility facility defined.\n";
return;
}
my $logFile = $ce->{courseFiles}->{logs}->{$facility};
local *LOG;
if (open LOG, ">>", $logFile) {
print LOG "[", time2str("%a %b %d %H:%M:%S %Y", time), "] @message\n";
close LOG;
} else {
warn "failed to open $logFile for writing: $!";
}
}
# $ce - a WeBWork::CourseEnvironment object
# $function - fully qualified function name
# $details - any information, do not use the characters '[' or ']'
# $beginEnd - the string "begin", "intermediate", or "end"
# use the intermediate step begun or completed for INTERMEDIATE
# use an empty string for $details when calling for END
sub writeTimingLogEntry($$$$) {
my ($ce, $function, $details, $beginEnd) = @_;
return unless defined $ce->{webworkFiles}->{logs}->{timing};
$beginEnd = ($beginEnd eq "begin") ? ">" : ($beginEnd eq "end") ? "<" : "-";
writeLog($ce, "timing", "$$ ".time." $beginEnd $function [$details]");
}
sub list2hash {
map {$_ => "0"} @_;
}
sub max {
my $soFar;
foreach my $item (@_) {
$soFar = $item unless defined $soFar;
if ($item > $soFar) {
$soFar = $item;
}
}
return defined $soFar ? $soFar : 0;
}
sub decodeAnswers($) {
my $string = shift;
return unless defined $string and $string;
my @array = split m/##/, $string;
$array[$_] =~ s/\\#\\/#/g foreach 0 .. $#array;
push @array, "" if @array%2;
return @array; # it's actually a hash ;)
}
sub encodeAnswers(\%\@) {
my %hash = %{ shift() };
my @order = @{ shift() };
my $string;
foreach my $name (@order) {
my $value = defined $hash{$name} ? $hash{$name} : "";
$name =~ s/#/\\#\\/g; # this is a WEIRD way to escape things
$value =~ s/#/\\#\\/g; # and it's not my fault!
if ($value =~ m/\\$/) {
# if the value ends with a backslash, string2hash will
# interpret that as a normal escape sequence (not part
# of the weird pound escape sequence) if the next
# character is &. So we have to protect against this.
# will adding a spcae at the end of the last answer
# hurt anything? i don't think so...
$value .= " ";
}
$string .= "$name##$value##"; # this is also not my fault
}
$string =~ s/##$//; # remove last pair of hashs
return $string;
}
sub ref2string($;$);
sub ref2string($;$) {
my $ref = shift;
my $dontExpand = shift || {};
my $refType = ref $ref;
my $result;
if ($refType and not $dontExpand->{$refType}) {
my $baseType = refBaseType($ref);
$result .= '' . $refType;
$result .= " ($baseType)" if $baseType and $refType ne $baseType;
$result .= ":
";
$result .= '
| $_ | "; $result .= "" . ref2string($hash{$_}, $dontExpand) . " | "; $result .= "|
| $_ | "; $result .= "".$FIELDS[$_]." | " if @FIELDS; $result .= "" . ref2string($array[$_], $dontExpand) . " | "; $result .= "
| $scalar | "; $result .= "||
| $ref | "; $result .= "