[system] / branches / rel-2-3-dev / webwork2 / lib / WeBWorK / ContentGenerator / Instructor / SendMail.pm Repository:
ViewVC logotype

Annotation of /branches/rel-2-3-dev/webwork2/lib/WeBWorK/ContentGenerator/Instructor/SendMail.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2786 - (view) (download) (as text)
Original Path: trunk/webwork2/lib/WeBWorK/ContentGenerator/Instructor/SendMail.pm

1 : sh002i 1663 ################################################################################
2 :     # WeBWorK Online Homework Delivery System
3 :     # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4 : apizer 2786 # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/SendMail.pm,v 1.36 2004/09/14 18:55:58 apizer Exp $
5 : sh002i 1663 #
6 :     # This program is free software; you can redistribute it and/or modify it under
7 :     # the terms of either: (a) the GNU General Public License as published by the
8 :     # Free Software Foundation; either version 2, or (at your option) any later
9 :     # version, or (b) the "Artistic License" which comes with this package.
10 :     #
11 :     # This program is distributed in the hope that it will be useful, but WITHOUT
12 :     # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 :     # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
14 :     # Artistic License for more details.
15 :     ################################################################################
16 :    
17 : gage 1368 package WeBWorK::ContentGenerator::Instructor::SendMail;
18 :     use base qw(WeBWorK::ContentGenerator::Instructor);
19 :    
20 :     =head1 NAME
21 :    
22 :     WeBWorK::ContentGenerator::Instructor::SendMail - Entry point for User-specific data editing
23 :    
24 :     =cut
25 :    
26 :     use strict;
27 :     use warnings;
28 :     use CGI qw();
29 : sh002i 1646 #use HTML::Entities;
30 : gage 1373 use Mail::Sender;
31 : mschmitt 2203 use WeBWorK::HTML::ScrollingRecordList qw/scrollingRecordList/;
32 : mschmitt 2207 use WeBWorK::Utils::FilterRecords qw/filterRecords/;
33 : gage 1368
34 : gage 2050 my $REFRESH_RESIZE_BUTTON = "Set preview to: "; # handle submit value idiocy
35 : gage 1368 sub initialize {
36 :     my ($self) = @_;
37 : gage 1928 my $r = $self->r;
38 :     my $db = $r->db;
39 :     my $ce = $r->ce;
40 :     my $authz = $r->authz;
41 :     my $user = $r->param('user');
42 : gage 1368
43 : mschmitt 2207 my @selected_filters;
44 :     if (defined ($r->param('classList!filter'))){ @selected_filters = $r->param('classList!filter');}
45 :     else {@selected_filters = ("all");}
46 :    
47 : toenail 2320
48 :     # Check permissions
49 :     return unless $authz->hasPermissions($user, "access_instructor_tools");
50 :     return unless $authz->hasPermissions($user, "send_mail");
51 :    
52 : gage 1369 #############################################################################################
53 :     # gather directory data
54 :     #############################################################################################
55 :     my $emailDirectory = $ce->{courseDirs}->{email};
56 :     my $scoringDirectory = $ce->{courseDirs}->{scoring};
57 :     my $templateDirectory = $ce->{courseDirs}->{templates};
58 : gage 1368
59 : mschmitt 2203 my $action = $r->param('action') ;
60 : gage 1369 my $openfilename = $r->param('openfilename');
61 :     my $savefilename = $r->param('savefilename');
62 : gage 1370
63 :    
64 :     #FIXME get these values from global course environment (see subroutines as well)
65 :     my $default_msg_file = 'default.msg';
66 :     my $old_default_msg_file = 'old_default.msg';
67 :    
68 : gage 1372
69 : dpvc 2388 # get user record
70 :     my $ur = $self->{db}->getUser($user);
71 :    
72 : gage 1370 # store data
73 : dpvc 2388 $self->{defaultFrom} = $ur->email_address . " (".$ur->first_name." ".$ur->last_name.")";
74 :     $self->{defaultReply} = $ur->email_address;
75 :     $self->{defaultSubject} = $self->r->urlpath->arg("courseID") . " notice";
76 :    
77 : gage 1370 $self->{rows} = (defined($r->param('rows'))) ? $r->param('rows') : $ce->{mail}->{editor_window_rows};
78 :     $self->{columns} = (defined($r->param('columns'))) ? $r->param('columns') : $ce->{mail}->{editor_window_columns};
79 :     $self->{default_msg_file} = $default_msg_file;
80 :     $self->{old_default_msg_file} = $old_default_msg_file;
81 : gage 1372 $self->{merge_file} = (defined($r->param('merge_file' ))) ? $r->param('merge_file') : 'None';
82 : gage 1376 $self->{preview_user} = (defined($r->param('preview_user'))) ? $r->param('preview_user') : $user;
83 : gage 1372
84 :    
85 :     #############################################################################################
86 :     # gather database data
87 :     #############################################################################################
88 :     # FIXME this might be better done in body? We don't always need all of this data. or do we?
89 : gage 1773 my @users = $db->listUsers;
90 : mschmitt 2203 my @Users = $db->getUsers(@users);
91 : gage 1372 my @user_records = ();
92 : mschmitt 2203
93 :     ## Mark's code to prefilter userlist
94 :    
95 :    
96 :     my (@viewable_sections,@viewable_recitations);
97 :    
98 :     if (defined @{$ce->{viewable_sections}->{$user}})
99 :     {@viewable_sections = @{$ce->{viewable_sections}->{$user}};}
100 :     if (defined @{$ce->{viewable_recitations}->{$user}})
101 :     {@viewable_recitations = @{$ce->{viewable_recitations}->{$user}};}
102 :    
103 :     if (@viewable_sections or @viewable_recitations){
104 :     foreach my $student (@Users){
105 :     my $keep = 0;
106 :     foreach my $sec (@viewable_sections){
107 :     if ($student->section() eq $sec){$keep = 1;}
108 :     }
109 :     foreach my $rec (@viewable_recitations){
110 :     if ($student->recitation() eq $rec){$keep = 1;}
111 :     }
112 :     if ($keep) {push @user_records, $student;}
113 :     }
114 : gage 1667 }
115 : mschmitt 2203 else {@user_records = @Users;}
116 :    
117 :     ## End Mark's code
118 :    
119 :     # foreach my $userName (@users) {
120 :     # my $userRecord = $db->getUser($userName); # checked
121 :     # die "record for user $userName not found" unless $userRecord;
122 :     # push(@user_records, $userRecord);
123 :     # }
124 : gage 1720 ###########################
125 :     # Sort the users for presentation in the select list
126 :     ###########################
127 : mschmitt 2203 # if (defined $r->param("sort_by") ) {
128 :     # my $sort_method = $r->param("sort_by");
129 :     # if ($sort_method eq 'section') {
130 :     # @user_records = sort { (lc($a->section) cmp lc($b->section)) || (lc($a->last_name) cmp lc($b->last_name)) } @user_records;
131 :     # } elsif ($sort_method eq 'recitation') {
132 :     # @user_records = sort { (lc($a->recitation) cmp lc($b->recitation)) || (lc($a->last_name) cmp lc($b->last_name)) } @user_records;
133 :     # } elsif ($sort_method eq 'alphabetical') {
134 :     # @user_records = sort { (lc($a->last_name) cmp lc($b->last_name)) } @user_records;
135 :     # } elsif ($sort_method eq 'id' ) {
136 :     # @user_records = sort { $a->user_id cmp $b->user_id } @user_records;
137 :     # }
138 :     # } else {
139 :     # @user_records = sort { $a->user_id cmp $b->user_id } @user_records;
140 :     # }
141 : gage 1372
142 : gage 1720
143 :     # replace the user names by a sorted version.
144 :     @users = map {$_->user_id} @user_records;
145 : gage 1372 # store data
146 :     $self->{ra_users} = \@users;
147 :     $self->{ra_user_records} = \@user_records;
148 :    
149 :     #############################################################################################
150 :     # gather list of recipients
151 :     #############################################################################################
152 :     my @send_to = ();
153 :     #FIXME this (radio) is a lousy name
154 :     my $recipients = $r->param('radio');
155 : gage 1375 if (defined($recipients) and $recipients eq 'all_students') { #only active students #FIXME status check??
156 : mschmitt 2207
157 :     ## Add code so that only people who pass the current filters are added to our list of recipients.
158 :     # @user_records = filterRecords({filter=\@selected_filters},@user_records);
159 :     # I wasn't able to make this work
160 :     # I edited the selection button to make that clear.
161 :     #
162 :    
163 : gage 1372 foreach my $ur (@user_records) {
164 :     push(@send_to,$ur->user_id) if $ur->status eq 'C' and not($ur->user_id =~ /practice/);
165 :     }
166 : gage 1375 } elsif (defined($recipients) and $recipients eq 'studentID' ) {
167 : gage 1372 @send_to = $r->param('classList');
168 :     } else {
169 : gage 1375 # no recipients have been defined -- probably the first time on the page
170 : gage 1372 }
171 :     $self->{ra_send_to} = \@send_to;
172 : gage 1370 #################################################################
173 :     # Check the validity of the input file name
174 :     #################################################################
175 :     my $input_file = '';
176 :     #make sure an input message file was submitted and exists
177 :     #else use the default message
178 :     if ( defined($openfilename) ) {
179 :     if ( -e "${emailDirectory}/$openfilename") {
180 :     if ( -R "${emailDirectory}/$openfilename") {
181 :     $input_file = $openfilename;
182 : gage 1369 } else {
183 : toenail 2320 $self->addbadmessage(CGI::p(join("",
184 : gage 1370 "The file ${emailDirectory}/$openfilename is not readable by the webserver.",CGI::br(),
185 :     "Check that it's permissions are set correctly.",
186 : toenail 2320 )));
187 : gage 1369 }
188 :     } else {
189 : gage 1370 $input_file = $default_msg_file;
190 : toenail 2320 $self->addbadmessage(CGI::p(join("",
191 : gage 1370 "The file ${emailDirectory}/$openfilename cannot be found.",CGI::br(),
192 :     "Check whether it exists and whether the directory $emailDirectory can be read by the webserver.",CGI::br(),
193 :     "Using contents of the default message $default_msg_file instead.",
194 : toenail 2320 )));
195 : gage 1369 }
196 : gage 1370 } else {
197 : gage 1372 $input_file = $default_msg_file;
198 : gage 1370 }
199 : gage 1372 $self->{input_file} =$input_file;
200 : gage 1370
201 :     #################################################################
202 :     # Determine the file name to save message into
203 :     #################################################################
204 : gage 1371 my $output_file = 'FIXME no output file specified';
205 : gage 1370 if (defined($action) and $action eq 'Save as Default') {
206 :     $output_file = $default_msg_file;
207 : toenail 2084 } elsif ( defined($action) and ($action =~/save/i)) {
208 :     if (defined($savefilename) and $savefilename ) {
209 :     $output_file = $savefilename;
210 :     } else {
211 : toenail 2320 $self->addbadmessage(CGI::p("No filename was specified for saving! The message was not saved."));
212 : toenail 2084 }
213 : gage 1370 } elsif ( defined($input_file) ) {
214 :     $output_file = $input_file;
215 :     }
216 : gage 1953
217 : gage 1370 #################################################################
218 :     # Sanity check on save file name
219 :     #################################################################
220 :    
221 :     if ($output_file =~ /^[~.]/ || $output_file =~ /\.\./) {
222 : toenail 2320 $self->addbadmessage(CGI::p("For security reasons, you cannot specify a message file from a directory",
223 : toenail 2084 "higher than the email directory (you can't use ../blah/blah for example). ",
224 : toenail 2320 "Please specify a different file or move the needed file to the email directory",));
225 : toenail 2084 }
226 : gage 1371 unless ($output_file =~ m|\.msg$| ) {
227 : toenail 2320 $self->addbadmessage(CGI::p("Invalid file name.",
228 : gage 1371 "The file name \"$output_file\" does not have a \".msg\" extension",
229 :     "All email file names must end in the extension \".msg\"",
230 :     "choose a file name with a \".msg\" extension.",
231 : toenail 2320 "The message was not saved.",));
232 : gage 1371 }
233 : toenail 2084
234 : gage 1371 $self->{output_file} = $output_file; # this is ok. It will be put back in the text input box for re-editing.
235 : gage 1370
236 : toenail 2084
237 : gage 1370 #############################################################################################
238 :     # Determine input source
239 :     #############################################################################################
240 : mschmitt 2203 #warn "Action = $action";
241 :     my $input_source;
242 :     if ($action){
243 :     $input_source = ( defined( $r->param('body') ) and $action ne 'Open' ) ? 'form' : 'file';}
244 :     else { $input_source = ( defined($r->param('body')) ) ? 'form' : 'file';}
245 : gage 1953
246 : gage 1370 #############################################################################################
247 :     # Get inputs
248 :     #############################################################################################
249 :     my($from, $replyTo, $r_text, $subject);
250 :     if ($input_source eq 'file') {
251 : gage 1953
252 : gage 1370 ($from, $replyTo,$subject,$r_text) = $self->read_input_file("$emailDirectory/$input_file");
253 :    
254 : gage 1953
255 : gage 1370 } elsif ($input_source eq 'form') {
256 :     # read info from the form
257 :     # bail if there is no message body
258 : gage 1369
259 : gage 1370 $from = $r->param('from');
260 :     $replyTo = $r->param('replyTo');
261 :     $subject = $r->param('subject');
262 :     my $body = $r->param('body');
263 :     # Sanity check: body must contain non-white space
264 : toenail 2320 $self->addbadmessage(CGI::p('You didn\'t enter any message.')) unless ($r->param('body') =~ /\S/);
265 : gage 1370 $r_text = \$body;
266 : gage 1369
267 : gage 1370 }
268 :     # store data
269 :     $self->{from} = $from;
270 :     $self->{replyTo} = $replyTo;
271 :     $self->{subject} = $subject;
272 :     $self->{r_text} = $r_text;
273 :    
274 :    
275 : gage 1372
276 :     ###################################################################################
277 :     #Determine the appropriate script action from the buttons
278 :     ###################################################################################
279 :     # first time actions
280 :     # open new file
281 :     # open default file
282 :     # choose merge file actions
283 :     # chose merge button
284 :     # option actions
285 :     # 'reset rows'
286 :    
287 :     # save actions
288 :     # "save" button
289 :     # "save as" button
290 :     # "save as default" button
291 :     # preview actions
292 :     # 'preview' button
293 :     # email actions
294 :     # 'entire class'
295 :     # 'selected studentIDs'
296 :     # error actions (various)
297 :    
298 :    
299 : gage 1369 #############################################################################################
300 : gage 1370 # if no form is submitted, gather data needed to produce the mail form and return
301 : gage 1369 #############################################################################################
302 : gage 1372 my $to = $r->param('To');
303 :     my $script_action = '';
304 :    
305 :    
306 : gage 1773 if(not defined($action) or $action eq 'Open' or $action eq $REFRESH_RESIZE_BUTTON or $action eq 'Sort by'
307 : gage 1375 or $action eq 'Set merge file to:' ){
308 : gage 1953
309 : gage 1369 return '';
310 :     }
311 :    
312 :    
313 :    
314 :    
315 :    
316 :     #############################################################################################
317 : gage 1370 # If form is submitted deal with filled out forms
318 : gage 1369 # and various actions resulting from different buttons
319 :     #############################################################################################
320 :    
321 : gage 1372
322 : gage 1370 if ($action eq 'Save' or $action eq 'Save as:' or $action eq 'Save as Default') {
323 :    
324 :     # warn "FIXME Saving files action = $action outputFileName=$output_file";
325 : gage 1369
326 : gage 1370 #################################################################
327 :     # construct message body
328 :     #################################################################
329 :     my $temp_body = ${ $r_text };
330 :     $temp_body =~ s/\r\n/\n/g;
331 :     $temp_body = join("",
332 :     "From: $from \nReply-To: $replyTo\n" ,
333 :     "Subject: $subject\n" ,
334 :     "Message: \n $temp_body");
335 :     # warn "FIXME from $from | subject $subject |reply $replyTo|msg $temp_body";
336 :     #################################################################
337 :     # overwrite protection
338 :     #################################################################
339 :     if ($action eq 'Save as:' and -e "$emailDirectory/$output_file") {
340 : toenail 2320 $self->addbadmessage(CGI::p("The file $emailDirectory/$output_file already exists and cannot be overwritten",
341 :     "The message was not saved"));
342 : toenail 2084 return;
343 : gage 1370 }
344 :    
345 :     #################################################################
346 :     # Back up existing file?
347 :     #################################################################
348 : gage 1371 if ($action eq 'Save as Default' and -e "$emailDirectory/$default_msg_file") {
349 :     rename("$emailDirectory/$default_msg_file","$emailDirectory/$old_default_msg_file") or
350 :     die "Can't rename $emailDirectory/$default_msg_file to $emailDirectory/$old_default_msg_file ",
351 :     "Check permissions for webserver on directory $emailDirectory. $!";
352 : toenail 2320 $self->addgoodmessage(CGI::p("Backup file <code>$emailDirectory/$old_default_msg_file</code> created." . CGI::br()));
353 : gage 1370 }
354 :     #################################################################
355 :     # Save the message
356 :     #################################################################
357 : toenail 2084 $self->saveProblem($temp_body, "${emailDirectory}/$output_file" ) unless ($output_file =~ /^[~.]/ || $output_file =~ /\.\./ || not $output_file =~ m|\.msg$|);
358 :     unless ( $self->{submit_message} or not -w "${emailDirectory}/$output_file" ) { # if there are no errors report success
359 : toenail 2320 $self->addgoodmessage(CGI::p("Message saved to file <code>${emailDirectory}/$output_file</code>."));
360 : gage 1953 }
361 :    
362 : gage 2050 } elsif ($action eq 'Preview message') {
363 : gage 1372 $self->{response} = 'preview';
364 : gage 1370
365 :     } elsif ($action eq 'Send Email') {
366 : gage 1373 $self->{response} = 'send_email';
367 :    
368 :     my @recipients = @{$self->{ra_send_to}};
369 : toenail 2320 $self->addbadmessage(CGI::p("No recipients selected ")) unless @recipients;
370 : gage 1373 # get merge file
371 :     my $merge_file = ( defined($self->{merge_file}) ) ? $self->{merge_file} : 'None';
372 :     my $delimiter = ',';
373 : gage 1397 my $rh_merge_data = $self->read_scoring_file("$merge_file", "$delimiter");
374 : gage 1376 unless (ref($rh_merge_data) ) {
375 : toenail 2320 $self->addbadmessage(CGI::p("No merge data file"));
376 :     $self->addbadmessage(CGI::p("Can't read merge file $merge_file. No message sent"));
377 : gage 1376 return;
378 :     } ;
379 : gage 1373
380 : gage 1376
381 : gage 1373 foreach my $recipient (@recipients) {
382 :     #warn "FIXME sending email to $recipient";
383 : gage 1667 my $ur = $self->{db}->getUser($recipient); #checked
384 :     die "record for user $recipient not found" unless $ur;
385 : sh002i 1677 unless ($ur->email_address) {
386 : toenail 2320 $self->addbadmessage(CGI::p("user $recipient does not have an email address -- skipping"));
387 : sh002i 1677 next;
388 :     }
389 : gage 1373 my ($msg, $preview_header);
390 :     eval{ ($msg,$preview_header) = $self->process_message($ur,$rh_merge_data); };
391 : toenail 2320 $self->addbadmessage(CGI::p("There were errors in processing user $ur, merge file $merge_file. $@")) if $@;
392 : gage 1373 my $mailer = Mail::Sender->new({
393 :     from => $from,
394 :     to => $ur->email_address,
395 :     smtp => $ce->{mail}->{smtpServer},
396 :     subject => $subject,
397 :     headers => "X-Remote-Host: ".$r->get_remote_host(),
398 :     });
399 :     unless (ref $mailer) {
400 : toenail 2320 $self->addbadmessage(CGI::p("Failed to create a mailer for user $recipient: $Mail::Sender::Error"));
401 : gage 1373 next;
402 :     }
403 :     unless (ref $mailer->Open()) {
404 : toenail 2320 $self->addbadmessage(CGI::p("Failed to open the mailer for user $recipient: $Mail::Sender::Error"));
405 : gage 1373 next;
406 :     }
407 : toenail 2320 my $MAIL = $mailer->GetHandle() or $self->addbadmessage(CGI::p("Couldn't get handle"));
408 :     print $MAIL $msg || $self->addbadmessage(CGI::p("Couldn't print to $MAIL"));
409 :     close $MAIL || $self->addbadmessage(CGI::p("Couldn't close $MAIL"));
410 : gage 1373 #warn "FIXME mailed to ", $ur->email_address, "from $from subject $subject";
411 :    
412 :     }
413 :    
414 : gage 1370 } else {
415 : toenail 2320 $self->addbadmessage(CGI::p("Didn't recognize button $action"));
416 : gage 1370 }
417 : gage 1369
418 :    
419 : toenail 2084
420 : gage 1369 } #end initialize
421 :    
422 : gage 1368
423 :    
424 :    
425 : gage 1928
426 : gage 1368 sub body {
427 : gage 1928 my ($self) = @_;
428 :     my $r = $self->r;
429 :     my $urlpath = $r->urlpath;
430 : toenail 2320 my $authz = $r->authz;
431 : gage 1928 my $setID = $urlpath->arg("setID");
432 : gage 1372 my $response = (defined($self->{response}))? $self->{response} : '';
433 : toenail 2320 my $user = $r->param('user');
434 :    
435 :     # Check permissions
436 :     return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to access instructor tools"))
437 :     unless $authz->hasPermissions($user, "access_instructor_tools");
438 :    
439 :     return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to send mail to students"))
440 :     unless $authz->hasPermissions($user, "send_mail");
441 :    
442 : gage 1372 if ($response eq 'preview') {
443 :     $self->print_preview($setID);
444 : gage 1373 } elsif (($response eq 'send_email')){
445 : toenail 2320 $self->addgoodmessage(CGI::p("Email sent to ". scalar(@{$self->{ra_send_to}})." students."));
446 : gage 2051 $self->{message} .= CGI::i("Email sent to ". scalar(@{$self->{ra_send_to}})." students.");
447 : gage 1372 $self->print_form($setID);
448 : gage 1375 } else {
449 :     $self->print_form($setID);
450 : gage 1372 }
451 :    
452 :     }
453 :     sub print_preview {
454 : gage 1928 my ($self) = @_;
455 :     my $r = $self->r;
456 :     my $urlpath = $r->urlpath;
457 :     my $setID = $urlpath->arg("setID");
458 :    
459 : gage 1372 # get preview user
460 : gage 1928 my $ur = $r->db->getUser($self->{preview_user}); #checked
461 : gage 1667 die "record for preview user ".$self->{preview_user}. " not found." unless $ur;
462 : gage 1372
463 :     # get merge file
464 :     my $merge_file = ( defined($self->{merge_file}) ) ? $self->{merge_file} : 'None';
465 :     my $delimiter = ',';
466 : gage 1397 my $rh_merge_data = $self->read_scoring_file("$merge_file", "$delimiter");
467 : gage 1372
468 :     my ($msg, $preview_header) = $self->process_message($ur,$rh_merge_data);
469 :    
470 :     my $recipients = join(" ",@{$self->{ra_send_to} });
471 : toenail 2084 my $errorMessage = defined($self->{submit_message}) ? CGI::i($self->{submit_message} ) : '' ;
472 : gage 1373 $msg = join("",
473 :     $errorMessage,
474 :     $preview_header,
475 :     "To: " , $ur->email_address,"\n",
476 :     "From: " , $self->{from} , "\n" ,
477 :     "Reply-To: " , $self->{replyTo} , "\n" ,
478 :     "Subject: " , $self->{subject} , "\n" ,"\n" ,
479 :     $msg , "\n"
480 :     );
481 : gage 1372
482 : gage 1373 return join("", '<pre>',$msg,"\n","\n",
483 : gage 1372 '</pre>',
484 :     CGI::p('Use browser back button to return from preview mode'),
485 :     CGI::h3('Emails to be sent to the following:'),
486 :     $recipients, "\n",
487 :    
488 :     );
489 :    
490 :     }
491 :     sub print_form {
492 : gage 1928 my ($self) = @_;
493 :     my $r = $self->r;
494 :     my $urlpath = $r->urlpath;
495 :     my $authz = $r->authz;
496 :     my $db = $r->db;
497 :     my $ce = $r->ce;
498 :     my $courseName = $urlpath->arg("courseID");
499 :     my $setID = $urlpath->arg("setID");
500 :     my $user = $r->param('user');
501 :    
502 : gage 1938 my $root = $ce->{webworkURLs}->{root};
503 :     my $sendMailPage = $urlpath->newFromModule($urlpath->module,courseID=>$courseName);
504 :     my $sendMailURL = $self->systemLink($sendMailPage, authen => 0);
505 : gage 1368
506 : toenail 2084 return CGI::em("You are not authorized to access the Instructor tools.") unless $authz->hasPermissions($user, "access_instructor_tools");
507 : gage 1368
508 :     my $userTemplate = $db->newUser;
509 :     my $permissionLevelTemplate = $db->newPermissionLevel;
510 :    
511 :     # This code will require changing if the permission and user tables ever have different keys.
512 : gage 1372 my @users = @{ $self->{ra_users} };
513 :     my $ra_user_records = $self->{ra_user_records};
514 :     my %classlistLabels = ();# %$hr_classlistLabels;
515 :     foreach my $ur (@{ $ra_user_records }) {
516 : gage 1720 $classlistLabels{$ur->user_id} = $ur->user_id.': '.$ur->last_name. ', '. $ur->first_name.' -- '.$ur->section." / ".$ur->recitation;
517 : gage 1372 }
518 : gage 1368
519 : mschmitt 2203 ## Mark edit define scrolling list
520 :     my $scrolling_user_list = scrollingRecordList({
521 :     name => "classList", ## changed from classList to action
522 :     request => $r,
523 :     default_sort => "lnfn",
524 :     default_format => "lnfn_uid",
525 :     default_filters => ["all"],
526 :     size => 5,
527 :     multiple => 1,
528 :     }, @{$ra_user_records});
529 : gage 1368
530 :     ##############################################################################################################
531 :    
532 : gage 1372
533 : gage 1369 my $from = $self->{from};
534 :     my $subject = $self->{subject};
535 :     my $replyTo = $self->{replyTo};
536 :     my $columns = $self->{columns};
537 :     my $rows = $self->{rows};
538 : gage 1370 my $text = defined($self->{r_text}) ? ${ $self->{r_text} }: 'FIXME no text was produced by initialization!!';
539 :     my $input_file = $self->{input_file};
540 :     my $output_file = $self->{output_file};
541 : gage 1371 my @sorted_messages = $self->get_message_file_names;
542 :     my @sorted_merge_files = $self->get_merge_file_names;
543 :     my $merge_file = ( defined($self->{merge_file}) ) ? $self->{merge_file} : 'None';
544 : gage 1372 my $delimiter = ',';
545 : gage 1397 my $rh_merge_data = $self->read_scoring_file("$merge_file", "$delimiter");
546 : gage 1372 my @merge_keys = keys %$rh_merge_data;
547 :     my $preview_user = $self->{preview_user};
548 : gage 1667 my $preview_record = $db->getUser($preview_user); # checked
549 :     die "record for preview user ".$self->{preview_user}. " not found." unless $preview_record;
550 : gage 1372
551 : gage 1667
552 : gage 1372 #############################################################################################
553 :    
554 : gage 1938 print CGI::start_form({method=>"post", action=>$sendMailURL});
555 : gage 1372 print $self->hidden_authen_fields();
556 :     #############################################################################################
557 :     # begin upper table
558 :     #############################################################################################
559 :    
560 : gage 1368 print CGI::start_table({-border=>'2', -cellpadding=>'4'});
561 : gage 1720 print CGI::Tr({-align=>'left',-valign=>'top'},
562 : gage 1372 #############################################################################################
563 :     # first column
564 :     #############################################################################################
565 :    
566 : gage 1720 CGI::td(CGI::strong("Message file: $input_file"),"\n",CGI::br(),
567 : gage 1371 CGI::submit(-name=>'action', -value=>'Open'), '&nbsp;&nbsp;&nbsp;&nbsp;',"\n",
568 :     CGI::popup_menu(-name=>'openfilename',
569 :     -values=>\@sorted_messages,
570 :     -default=>$input_file
571 :     ), "\n",CGI::br(),
572 :    
573 :     "Save file to: $output_file","\n",CGI::br(),
574 : gage 1370 "\n", 'From:','&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;', CGI::textfield(-name=>"from", -size=>30, -value=>$from, -override=>1),
575 :     "\n", CGI::br(),'Reply-To: ', CGI::textfield(-name=>"replyTo", -size=>30, -value=>$replyTo, -override=>1),
576 : gage 1376 "\n", CGI::br(),'Subject: ', CGI::br(), CGI::textarea(-name=>'subject', -default=>$subject, -rows=>3,-columns=>30, -override=>1),
577 : gage 1370 ),
578 : gage 1372 #############################################################################################
579 :     # second column
580 :     #############################################################################################
581 : mschmitt 2203 # CGI::td({-align=>'left',style=>'font-size:smaller'},
582 :     #
583 :     # CGI::strong("Send to:"),
584 :     # CGI::radio_group(-name=>'radio', -values=>['all_students','studentID'],
585 : mschmitt 2207 # -labels=>{all_students=>'All students in course',studentID => 'Selected'},
586 : mschmitt 2203 # -default=>'studentID',
587 :     # -linebreak=>0
588 :     # ), CGI::br(),CGI::br(),
589 :     ## Edit by Mark to insert scrolling list
590 :     CGI::td({-style=>"width:33%"},CGI::strong("Send to:"),
591 :     CGI::radio_group(-name=>'radio', -values=>['all_students','studentID'],
592 : mschmitt 2207 -labels=>{all_students=>'All students in course',studentID => 'Selected students'},
593 : mschmitt 2203 -default=>'studentID', -linebreak=>0),
594 :     CGI::br(),$scrolling_user_list),
595 :     ## Edit here to insert filtering
596 :     ## be sure to fail GRACEFULLY!
597 :     #
598 :     #
599 :     # CGI::input({type=>'submit',value=>'Sort by',name=>'action'}),,
600 :     # CGI::radio_group(-name=>'sort_by', -values=>['id','alphabetical','section','recitation'],
601 :     # -labels=>{id=>'Login',alphabetical=>'Alph.',section => 'Sec.',recitation=>'Rec.'},
602 :     # -default=>defined($r->param("sort_by")) ? $r->param("sort_by") : 'id',
603 :     # -linebreak=>0
604 :     # ),
605 :     #
606 :     # CGI::br(),CGI::br(),
607 :     # CGI::popup_menu(-name=>'classList',
608 :     # -values=>\@users,
609 :     # -labels=>\%classlistLabels,
610 :     # -size => 10,
611 :     # -multiple => 1,
612 :     # -default=>$user
613 :     # ),
614 :     # ),
615 : gage 1720
616 : mschmitt 2203
617 : gage 1720
618 : mschmitt 2203
619 : gage 1372 #############################################################################################
620 :     # third column
621 :     #############################################################################################
622 : gage 1370 CGI::td({align=>'left'},
623 : gage 1720 "<b>Merge file:</b> $merge_file", CGI::br(),
624 : gage 1375 CGI::submit(-name=>'action', -value=>'Set merge file to:'),CGI::br(),
625 : gage 1371 CGI::popup_menu(-name=>'merge_file',
626 :     -values=>\@sorted_merge_files,
627 :     -default=>$merge_file,
628 : gage 2050 ), "\n",CGI::hr(),
629 :     CGI::b("Viewing email for: "), "$preview_user",CGI::br(),
630 :     CGI::submit(-name=>'action', -value=>'resize', -label=>$REFRESH_RESIZE_BUTTON),'&nbsp;',
631 : gage 1372 CGI::popup_menu(-name=>'preview_user',
632 : gage 1370 -values=>\@users,
633 : gage 1372 #-labels=>\%classlistLabels,
634 :     -default=>$preview_user,
635 : gage 1369 ),
636 : gage 2050 CGI::br(),
637 :     CGI::submit(-name=>'action', -value=>'preview',-label=>'Preview message'),'&nbsp;&nbsp;',
638 :    
639 :     CGI::br(),
640 :    
641 : gage 1372 CGI::hr(),
642 : gage 1370 " Rows: ", CGI::textfield(-name=>'rows', -size=>3, -value=>$rows),
643 :     " Columns: ", CGI::textfield(-name=>'columns', -size=>3, -value=>$columns),
644 : gage 2050 CGI::br(),CGI::i('Press any action button to update display'),CGI::br(),
645 : gage 1370 #show available macros
646 :     CGI::popup_menu(
647 :     -name=>'dummyName',
648 :     -values=>['', '$SID', '$FN', '$LN', '$SECTION', '$RECITATION','$STATUS', '$EMAIL', '$LOGIN', '$COL[3]', '$COL[-1]'],
649 :     -labels=>{''=>'list of insertable macros',
650 :     '$SID'=>'$SID - Student ID',
651 :     '$FN'=>'$FN - First name',
652 :     '$LN'=>'$LN - Last name',
653 : gage 1372 '$SECTION'=>'$SECTION',
654 : gage 1370 '$RECITATION'=>'$RECITATION',
655 :     '$STATUS'=>'$STATUS - C, Audit, Drop, etc.',
656 :     '$EMAIL'=>'$EMAIL - Email address',
657 :     '$LOGIN'=>'$LOGIN - Login',
658 : gage 1372 '$COL[3]'=>'$COL[3] - 3rd col',
659 : gage 1370 '$COL[-1]'=>'$COL[-1] - Last column'
660 :     }
661 :     ), "\n",
662 :     ),
663 : gage 1368
664 : gage 1370 ); # end Tr
665 : gage 1372 print CGI::end_table();
666 :     #############################################################################################
667 :     # end upper table
668 :     #############################################################################################
669 :    
670 :     # show merge file
671 :     # print "<pre>",(map {$_ =~s/\s/\./g;$_} map {sprintf('%-8.8s',$_);} 0..8),"</pre>";
672 :     # print CGI::popup_menu(
673 :     # -name=>'dummyName2',
674 :     # -values=>\@merge_keys,
675 :     # -labels=>$rh_merge_data,
676 :     # -multiple=>1,
677 :     # -size =>2,
678 :     #
679 :     # ), "\n",CGI::br();
680 :     # warn "merge keys ", join( " ",@merge_keys);
681 :     #############################################################################################
682 :     # merge file fragment and message text area field
683 :     #############################################################################################
684 : gage 1373 my @tmp2;
685 : gage 1667 eval{ @tmp2= @{$rh_merge_data->{ $db->getUser($preview_user)->student_id } };}; # checked
686 : gage 1730 if ($@ and $merge_file ne 'None') {
687 : gage 1381 print "No merge data for $preview_user in merge file: &lt;$merge_file&gt;",CGI::br();
688 : gage 1373 } else {
689 : apizer 2786 print CGI::pre("",data_format(1..($#tmp2+1)),"<br>", data_format2(@tmp2));
690 : gage 1373 }
691 : gage 1368 #create a textbox with the subject and a textarea with the message
692 :     #print actual body of message
693 : gage 1369
694 : gage 1370 print "\n", CGI::p( $self->{message}) if defined($self->{message});
695 : gage 1369 print "\n", CGI::p( CGI::textarea(-name=>'body', -default=>$text, -rows=>$rows, -columns=>$columns, -override=>1));
696 : gage 1372
697 :     #############################################################################################
698 :     # action button table
699 :     #############################################################################################
700 : gage 1370 print CGI::table( { -border=>2,-cellpadding=>4},
701 :     CGI::Tr(
702 :     CGI::td( CGI::submit(-name=>'action', -value=>'Send Email') ), "\n",
703 :     CGI::td(CGI::submit(-name=>'action', -value=>'Save')," to $output_file"), " \n",
704 :     CGI::td(CGI::submit(-name=>'action', -value=>'Save as:'),
705 :     CGI::textfield(-name=>'savefilename', -size => 20, -value=> "$output_file", -override=>1)
706 :     ), "\n",
707 :     CGI::td(CGI::submit(-name=>'action', -value=>'Save as Default')),
708 :     )
709 :     );
710 : gage 1368
711 :     ##############################################################################################################
712 : gage 1369
713 :     print CGI::end_form();
714 : gage 1368 return "";
715 :     }
716 :    
717 : gage 1369 ##############################################################################
718 :     # Utility methods
719 :     ##############################################################################
720 : gage 1370 sub submission_error {
721 : gage 1369 my $self = shift;
722 : toenail 2084 my $msg = join( " ", @_);
723 : gage 1730 $self->{submitError} .= CGI::br().$msg;
724 : toenail 2084 return;
725 : gage 1369 }
726 :    
727 : gage 1370 sub saveProblem {
728 :     my $self = shift;
729 :     my ($body, $probFileName)= @_;
730 :     local(*PROBLEM);
731 :     open (PROBLEM, ">$probFileName") ||
732 : toenail 2320 $self->addbadmessage(CGI::p("Could not open $probFileName for writing.
733 :     Check that the permissions for this problem are 660 (-rw-rw----)"));
734 : toenail 2084 print PROBLEM $body if -w $probFileName;
735 : gage 1370 close PROBLEM;
736 :     chmod 0660, "$probFileName" ||
737 : toenail 2320 $self->addbadmessage(CGI::p("CAN'T CHANGE PERMISSIONS ON FILE $probFileName"));
738 : gage 1370 }
739 :    
740 :     sub read_input_file {
741 :     my $self = shift;
742 :     my $filePath = shift;
743 :     my ($text, @text);
744 :     my $header = '';
745 :     my ($subject, $from, $replyTo);
746 :     local(*FILE);
747 : gage 1371 if (-e "$filePath" and -r "$filePath") {
748 : toenail 2320 open FILE, "$filePath" || do { $self->addbadmessage(CGI::p("Can't open $filePath")); return};
749 : gage 1371 while ($header !~ s/Message:\s*$//m and not eof(FILE)) {
750 : gage 1370 $header .= <FILE>;
751 :     }
752 :     $text = join( '', <FILE>);
753 :     $text =~ s/^\s*//; # remove initial white space if any.
754 :     $header =~ /^From:\s(.*)$/m;
755 :     $from = $1 or $from = $self->{defaultFrom};
756 :    
757 :     $header =~ /^Reply-To:\s(.*)$/m;
758 :     $replyTo = $1 or $replyTo = $self->{defaultReply};
759 :    
760 :     $header =~ /^Subject:\s(.*)$/m;
761 :     $subject = $1;
762 :    
763 :     } else {
764 :     $from = $self->{defaultFrom};
765 :     $replyTo = $self->{defaultReply};
766 : gage 1371 $text = (-e "$filePath") ? "FIXME file $filePath can't be read" :"FIXME file $filePath doesn't exist";
767 : dpvc 2388 $subject = $self->{defaultSubject};
768 : gage 1370 }
769 :     return ($from, $replyTo, $subject, \$text);
770 :     }
771 : gage 1371
772 :    
773 : gage 1397 sub get_message_file_names {
774 :     my $self = shift;
775 :     return $self->read_dir($self->{ce}->{courseDirs}->{email}, '\\.msg$');
776 : gage 1371 }
777 : gage 1397 sub get_merge_file_names {
778 :     my $self = shift;
779 : gage 1730 return 'None', $self->read_dir($self->{ce}->{courseDirs}->{scoring}, '\\.csv$'); #FIXME ? check that only readable files are listed.
780 : gage 1371 }
781 : gage 1372
782 : gage 1397
783 : gage 1372 sub getRecord {
784 :     my $self = shift;
785 :     my $line = shift;
786 :     my $delimiter = shift;
787 :     $delimiter = ',' unless defined($delimiter);
788 :    
789 :     # Takes a delimited line as a parameter and returns an
790 :     # array. Note that all white space is removed. If the
791 :     # last field is empty, the last element of the returned
792 :     # array is also empty (unlike what the perl split command
793 :     # would return). E.G. @lineArray=&getRecord(\$delimitedLine).
794 :    
795 :     my(@lineArray);
796 : gage 2001 $line.="${delimiter}___"; # add final field which must be non-empty
797 :     @lineArray = split(/\s*${delimiter}\s*/,$line); # split line into fields
798 : gage 1372 $lineArray[0] =~s/^\s*//; # remove white space from first element
799 : gage 2001 pop @lineArray; # remove the last artificial field
800 : gage 1372 @lineArray;
801 :     }
802 :    
803 :     sub process_message {
804 :     my $self = shift;
805 :     my $ur = shift;
806 :     my $rh_merge_data = shift;
807 :     my $text = defined($self->{r_text}) ? ${ $self->{r_text} }:
808 : gage 1730 'FIXME no text was produced by initialization!!';
809 :     my $merge_file = ( defined($self->{merge_file}) ) ? $self->{merge_file} : 'None';
810 : gage 1372 #user macros that can be used in the email message
811 :     my $SID = $ur->student_id;
812 :     my $FN = $ur->first_name;
813 :     my $LN = $ur->last_name;
814 :     my $SECTION = $ur->section;
815 :     my $RECITATION = $ur->recitation;
816 :     my $STATUS = $ur->status;
817 :     my $EMAIL = $ur->email_address;
818 :     my $LOGIN = $ur->user_id;
819 : gage 1730
820 : gage 1372 # get record from merge file
821 :     # FIXME this is inefficient. The info should be cached
822 : gage 1373 my @COL = defined($rh_merge_data->{$SID}) ? @{$rh_merge_data->{$SID} } : ();
823 : gage 1730 if ($merge_file ne 'None' && not defined($rh_merge_data->{$SID}) ) {
824 : toenail 2320 $self->addbadmessage(CGI::p("No merge data for student id:$SID; name:$FN $LN; login:$LOGIN"));
825 : gage 1730 }
826 : apizer 2779 unshift(@COL,""); ## this makes COL[1] the first column
827 : gage 1372 my $endCol = @COL;
828 :     # for safety, only evaluate special variables
829 : gage 1373 my $msg = $text;
830 :     $msg =~ s/(\$SID)/eval($1)/ge;
831 :     $msg =~ s/(\$LN)/eval($1)/ge;
832 :     $msg =~ s/(\$FN)/eval($1)/ge;
833 :     $msg =~ s/(\$STATUS)/eval($1)/ge;
834 :     $msg =~ s/(\$SECTION)/eval($1)/ge;
835 :     $msg =~ s/(\$RECITATION)/eval($1)/ge;
836 :     $msg =~ s/(\$EMAIL)/eval($1)/ge;
837 :     $msg =~ s/(\$LOGIN)/eval($1)/ge;
838 : apizer 2779 # $msg =~ s/\$COL\[ *-/\$COL\[$endCol-/g; ## Perl handles negative indexes correctly, so there is no need to do this
839 : apizer 2786 $msg =~ s/\$COL\[(\-?\d+)\]/$COL[$1]/ge if defined($COL[1]); # prevents extraneous error messages.
840 : apizer 2779
841 : gage 1373 $msg =~ s/\r//g;
842 : apizer 2779
843 :     my @preview_COL = @COL;
844 :     shift @preview_COL; ## shift back for preview
845 :     my $preview_header = CGI::pre("",data_format(1..($#COL)),"<br>", data_format2(@preview_COL)).
846 : gage 1372 CGI::h3( "This sample mail would be sent to $EMAIL");
847 :    
848 :     return $msg, $preview_header;
849 :     }
850 : gage 1948
851 :    
852 :     # Ê sub data_format {
853 :     #
854 :     # Ê Ê Ê Ê Êmap {$_ =~s/\s/\./g;$_} Ê Ê map {sprintf('%-8.8s',$_);} Ê@_;
855 : gage 1372 sub data_format {
856 : gage 1948 map {"COL[$_]".'&nbsp;'x(3-length($_));} @_; # problems if $_ has length bigger than 4
857 : gage 1372 }
858 : gage 1947 sub data_format2 {
859 : gage 1948 map {$_ =~s/\s/&nbsp;/g;$_} map {sprintf('%-8.8s',$_);} @_;
860 : gage 1947 }
861 : gage 1368 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9