;
# close(FILE);
#
# foreach (@classList) { ## read through classlist and send e-mail
# ## message to all active students
# unless ($_ =~ /\S/) {next;} ## skip blank lines
# chomp;
# my @classListRecord=&getRecord($_);
# my ($studentID, $lastName, $firstName, $status, $comment, $section, $recitation, $email_address, $login_name)
# = @classListRecord;
# unless (&dropStatus($status)) {
# push (@studentID, $studentID);
# $fn{$studentID} = $firstName;
# $ln{$studentID} = $lastName;
# $section{$studentID} = $section;
# $recitation{$studentID} = $recitation;
# $status{$studentID} = $status;
# $email{$studentID} = $email_address;
# $login{$studentID} = $login_name;
# }
# }
} elsif ($r->param('To') eq 'studentID' && defined($r->param('studentID'))) {
@studentID = $r->param('studentID');
my ($studentID, $login_name);
#
# foreach $studentID (@studentID) {
# $login_name = $studentID_LoginName_Hash{$studentID};
# &attachCLRecord($login_name);
# $fn{$studentID} = CL_getStudentFirstName($login_name);
# $ln{$studentID} = CL_getStudentLastName($login_name);
# $section{$studentID} = CL_getClassSection($login_name);
# $recitation{$studentID} = CL_getClassRecitation($login_name);
# $status{$studentID} = CL_getStudentStatus($login_name);
# $email{$studentID} = CL_getStudentEmailAddress($login_name);
# $login{$studentID} = $login_name;
# }
} elsif ($r->param('To') eq 'all_students') {
@studentID = ();
my ($studentID, $login_name, $status);
# foreach $login_name (@availableStudents) {
# &attachCLRecord($login_name);
# $status = CL_getStudentStatus($login_name);
# next if &dropStatus($status);
# $studentID = CL_getStudentID($login_name);
# push(@studentID,$studentID);
#
# $fn{$studentID} = CL_getStudentFirstName($login_name);
# $ln{$studentID} = CL_getStudentLastName($login_name);
# $section{$studentID} = CL_getClassSection($login_name);
# $recitation{$studentID} = CL_getClassRecitation($login_name);
# $status{$studentID} = CL_getStudentStatus($login_name);
# $email{$studentID} = CL_getStudentEmailAddress($login_name);
# $login{$studentID} = $login_name;
# }
} else {
$self->submission_error('You didn\'t select any recipients. Make sure you select either all student in the course, individual students or a whole classlist.');
}
# my $mergeFile = '';
#
# #the radio button named 'merge' determines whether to take the selected mergefile
# #or one that was typed in. A error message is given if select one and use the other
# $mergeFile = $scoringDirectory . $r->param('mergeFiles')
# if ($r->param('merge') eq 'mergeFiles' && defined($r->param('mergeFiles')) && $r->param('mergeFiles') ne 'None');
#
# $mergeFile = $templateDirectory . $r->param('mergeFile')
# if ($r->param('merge') eq 'mergeFile' && defined($r->param('mergeFile')) && $r->param('mergeFile') !~ m|/$|); #does not end in a /
#
# if ($mergeFile =~ /^[~.]/ || $mergeFile =~ /\.\./) {
# $self->submission_error("For security reasons, you cannot specify a merge file from a directory higher than the email directory. Please specify a different file or move the needed file to the email directory");
# }
# if ($r->param('body') =~ /(\$COL\[.*?\])/ && !(-e $mergeFile)) {
# $self->submission_error("In order to use the \$COL[] you must specify a merge file. The file you specified does not exist. Also, make sure you selected the right checkbox.");
# }
#
my %mergeAArray = ();
# unless ($mergeFile eq '') {%mergeAArray = &delim2aa($mergeFile);}
#
#
# foreach my $studentID (@studentID) {
# @COL =();
# $SID = $studentID;
# $LN = defined $ln{$studentID} ? $ln{$studentID} :'';
# $FN = defined $fn{$studentID} ? $fn{$studentID} :'';
# $SECTION = defined $section{$studentID} ? $section{$studentID} :'';
# $RECITATION = defined $recitation{$studentID} ? $recitation{$studentID} :'';
# $EMAIL = defined $email{$studentID} ? $email{$studentID} :'';
# $STATUS =defined $status{$studentID} ? $status{$studentID} :'';
# $LOGIN = $login{$studentID};
#
# next if ($LOGIN =~ /^$practiceUser/); ## skip practice users
#
# if ($timeout_attempts >= $max_timeout_attempts) { ## have attemped to connect to smtp server
# ## the max allowed times. Now just collect
# ## data on emails not sent and exit
# ++$emails_not_sent;
# &log_error(\@exceeded_max_timeout,$FN,$LN,$EMAIL);
# next;
# }
#
# unless ((defined $mergeAArray{$studentID}) or ($mergeFile eq '')) {
# if ($cgi->param('no_record')) {
# ++$emails_not_sent;
# &log_error(\@no_record,$FN,$LN,$EMAIL);
# next;
# }
# }
# my ($dbString, @dbArray);
# if (defined $mergeAArray{$SID}) {
# $dbString = $mergeAArray{$SID}; ## get sid record from merge file
# @dbArray = &getRecord($dbString);
# unshift(@dbArray,$SID);
# unshift(@dbArray,""); ## note COL[1] is the first column
# @COL= @dbArray; ## put merge fields in COL array
# $endCol = @COL; ## \endCol-1 gives last field, etc
# }
# my $smtp;
# if ($smtp = Net::SMTP->new($Global::smtpServer, Timeout => $timeout_sec)) {} else {
# # &internal_error("Couldn't contact SMTP server.");
# ++$emails_not_sent;
# &log_error(\@timeout_problem,$FN,$LN,$EMAIL);
# ++$timeout_attempts;
# next;
# }
#
# $smtp->mail($smtpSender);
#
# if ( $smtp->recipient($EMAIL)) { # this one's okay, keep going
# if ( $smtp->data("To: $EMAIL\n" . output() ) ) {
# ++$emails_sent;
# } else {
# ++$emails_not_sent;
# &log_error(\@unknown_problem,$FN,$LN,$EMAIL);
# next;
# }
# # &internal_error("Unknown problem sending message data to SMTP server.");
# } else { # we have a problem with this address
# $smtp->reset;
# #&internal_error("SMTP server doesn't like this address: <$EMAIL>.");
# ++$emails_not_sent;
# &log_error(\@bad_email_addresses,$FN,$LN,$EMAIL);
# }
# $smtp->quit;
# }
# &success;
}
} #end initialize
# sub fieldEditHTML {
# my ($self, $fieldName, $value, $properties) = @_;
# my $size = $properties->{size};
# my $type = $properties->{type};
# my $access = $properties->{access};
# my $items = $properties->{items};
# my $synonyms = $properties->{synonyms};
#
#
# if ($access eq "readonly") {
# return $value;
# }
# if ($type eq "number" or $type eq "text") {
# return CGI::input({type=>"text", name=>$fieldName, value=>$value, size=>$size});
# }
# if ($type eq "enumerable") {
# my $matched = undef; # Whether a synonym match has occurred
#
# # Process synonyms for enumerable objects
# foreach my $synonym (keys %$synonyms) {
# if ($synonym ne "*" and $value =~ m/$synonym/) {
# $value = $synonyms->{$synonym};
# $matched = 1;
# }
# }
# if (!$matched and exists $synonyms->{"*"}) {
# $value = $synonyms->{"*"};
# }
# return CGI::popup_menu({
# name => $fieldName,
# values => [keys %$items],
# default => $value,
# labels => $items,
# });
# }
# }
sub title {
my $self = shift;
return 'Send mail to ' .$self->{ce}->{courseName};
}
sub path {
my $self = shift;
my $args = $_[-1];
my $ce = $self->{ce};
my $root = $ce->{webworkURLs}->{root};
my $courseName = $ce->{courseName};
return $self->pathMacro($args,
"Home" => "$root",
$courseName => "$root/$courseName",
'instructor' => "$root/$courseName/instructor",
"Send Mail to: $courseName" => '',
);
}
sub body {
my ($self, $setID) = @_;
my $response = (defined($self->{response}))? $self->{response} : '';
if ($response eq 'preview') {
$self->print_preview($setID);
} else {
$self->print_form($setID);
}
}
sub print_preview {
my ($self, $setID) = @_;
# get preview user
my $ur = $self->{db}->getUser($self->{preview_user});
# get merge file
my $merge_file = ( defined($self->{merge_file}) ) ? $self->{merge_file} : 'None';
my $delimiter = ',';
my $rh_merge_data = $self->read_merge_file("$merge_file", "$delimiter");
my ($msg, $preview_header) = $self->process_message($ur,$rh_merge_data);
my $recipients = join(" ",@{$self->{ra_send_to} });
return join("", '',$preview_header,$msg,"\n","\n",
'
',
CGI::p('Use browser back button to return from preview mode'),
CGI::h3('Emails to be sent to the following:'),
$recipients, "\n",
);
}
sub print_form {
my ($self, $setID) = @_;
my $r = $self->{r};
my $authz = $self->{authz};
my $user = $r->param('user');
my $db = $self->{db};
my $ce = $self->{ce};
my $root = $ce->{webworkURLs}->{root};
my $courseName = $ce->{courseName};
return CGI::em("You are not authorized to access the Instructor tools.") unless $authz->hasPermissions($user, "access_instructor_tools");
my $userTemplate = $db->newUser;
my $permissionLevelTemplate = $db->newPermissionLevel;
# This code will require changing if the permission and user tables ever have different keys.
my @users = @{ $self->{ra_users} };
my $ra_user_records = $self->{ra_user_records};
my %classlistLabels = ();# %$hr_classlistLabels;
foreach my $ur (@{ $ra_user_records }) {
$classlistLabels{$ur->user_id} = $ur->user_id.' '.$ur->last_name. ', '. $ur->first_name.' - '.$ur->section;
}
##############################################################################################################
my $from = $self->{from};
my $subject = $self->{subject};
my $replyTo = $self->{replyTo};
my $columns = $self->{columns};
my $rows = $self->{rows};
my $text = defined($self->{r_text}) ? ${ $self->{r_text} }: 'FIXME no text was produced by initialization!!';
my $input_file = $self->{input_file};
my $output_file = $self->{output_file};
my @sorted_messages = $self->get_message_file_names;
my @sorted_merge_files = $self->get_merge_file_names;
my $merge_file = ( defined($self->{merge_file}) ) ? $self->{merge_file} : 'None';
my $delimiter = ',';
my $rh_merge_data = $self->read_merge_file("$merge_file", "$delimiter");
my @merge_keys = keys %$rh_merge_data;
my $preview_user = $self->{preview_user};
my $preview_record = $db->getUser($preview_user);
#############################################################################################
print CGI::start_form({method=>"post", action=>$r->uri()});
print $self->hidden_authen_fields();
#############################################################################################
# begin upper table
#############################################################################################
print CGI::start_table({-border=>'2', -cellpadding=>'4'});
print CGI::Tr({-align=>'left',-valign=>'VCENTER'},
#############################################################################################
# first column
#############################################################################################
CGI::td("Message file: $input_file","\n",CGI::br(),
CGI::submit(-name=>'action', -value=>'Open'), ' ',"\n",
CGI::popup_menu(-name=>'openfilename',
-values=>\@sorted_messages,
-default=>$input_file
), "\n",CGI::br(),
"Save file to: $output_file","\n",CGI::br(),
"\n", 'From:',' ', CGI::textfield(-name=>"from", -size=>30, -value=>$from, -override=>1),
"\n", CGI::br(),'Reply-To: ', CGI::textfield(-name=>"replyTo", -size=>30, -value=>$replyTo, -override=>1),
"\n", CGI::br(),'Subject: ', CGI::br(), CGI::textarea(-name=>'subject', -default=>$subject, -rows=>3,-columns=>35, -override=>1),
),
#############################################################################################
# second column
#############################################################################################
CGI::td({-align=>'left'},
CGI::radio_group(-name=>'radio', -values=>['all_students','studentID'],
-labels=>{all_students=>'All active students',studentID => 'Select recipients'},
-default=>'studentID',
-linebreak=>1),
CGI::br(),
CGI::popup_menu(-name=>'classList',
-values=>\@users,
-labels=>\%classlistLabels,
-size => 10,
-multiple => 1,
-default=>$user
),
),
#############################################################################################
# third column
#############################################################################################
CGI::td({align=>'left'},
"Merge file is: $merge_file", CGI::br(),
CGI::submit(-name=>'action', -value=>'Choose merge file'),CGI::br(),
CGI::popup_menu(-name=>'merge_file',
-values=>\@sorted_merge_files,
-default=>$merge_file,
), "\n",CGI::hr(),CGI::br(),
CGI::submit(-name=>'action', -value=>'preview',-label=>'Preview')," email to ",
CGI::popup_menu(-name=>'preview_user',
-values=>\@users,
#-labels=>\%classlistLabels,
-default=>$preview_user,
),
CGI::hr(),
CGI::submit(-name=>'action', -value=>'resize', -label=>'Resize message window'),CGI::br(),
" Rows: ", CGI::textfield(-name=>'rows', -size=>3, -value=>$rows),
" Columns: ", CGI::textfield(-name=>'columns', -size=>3, -value=>$columns),
CGI::br(),CGI::br(),
#show available macros
CGI::popup_menu(
-name=>'dummyName',
-values=>['', '$SID', '$FN', '$LN', '$SECTION', '$RECITATION','$STATUS', '$EMAIL', '$LOGIN', '$COL[3]', '$COL[-1]'],
-labels=>{''=>'list of insertable macros',
'$SID'=>'$SID - Student ID',
'$FN'=>'$FN - First name',
'$LN'=>'$LN - Last name',
'$SECTION'=>'$SECTION',
'$RECITATION'=>'$RECITATION',
'$STATUS'=>'$STATUS - C, Audit, Drop, etc.',
'$EMAIL'=>'$EMAIL - Email address',
'$LOGIN'=>'$LOGIN - Login',
'$COL[3]'=>'$COL[3] - 3rd col',
'$COL[-1]'=>'$COL[-1] - Last column'
}
), "\n",
),
); # end Tr
print CGI::end_table();
#############################################################################################
# end upper table
#############################################################################################
# show merge file
# print "",(map {$_ =~s/\s/\./g;$_} map {sprintf('%-8.8s',$_);} 0..8),"";
# print CGI::popup_menu(
# -name=>'dummyName2',
# -values=>\@merge_keys,
# -labels=>$rh_merge_data,
# -multiple=>1,
# -size =>2,
#
# ), "\n",CGI::br();
# warn "merge keys ", join( " ",@merge_keys);
#############################################################################################
# merge file fragment and message text area field
#############################################################################################
my @tmp2= @{$rh_merge_data->{ $db->getUser($preview_user)->student_id } };
print CGI::pre("",data_format(0..($#tmp2)),"\n", data_format(@tmp2));
#create a textbox with the subject and a textarea with the message
#print actual body of message
print "\n", CGI::p( $self->{message}) if defined($self->{message});
print "\n", CGI::p( CGI::textarea(-name=>'body', -default=>$text, -rows=>$rows, -columns=>$columns, -override=>1));
#############################################################################################
# action button table
#############################################################################################
print CGI::table( { -border=>2,-cellpadding=>4},
CGI::Tr(
CGI::td( CGI::submit(-name=>'action', -value=>'Send Email') ), "\n",
CGI::td(CGI::submit(-name=>'action', -value=>'Save')," to $output_file"), " \n",
CGI::td(CGI::submit(-name=>'action', -value=>'Save as:'),
CGI::textfield(-name=>'savefilename', -size => 20, -value=> "$output_file", -override=>1)
), "\n",
CGI::td(CGI::submit(-name=>'action', -value=>'Save as Default')),
)
);
##############################################################################################################
print CGI::end_form();
return "";
}
##############################################################################
# Utility methods
##############################################################################
sub submission_error {
my $self = shift;
my $msg = join( " ", @_);
$self->{submitError}= $msg; #CGI::b(HTML::Entities::encode($msg));
# qq{Please hit the "Back" button on your browser to
# try again, or notify your web master
# if you believe this message is in error.
# };
return;
}
sub saveProblem {
my $self = shift;
my ($body, $probFileName)= @_;
local(*PROBLEM);
open (PROBLEM, ">$probFileName") ||
$self->submission_error("Could not open $probFileName for writing.
Check that the permissions for this problem are 660 (-rw-rw----)");
print PROBLEM $body;
close PROBLEM;
chmod 0660, "$probFileName" ||
$self->submission_error("
CAN'T CHANGE PERMISSIONS ON FILE $probFileName");
}
sub read_input_file {
my $self = shift;
my $filePath = shift;
my ($text, @text);
my $header = '';
my ($subject, $from, $replyTo);
local(*FILE);
if (-e "$filePath" and -r "$filePath") {
open FILE, "$filePath" || do { $self->submission_error("Can't open $filePath"); return};
while ($header !~ s/Message:\s*$//m and not eof(FILE)) {
$header .= ;
}
$text = join( '', );
$text =~ s/^\s*//; # remove initial white space if any.
$header =~ /^From:\s(.*)$/m;
$from = $1 or $from = $self->{defaultFrom};
$header =~ /^Reply-To:\s(.*)$/m;
$replyTo = $1 or $replyTo = $self->{defaultReply};
$header =~ /^Subject:\s(.*)$/m;
$subject = $1;
} else {
$from = $self->{defaultFrom};
$replyTo = $self->{defaultReply};
$text = (-e "$filePath") ? "FIXME file $filePath can't be read" :"FIXME file $filePath doesn't exist";
$subject = "FIXME default subject";
}
return ($from, $replyTo, $subject, \$text);
}
sub get_message_file_names {
my $self = shift;
my $emailDirectory = $self->{ce}->{courseDirs}->{email};
#get all message files and create a list
local(*EMAILDIR);
opendir( EMAILDIR, $emailDirectory )|| die "Can't access directory $emailDirectory. Please check that webserver has permission to read this directory.";
my @messageFiles = grep /\.msg$/, readdir EMAILDIR; #all message files
closedir EMAILDIR;
return sort @messageFiles;
}
sub get_merge_file_names {
my $self = shift;
my $scoringDirectory = $self->{ce}->{courseDirs}->{scoring};
#get all message files and create a list
local(*SCORINGDIR);
opendir( SCORINGDIR, $scoringDirectory )|| die "Can't access directory $scoringDirectory.",
"Please check that webserver has permission to read this directory.";
my @mergeFiles = grep( /\.csv$/, readdir SCORINGDIR); #all message files
closedir SCORINGDIR;
@mergeFiles = sort @mergeFiles;
# warn "FIXME scoring directory $scoringDirectory merge Files", join(" ", @mergeFiles);
unshift(@mergeFiles, 'None');
return @mergeFiles;
}
sub read_merge_file {
my $self = shift;
my $fileName = shift;
my $delimiter = shift;
$delimiter = ',' unless defined($delimiter);
my $scoringDirectory= $self->{ce}->{courseDirs}->{scoring};
my $filePath = "$scoringDirectory/$fileName";
# Takes a delimited file as a parameter and returns an
# associative array with the first field as the key.
# Blank lines are skipped. White space is removed
my(@dbArray,$key,%assocArray,$dbString);
local(*FILE);
open(FILE, "$filePath") or $self->submission_error("Can't open file $filePath");
my $index=0;
while (){
unless ($_ =~ /\S/) {next;} ## skip blank lines
chomp;
@{$dbArray[$index]} =$self->getRecord($_,$delimiter);
$key =$dbArray[$index][0];
#@dbArray = map {$_ =~s/\s/\./g;$_} map {sprintf('%-8.8s',$_);} @dbArray;
#$dbString = join(" | ",@dbArray);
$assocArray{$key}=$dbArray[$index];
$index++;
}
close(FILE);
return \%assocArray;
}
sub getRecord {
my $self = shift;
my $line = shift;
my $delimiter = shift;
$delimiter = ',' unless defined($delimiter);
# Takes a delimited line as a parameter and returns an
# array. Note that all white space is removed. If the
# last field is empty, the last element of the returned
# array is also empty (unlike what the perl split command
# would return). E.G. @lineArray=&getRecord(\$delimitedLine).
my(@lineArray);
$line.=$delimiter; # add 'A' to end of line so that
# last field is never empty
@lineArray = split(/\s*${delimiter}\s*/,$line);
$lineArray[0] =~s/^\s*//; # remove white space from first element
@lineArray;
}
sub process_message {
my $self = shift;
my $ur = shift;
my $rh_merge_data = shift;
my $text = defined($self->{r_text}) ? ${ $self->{r_text} }:
'FIXME no text was produced by initialization!!';
#user macros that can be used in the email message
my $SID = $ur->student_id;
my $FN = $ur->first_name;
my $LN = $ur->last_name;
my $SECTION = $ur->section;
my $RECITATION = $ur->recitation;
my $STATUS = $ur->status;
my $EMAIL = $ur->email_address;
my $LOGIN = $ur->user_id;
# get record from merge file
# FIXME this is inefficient. The info should be cached
my @COL = @{$rh_merge_data->{$SID} };
my $endCol = @COL;
# for safety, only evaluate special variables
my $tmp = $text;
$tmp =~ s/(\$SID)/eval($1)/ge;
$tmp =~ s/(\$LN)/eval($1)/ge;
$tmp =~ s/(\$FN)/eval($1)/ge;
$tmp =~ s/(\$STATUS)/eval($1)/ge;
$tmp =~ s/(\$SECTION)/eval($1)/ge;
$tmp =~ s/(\$RECITATION)/eval($1)/ge;
$tmp =~ s/(\$EMAIL)/eval($1)/ge;
$tmp =~ s/(\$LOGIN)/eval($1)/ge;
$tmp =~ s/\$COL\[ *-/\$COL\[$endCol-/g;
$tmp =~ s/(\$COL\[.*?\])/eval($1)/ge;
my $preview_header = CGI::pre("",data_format(0..($#COL)),"\n", data_format(@COL)).
CGI::h3( "This sample mail would be sent to $EMAIL");
my $msg = join("",
"To: " , $ur->email_address,"\n",
"From: " , $self->{from} , "\n" ,
"Reply-To: " , $self->{replyTo} , "\n" ,
"Subject: " , $self->{subject} , "\n" ,"\n" ,
$tmp , "\n"
);
$msg =~ s/\r//g;
return $msg, $preview_header;
}
sub data_format {
map {$_ =~s/\s/\./g;$_} map {sprintf('%-8.8s',$_);} @_;
}
1;