# The LearningOnline Network # Utilities to administer domain course requests and course self-enroll requests # # $Id: loncoursequeueadmin.pm,v 1.1 2009/08/11 00:39:45 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA 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. # # LON-CAPA 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 LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # ### =head1 NAME Apache::loncoursequeueadmin.pm =head1 SYNOPSIS Adminitsration utilities used by domain coordinators for queued course creation requests, and by course coordinators for queued self-enrollment requests. This is part of the LearningOnline Network with CAPA project described at http://www.lon-capa.org. =head1 SUBROUTINES =over =item send_selfserve_notification() =item display_queued_requests() =item update_request_queue() =item get_student_counts() =back =cut package Apache::loncoursequeueadmin; use strict; use Apache::Constants qw(:common :http); use Apache::lonnet; use Apache::loncommon; use Apache::lonmsg; use Apache::lonlocal; use LONCAPA; sub send_selfserve_notification { my ($notifylist,$textstr,$cid,$coursedesc,$timestamp,$context,$sender, $approvedlist,$rejectedlist) = @_; # FIXME locallocaltime needs to be able to take $sender_lh as an argument # so this can be localized to the recipients date display format/time zone $timestamp =&Apache::lonlocal::locallocaltime($timestamp); my $msgcc; my ($rawsubj,@rawmsg,$subject,$message,$namelink); $namelink = &Apache::loncommon::aboutmewrapper( &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'})); if ($context eq 'coursemanagers') { $rawsubj = 'Self-enrollment requests processed'; push(@rawmsg,{ mt => 'Enrollment requests in the following course: [_1] have been processed.', args => ["\n$coursedesc\n"], }); } elsif ($context eq 'domainmanagers') { $rawsubj = 'Course request'; push(@rawmsg,{ mt => 'Your request for creation of the following course: [_1], requested on [_2], has been reviewed by a Domain Coordinator.', args => ["\n$coursedesc\n","$timestamp\n"], }); if (ref($textstr) eq 'ARRAY') { push(@rawmsg,@{$textstr}); } } elsif ($context eq 'enroller') { $rawsubj = 'Enrollment request'; push(@rawmsg,{ mt => 'Your request for enrollment in the following course: [_1], requested on [_2], has been reviewed by a Course Coordinator.', args => ["\n$coursedesc\n","$timestamp\n"], }); if (ref($textstr) eq 'ARRAY') { push(@rawmsg,@{$textstr}); } } elsif ($context eq 'courserequestor') { $rawsubj = 'Course request'; push(@rawmsg,{ mt => 'Your request for creation of the following course: [_1], requested on [_2], has been reviewed by a Domain Coordinator.', args => ["\n$coursedesc\n","$timestamp\n"], }); if (ref($textstr) eq 'ARRAY') { push(@rawmsg,@{$textstr}); } } elsif ($context eq 'coursereq') { push(@rawmsg,{ mt => 'Creation of the following course: [_1] was requested by [_2] on [_3].', args => ["\n$coursedesc\n",$textstr,$timestamp], }, { mt =>'As Domain Coordinator, use: [_1]Main Menu -> Create a new course -> Manage Course Requests[_1] to display a list of pending course requests,[_1] which you can either approve or reject.', args => ["\n"], }); } elsif ($context eq 'selfenrollreq') { $rawsubj = 'Self-enrollment request'; push(@rawmsg,{ mt => 'Enrollment in the following course: [_1] was requested by [_2] on [_3].', args => ["\n$coursedesc\n",$textstr,$timestamp], }, { mt =>'As Course Coordinator, use: [_1]Main Menu -> Manage Course Users -> Enrollment Requests[_1] to display a list of pending enrollment requests,[_1] which you can either approve or reject.', args => ["\n"], }); } my @to_notify = split(/,/,$notifylist); my $numsent = 0; my @recusers; my @recudoms; foreach my $cc (@to_notify) { my ($ccname,$ccdom) = split(/:/,$cc); if (!exists($msgcc->{$ccname.':'.$ccdom})) { push(@recusers,$ccname); push(@recudoms,$ccdom); $msgcc->{$ccname.':'.$ccdom}=''; $numsent ++; } } my %reciphash = ( cc => $msgcc, ); my ($uname,$udom); if ($sender =~ /:/) { ($uname,$udom) = split(/:/,$sender); } else { $uname = $sender; my %courseinfo = &Apache::lonnet::coursedescription($cid); $udom = $courseinfo{'num'}; } my %sentmessage; my $stamp = time; my $msgcount = &Apache::lonmsg::get_uniq(); my $sender_lh = &Apache::loncommon::user_lang($uname,$udom,$cid); $subject = &Apache::lonlocal::mt_user($sender_lh,$rawsubj); $message = ''; foreach my $item (@rawmsg) { if (ref($item) eq 'HASH') { $message .= &Apache::lonlocal::mt_user($sender_lh,$item->{mt},@{$item->{args}})."\n"; } } &Apache::lonmsg::process_sent_mail($subject,'',$numsent,$stamp,$uname,$udom,$msgcount,$cid,$$,$message,\@recusers,\@recudoms); my ($recipid,$recipstatus) = &Apache::lonmsg::store_recipients($subject,$uname,$udom,\%reciphash); my $status; foreach my $recip (sort(keys(%{$msgcc}))) { my ($ccname,$ccdom) = split(/:/,$recip); my $recip_lh = &Apache::loncommon::user_lang($ccname,$ccdom,$cid); my $subject = &Apache::lonlocal::mt_user($sender_lh,$rawsubj); my $message = ''; foreach my $item (@rawmsg) { if (ref($item) eq 'HASH') { $message .= &Apache::lonlocal::mt_user($sender_lh,$item->{mt}, @{$item->{args}})."\n"; } } if ($context eq 'managers') { if ($approvedlist) { $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Approved enrollments:')."\n".$approvedlist; } if ($rejectedlist) { $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Rejected enrollments:')."\n".$rejectedlist; } } elsif ($context eq 'domainmanagers') { if ($approvedlist) { $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Approved course requests:')."\n".$approvedlist; } if ($rejectedlist) { $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Rejected course requests:')."\n".$rejectedlist; } } $status .= &Apache::lonmsg::user_normal_msg($ccname,$ccdom,$subject,$message,undef,undef,undef,1,\%sentmessage,undef,undef,undef,1,$recipid).','; } $status =~ s/,$//; return ($recipstatus,$status); } sub display_queued_requests { my ($context,$dom,$cnum) = @_; my ($namespace,$formaction,%requesthash); if ($context eq 'course') { $formaction = '/adm/createuser'; $namespace = 'selfenrollrequests'; %requesthash = &Apache::lonnet::dump($namespace,$dom,$cnum); } else { $formaction = '/adm/createcourse'; $namespace = 'courserequestqueue'; %requesthash = &Apache::lonnet::dump_dom($namespace,$dom,undef,'_approval'); } my ($output,%queue_by_date,%crstypes); if (keys(%requesthash) > 0) { $output = '
'. ''. ''. &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(). ''.&mt('Action').''. ''.&mt('Requestor').''; if ($context eq 'course') { $output .= ''.&mt('Section').''. ''.&mt('Date requested').''; %crstypes = &Apache::lonlocal::texthash ( official => 'Official course', unofficial => 'Unofficial course', community => 'Community', ); } else { $output .= ''.&mt('Type').''. ''.&mt('Date requested').''. ''.&mt('Details').''; } $output .= &Apache::loncommon::end_data_table_header_row(); foreach my $item (keys(%requesthash)) { my ($timestamp,$entry); if ($context eq 'course') { ($timestamp, my $usec) = split(/:/,$requesthash{$item}); $entry = $item.':'.$usec; } else { $timestamp = $requesthash{$item}{'timestamp'}; if (ref($requesthash{$item}) eq 'HASH') { $entry = $item.':'.$requesthash{$item}{'ownername'}.':'. $requesthash{$item}{'ownerdom'}.':'. $requesthash{$item}{'crstype'}.':'. $requesthash{$item}{'description'}; } } if ($entry ne '') { if (exists($queue_by_date{$timestamp})) { if (ref($queue_by_date{$timestamp}) eq 'ARRAY') { push(@{$queue_by_date{$timestamp}},$entry); } } else { @{$queue_by_date{$timestamp}} = ($entry); } } } my @sortedtimes = sort {$a <=> $b} (keys(%queue_by_date)); my $count = 0; foreach my $item (@sortedtimes) { if (ref($queue_by_date{$item}) eq 'ARRAY') { foreach my $request (sort(@{$queue_by_date{$item}})) { my ($row,$approve,$reject,$showtime,$showsec,$namelink, $detailslink,$crstype); $showtime = &Apache::lonlocal::locallocaltime($item); if ($context eq 'course') { my ($puname,$pudom,$pusec) = split(/:/,$request); $approve = $count.':'.$puname.':'.$pudom.':'.$pusec; $reject = $puname.':'.$pudom; $showsec = $pusec; if ($showsec eq '') { $showsec = &mt('none'); } $namelink = &Apache::loncommon::aboutmewrapper( &Apache::loncommon::plainname($puname,$pudom), $puname,$pudom); } else { my ($cnum,$ownername,$ownerdom,$type,$cdesc)=split(/:/,$request); $detailslink=''.$cdesc.''; $crstype = $type; if (defined($crstypes{$type})) { $crstype = $crstypes{$type}; } $approve = $count.':'.$cnum; $reject = $cnum; $namelink = &Apache::loncommon::aboutmewrapper( &Apache::loncommon::plainname($ownername,$ownerdom), $ownername,$ownerdom); } $row = '
'. '
'. ''.$namelink.''."\n"; if ($context eq 'course') { $row .= ''.$showsec.''."\n". ''.$showtime.''."\n"; } else { $row .= ''.$crstype.''."\n". ''.$showtime.''."\n". ''.$detailslink.''."\n"; } $output .= &Apache::loncommon::start_data_table_row()."\n". $row. &Apache::loncommon::end_data_table_row()."\n"; $count ++; } } } $output .= &Apache::loncommon::end_data_table(). '
'; } else { if ($context eq 'course') { $output .= &mt('There are currently no enrollment requests.'); } else { $output .= &mt('There are currently no course requests awaiting approval.'); } } return $output; } sub update_request_queue { my ($context,$cdom,$cnum,$coursedesc) = @_; my ($output,$access_start,$access_end,$limit,$cap,$notifylist,$namespace, $stucounts,$idx,$classlist,%requesthash,$coursedesc,$cid,$hostname, $protocol,$now,$sender,$approvedmsg,$rejectedmsg,$beneficiary, @existing,@missingreq,@invalidusers,@limitexceeded,@enrolled,@created, @enrollerrors,@create_errors,@warn_approves,@warn_rejects,@approvals, @rejections); @approvals = &Apache::loncommon::get_env_multiple('form.approvereq'); @rejections = &Apache::loncommon::get_env_multiple('form.rejectreq'); $now = time; $sender = $env{'user.name'}.':'.$env{'user.domain'}; if ($context eq 'course') { $namespace = 'selfenrollrequests'; $beneficiary = 'enroller'; $cid = $env{'request.course.id'}; my $chome = &Apache::lonnet::homeserver($cnum,$cdom); $hostname = &Apache::lonnet::hostname($chome); $protocol = $Apache::lonnet::protocol{$chome}; $protocol = 'http' if ($protocol ne 'https'); %requesthash = &Apache::lonnet::dump($namespace,$cdom,$cnum); $access_start = $env{'course.'.$cid.'.internal.selfenroll_start_access'}; $access_end = $env{'course.'.$cid.'.internal.selfenroll_end_access'}; $limit = $env{'course.'.$cid.'.internal.selfenroll_limit'}; $cap = $env{'course.'.$cid.'.internal.selfenroll_cap'}; $notifylist = $env{'course.'.$cid.'.internal.selfenroll_notifylist'}; $namespace = 'selfenrollrequests'; ($stucounts,$idx,$classlist) = &get_student_counts($cdom,$cnum); $approvedmsg = [{ mt => 'Your request for enrollment has been approved.', }, { mt => 'Visit [_1], to log-in and access the course', args => [$protocol.'://'.$hostname], }]; $rejectedmsg = [{ mt => 'Your request for enrollment has not been approved.', }]; } else { $beneficiary = 'requestor'; } foreach my $item (sort {$a <=> $b} @approvals) { if ($context eq 'course') { my ($num,$uname,$udom,$usec) = split(/:/,$item); my $uhome = &Apache::lonnet::homeserver($uname,$udom); if ($uhome ne 'no_host') { if (exists($requesthash{$uname.':'.$udom})) { if (exists($classlist->{$uname.':'.$udom})) { if (ref($classlist->{$uname.':'.$udom}) eq 'ARRAY') { if (($classlist->{$uname.':'.$udom}->[$idx->{'status'}] eq 'Active') || ($classlist->{$uname.':'.$udom}->[$idx->{'status'}] eq 'Future')) { push(@existing,$uname.':'.$udom); next; } } } } else { push(@missingreq,$uname.':'.$udom); next; } if (!grep(/^\Q$item\E$/,@rejections)) { if ($limit eq 'allstudents') { if ($stucounts->{$limit} >= $cap) { push(@limitexceeded,$uname.':'.$udom); last; } } elsif ($limit eq 'selfenrolled') { if ($stucounts->{$limit} >= $cap) { push(@limitexceeded,$uname.':'.$udom); last; } } my $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$usec,$access_end,$access_start,'selfenroll',undef,$cdom.'_'.$cnum,1); if ($result eq 'ok') { push(@enrolled,$uname.':'.$udom); $stucounts->{'allstudents'} ++; $stucounts->{'selfenrolled'} ++; &send_selfserve_notification($uname.':'.$udom,$approvedmsg, $cid,$coursedesc,$now,$beneficiary,$sender); my %userrequest; if ($context eq 'course') { %userrequest = ( $cdom.'_'.$cnum => { timestamp => $now, section => $usec, adjudicator => $env{'user.name'}.':'.$env{'user.domain'}, status => 'approved', } ); } else { %userrequest = (); } my $userresult = &Apache::lonnet::put($namespace,\%userrequest,$udom,$uname); if ($userresult ne 'ok') { push(@warn_approves,$uname.':'.$udom); } } else { push(@enrollerrors,$uname.':'.$udom); } } } else { push(@invalidusers,$uname.':'.$udom); } } else { } } my @changes = (@enrolled,@rejections); if (@rejections) { foreach my $user (@rejections) { &send_selfserve_notification($user,$rejectedmsg,$cid,$coursedesc, $now,$beneficiary,$sender); if ($context eq 'course') { my ($uname,$udom) = split(/:/,$user); my %userrequest = ( $cdom.'_'.$cnum => { timestamp => $now, adjudicator => $env{'user.name'}.':'.$env{'user.domain'}, status => 'rejected', } ); my $userresult = &Apache::lonnet::put($namespace,\%userrequest,$udom,$uname); if ($userresult ne 'ok') { push(@warn_rejects,$user); } } else { } } } if (@changes) { my $delresult; if ($context eq 'course') { $delresult = &Apache::lonnet::del($namespace,\@changes,$cdom,$cnum); } else { $delresult = &Apache::lonnet::del_dom($namespace,\@changes,$cdom); } if ($delresult eq 'ok') { my $namelink = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'}).' ('.$env{'user.name'}.':'.$env{'user.domain'}.')'; my ($chgmsg,$approvedlist,$rejectedlist); if ($context eq 'course') { $chgmsg = "'Action was taken on the following enrollment requests by [_1].',$namelink"; if (@enrolled) { $approvedlist = join("\n",@enrolled); $output .= '

'.&mt('The following were enrolled in the course:').'

'; } if (@rejections) { $rejectedlist = join("\n",@rejections); $output .= '

'.&mt('The following enrollment requests were rejected:').'

'; } } else { } &send_selfserve_notification($notifylist,$chgmsg,$cid,$coursedesc, $now,'coursemanagers',$sender, $approvedlist,$rejectedlist); } } if (@existing) { if ($context eq 'course') { $output .= '

'.&mt('The following enrollment requests were deleted because the user is already enrolled in the course:').'

'; } else { } } if (@missingreq) { if ($context eq 'course') { $output .= '

'.&mt('The following enrollment requests were ignored because the request is no longer in the enrollment queue:').'

'; } } if (@invalidusers) { if ($context eq 'course') { $output .= '

'.&mt('The following enrollment requests were deleted because the requestor does not have a LON-CAPA account:').'

'; } else { } } if (@limitexceeded) { if ($context eq 'course') { $output .= '

'.&mt('The following enrollment requests were skipped because the enrollment limit has been reached for the course:').'

'; } else { } } if (@enrollerrors) { if ($context eq 'course') { $output .= '

'.&mt('The following enrollment requests could not be processed because an error occurred:').'

'; } else { } } if (@warn_approves) { if ($context eq 'course') { $output .= '

'.&mt("For the following users, an error occurred when updating the user's own self-enroll requests record:").'

'; } else { } } if (@warn_rejects) { if ($context eq 'course') { $output .= '

'.&mt("For the following users, an error occurred when updating the user's own self-enroll requests record:").'

'; } else { } } return $output; } sub get_student_counts { my ($cdom,$cnum) = @_; my (%idx,%stucounts); my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cnum); $idx{'type'} = &Apache::loncoursedata::CL_TYPE(); $idx{'status'} = &Apache::loncoursedata::CL_STATUS(); while (my ($student,$data) = each(%$classlist)) { if (($data->[$idx{'status'}] eq 'Active') || ($data->[$idx{'status'}] eq 'Future')) { if ($data->[$idx{'type'}] eq 'selfenroll') { $stucounts{'selfenroll'} ++; } $stucounts{'allstudents'} ++; } } return (\%stucounts,\%idx,$classlist); } 1;