# The LearningOnline Network
# Request a course
#
# $Id: lonrequestcourse.pm,v 1.43 2010/01/14 20:08:13 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::lonrequestcourse.pm
=head1 SYNOPSIS
Allows users to request creation of new courses.
This is part of the LearningOnline Network with CAPA project
described at http://www.lon-capa.org.
=head1 SUBROUTINES
=over
=item handler()
=item get_breadcrumbs()
=item header()
=item form_elements()
=item onload_action()
=item print_main_menu()
=item request_administration()
=item close_popup_form()
=item get_instcode()
=item print_request_form()
=item print_enrollment_menu()
=item show_invalid_crosslists()
=item inst_section_selector()
=item date_setting_table()
=item print_personnel_menu()
=item print_request_status()
=item print_request_logs()
=item print_review()
=item dates_from_form()
=item courseinfo_form()
=item clone_form()
=item clone_text()
=item coursecode_form()
=item get_course_dom()
=item display_navbuttons()
=item print_request_outcome()
=item check_autolimit()
=item retrieve_settings()
=item get_request_settings()
=item extract_instcode()
=item generate_date_items()
=back
=cut
package Apache::lonrequestcourse;
use strict;
use Apache::Constants qw(:common :http);
use Apache::lonnet;
use Apache::loncommon;
use Apache::lonlocal;
use Apache::loncoursequeueadmin;
use Apache::lonuserutils;
use LONCAPA qw(:DEFAULT :match);
sub handler {
my ($r) = @_;
&Apache::loncommon::content_type($r,'text/html');
$r->send_http_header;
if ($r->header_only) {
return OK;
}
&Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
['action','showdom','cnum','state','crstype']);
&Apache::lonhtmlcommon::clear_breadcrumbs();
my $dom = &get_course_dom();
my $action = $env{'form.action'};
my $state = $env{'form.state'};
my (%states,%stored);
my ($jscript,$uname,$udom,$result,$warning);
$states{'display'} = ['details'];
$states{'view'} = ['pick_request','details','cancel','removal'];
$states{'log'} = ['filter','display'];
$states{'new'} = ['courseinfo','enrollment','personnel','review','process'];
if (($action eq 'new') && ($env{'form.crstype'} eq 'official')) {
unless ($env{'form.state'} eq 'crstype') {
unshift(@{$states{'new'}},'codepick');
}
}
foreach my $key (keys(%states)) {
if (ref($states{$key}) eq 'ARRAY') {
unshift (@{$states{$key}},'crstype');
}
}
my @invalidcrosslist;
my %trail = (
crstype => 'Request Action',
codepick => 'Category',
courseinfo => 'Description',
enrollment => 'Access Dates',
personnel => 'Personnel',
review => 'Review',
process => 'Result',
pick_request => 'Display Summary',
details => 'Request Details',
cancel => 'Cancel Request',
removal => 'Outcome',
);
if (($env{'form.crstype'} eq 'official') && (&Apache::lonnet::auto_run('',$dom))) {
$trail{'enrollment'} = 'Enrollment';
}
my ($page,$crumb,$newinstcode,$codechk,$checkedcode,$description) =
&get_breadcrumbs($dom,$action,\$state,\%states,\%trail);
if ($action eq 'display') {
if (($dom eq $env{'request.role.domain'}) && (&Apache::lonnet::allowed('ccc',$dom))) {
my $namespace = 'courserequestqueue';
if ($env{'form.cnum'} ne '') {
my $cnum = $env{'form.cnum'};
my $reqkey = $cnum.'_approval';
my $namespace = 'courserequestqueue';
my $domconfig = &Apache::lonnet::get_domainconfiguser($dom);
my %queued =
&Apache::lonnet::get($namespace,[$reqkey],$dom,$domconfig);
if (ref($queued{$reqkey}) eq 'HASH') {
$uname = $queued{$reqkey}{'ownername'};
$udom = $queued{$reqkey}{'ownerdom'};
if (($udom =~ /^$match_domain$/) && ($uname =~ /^$match_username$/)) {
$result = &retrieve_settings($dom,$cnum,$udom,$uname);
} else {
if ($env{'form.crstype'} eq 'community') {
$warning = &mt('Invalid username or domain for community requestor');
} else {
$warning = &mt('Invalid username or domain for course requestor');
}
}
} else {
if ($env{'form.crstype'} eq 'community') {
$warning = &mt('No information was found for this community request.');
} else {
$warning = &mt('No information was found for this course request.');
}
}
} else {
$warning = &mt('No course request ID provided.');
}
} else {
if ($env{'form.crstype'} eq 'any') {
$warning = &mt('You do not have rights to view course or community request information.');
} elsif ($env{'form.crstype'} eq 'community') {
$warning = &mt('You do not have rights to view community request information.');
} else {
$warning = &mt('You do not have rights to view course request information.');
}
}
} elsif ((defined($state)) && (defined($action))) {
if (($action eq 'view') && ($state eq 'details')) {
if ((defined($env{'form.showdom'})) && (defined($env{'form.cnum'}))) {
my $result = &retrieve_settings($env{'form.showdom'},$env{'form.cnum'});
}
} elsif ($env{'form.crstype'} eq 'official') {
if (&Apache::lonnet::auto_run('',$dom)) {
if (($action eq 'new') && (($state eq 'enrollment') ||
($state eq 'personnel'))) {
my $checkcrosslist = 0;
for (my $i=0; $i<$env{'form.crosslisttotal'}; $i++) {
if ($env{'form.crosslist_'.$i}) {
$checkcrosslist ++;
}
}
if ($checkcrosslist) {
my %codechk;
my (@codetitles,%cat_titles,%cat_order,@code_order,$lastitem);
&Apache::lonnet::auto_possible_instcodes($dom,\@codetitles,
\%cat_titles,
\%cat_order,
\@code_order);
my $numtitles = scalar(@codetitles);
if ($numtitles) {
for (my $i=0; $i<$env{'form.crosslisttotal'}; $i++) {
if ($env{'form.crosslist_'.$i}) {
my $codecheck;
my $crosslistcode = '';
foreach my $item (@code_order) {
$crosslistcode .= $env{'form.crosslist_'.$i.'_'.$item};
}
if ($crosslistcode ne '') {
($codechk{$i}, my $rest) =
&Apache::lonnet::auto_validate_instcode('',$dom,$crosslistcode);
}
unless ($codechk{$i} eq 'valid') {
$env{'form.crosslist_'.$i} = '';
push(@invalidcrosslist,$crosslistcode);
}
}
}
}
}
}
}
}
my %elements = &form_elements($dom);
my $elementsref = {};
if (ref($elements{$action}) eq 'HASH') {
if (ref($elements{$action}{$state}) eq 'HASH') {
$elementsref = $elements{$action}{$state};
}
}
if (($state eq 'courseinfo') && ($env{'form.clonedom'} eq '')) {
$env{'form.clonedom'} = $dom;
}
if ($state eq 'crstype') {
$jscript = &mainmenu_javascript();
} else {
$jscript = &Apache::lonhtmlcommon::set_form_elements($elementsref,\%stored);
}
}
if ($state eq 'personnel') {
$jscript .= "\n".&Apache::loncommon::userbrowser_javascript();
}
my $loaditems = &onload_action($action,$state);
my (%can_request,%request_domains);
my $canreq =
&Apache::lonnet::check_can_request($dom,\%can_request,\%request_domains);
if ($action eq 'new') {
if ($canreq) {
if ($state eq 'crstype') {
&print_main_menu($r,\%can_request,\%states,$dom,$jscript,$loaditems,
$crumb,\%request_domains);
} else {
&request_administration($r,$action,$state,$page,\%states,$dom,
$jscript,$loaditems,$crumb,$newinstcode,
$codechk,$checkedcode,$description,
\@invalidcrosslist);
}
} else {
$r->print(&header('Course/Community Requests').$crumb.
'
'.
&mt('You do not have privileges to request creation of courses or communities.').
'
'."\n".
&Apache::loncommon::end_page());
return;
}
$r->print(''.&Apache::lonhtmlcommon::row_closure());
my $formname = 'requestcrs';
my $nexttext = &mt('Next');
$r->print(&Apache::lonhtmlcommon::row_title(&mt('Action')).'
'.
&Apache::lonhtmlcommon::row_closure(1).
&Apache::lonhtmlcommon::row_title(&mt('Type')).'
'."\n".
&Apache::lonhtmlcommon::row_closure(1)."\n".
&Apache::lonhtmlcommon::end_pick_box().''."\n".
'');
$r->print(&Apache::loncommon::end_page());
return;
}
sub request_administration {
my ($r,$action,$state,$page,$states,$dom,$jscript,$loaditems,$crumb,
$newinstcode,$codechk,$checkedcode,$description,$invalidcrosslist,
$uname,$udom) = @_;
my $js;
if (($action eq 'new') || (($action eq 'view') && ($state eq 'pick_request'))) {
$js = <print(&header($title,$js.$jscript,$loaditems,$jsextra).$crumb);
&print_request_form($r,$action,$state,$page,$states,$dom,$newinstcode,
$codechk,$checkedcode,$description,$invalidcrosslist);
} elsif ($action eq 'view') {
my $jsextra;
my $formname = 'requestcrs';
my $prev = $states->{$action}[$page-1];
my $next = $states->{$action}[$page+1];
if ($state eq 'pick_request') {
$next = $states->{$action}[$page+1];
$jsextra = &viewrequest_javascript($formname,$next);
} elsif ($state eq 'details') {
$jsextra = &viewdetails_javascript($formname);
} elsif ($state eq 'cancel') {
$jsextra = &viewcancel_javascript($formname);
}
my $title;
if ($env{'form.crstype'} eq 'community') {
$title = 'Manage community requests';
} else {
$title = 'Manage course requests';
}
$r->print(&header($title,$js.$jscript.$jsextra,$loaditems).$crumb);
my $form = '';
if ($state eq 'pick_request') {
my $title;
if ($env{'form.crstype'} eq 'community') {
$title = &mt('Pending community requests');
} elsif ($env{'form.crstype'} eq 'official') {
$title = &mt('Pending requests for official courses');
} elsif ($env{'form.crstype'} eq 'unofficial') {
$title = &mt('Pending requests for unofficial courses');
} else {
$title = &mt('Pending course/community requests');
}
$r->print('
'.$title.'
'."\n".$form."\n".
&print_request_status($dom).'
');
} elsif ($state eq 'details') {
my (@codetitles,%cat_titles,%cat_order,@code_order,$instcode,$code_chk);
my $origcnum = $env{'form.cnum'};
if ($origcnum eq '') {
$origcnum = $env{'form.origcnum'};
}
if ($env{'form.crstype'} eq 'official') {
&Apache::lonnet::auto_possible_instcodes($dom,\@codetitles,\%cat_titles,
\%cat_order,\@code_order);
}
my $title;
if ($env{'form.crstype'} eq 'community') {
$title = &mt('Community Request Details');
} else {
$title = &mt('Course Request Details');
}
$r->print('
'.$title.'
'."\n".$form."\n".
&print_review($dom,\@codetitles,\%cat_titles,\%cat_order,
\@code_order)."\n".
''."\n");
my @excluded = &get_excluded_elements($dom,$states,'new','review');
push(@excluded,'origcnum');
$r->print(&Apache::lonhtmlcommon::echo_form_input(\@excluded).'
');
my $other = 'modify';
my %navtxt = &Apache::lonlocal::texthash (
prev => 'Back',
other => 'Modify Request',
next => 'Cancel Request',
);
&display_navbuttons($r,$dom,$formname,$prev,$navtxt{'prev'},$next,
$navtxt{'next'},$state,$other,$navtxt{'other'});
$r->print('');
} elsif ($state eq 'cancel') {
my $title;
if ($env{'form.crstype'} eq 'community') {
$title = &mt('Cancel community request');
} else {
$title = &mt('Cancel course request');
}
my ($result,$output) = &print_cancel_request($dom,$env{'form.origcnum'});
$r->print('
'.$title.'
'."\n".$form."\n".
$output);
my @excluded = &get_excluded_elements($dom,$states,'view','cancel');
$r->print(&Apache::lonhtmlcommon::echo_form_input(\@excluded).'
');
my %navtxt = &Apache::lonlocal::texthash (
prev => 'Back',
next => 'Confirm Cancellation',
);
if ($result eq 'ok') {
&display_navbuttons($r,$dom,$formname,$prev,$navtxt{'prev'},$next,
$navtxt{'next'},$state);
} else {
&display_navbuttons($r,$dom,$formname,$prev,$navtxt{'prev'},undef,
'',$state);
}
$r->print('');
} elsif ($state eq 'removal') {
my $cnum = $env{'form.origcnum'};
my $statuskey = 'status:'.$dom.':'.$cnum;
my %userreqhash = &Apache::lonnet::get('courserequests',[$statuskey],
$env{'user.domain'},$env{'user.name'});
my $currstatus = $userreqhash{$statuskey};
my ($result,$error);
if (($currstatus eq 'approval') || ($currstatus eq 'pending')) {
my %status = (
$statuskey => 'cancelled',
);
my $statusresult = &Apache::lonnet::put('courserequests',\%status);
if ($statusresult eq 'ok') {
my $delresult =
&Apache::lonnet::del_dom('courserequestqueue',
[$cnum.'_'.$currstatus],$dom);
if ($delresult eq 'ok') {
$result = 'ok';
} else {
$error = &mt('An error occurred when updating the pending requests queue: [_1]',$delresult);
}
} else {
$error = &mt("An error occurred when updating the status of this request in the requestor's records: [_1]",$statusresult);
}
} else {
$error = &mt('The current status of this request could not be verified as pending approval/institutional action.');
}
$r->print('
'.&mt('Request Cancellation').'
'."\n".$form."\n".
''."\n".
''."\n".
''."\n".
''."\n");
if ($result eq 'ok') {
if ($env{'form.crstype'} eq 'community') {
$r->print(&mt('Your community request has been cancelled.'));
} else {
$r->print(&mt('Your course request has been cancelled.'));
}
} else {
$r->print('
'.
&mt('The request cancellation process was not complete.').
' '.$error.'
'.
&close_popup_form());
} elsif ($action eq 'log') {
$r->print(&coursereq_log('View request log',$jscript,$loaditems).$crumb);
}
$r->print(&Apache::loncommon::end_page());
return;
}
sub enrollment_lcsec_js {
my %alerts = §ion_check_alerts();
my $secname = $alerts{'badsec'};
my $secnone = $alerts{'reserved'};
my $output = '
function validateEnrollSections(formname,nextstate) {
var badsectotal = 0;
var reservedtotal = 0;
var secTest = "";
';
for (my $i=0; $i<$env{'form.sectotal'}; $i++) {
$output .= "
var selSec = 0;
for (var j=0; j "You need to change one or more LON-CAPA section names - none is a reserved word in the system, and may not be used.",
badsec => 'You need to change one or more LON-CAPA section names - names may only contain letters or numbers.',
separate => 'Separate multiple sections with a comma.'
);
return %lt;
}
sub section_check_javascript {
return <<"END";
function validsection(field,mult) {
var str = field.value;
var badsec=0;
var reserved=0;
if (window.RegExp) {
var badsecnum=0;
var reservednum=0;
var pattern=/[^a-zA-Z0-9]/;
str = str.replace(/(^\\s*)|(\\s*\$)/gi,"");
str = str.replace(/[ ]{2,}/gi," ");
if (mult == '1') {
var sections = new Array();
sections = str.split(/\\s*[\\s,;:]\\s*/);
var i;
for (i=0; i 0) {
return 'badsec';
}
if (reservednum > 0) {
return 'reserved';
}
}
return;
}
END
}
sub close_popup_form {
my $close= &mt('Close Window');
return << "END";
END
}
sub get_instcode {
my ($dom) = @_;
my ($instcode,$numtitles);
my (@codetitles,%cat_titles,%cat_order,@code_order,$instcode,$code_chk);
&Apache::lonnet::auto_possible_instcodes($dom,\@codetitles,\%cat_titles,
\%cat_order,\@code_order);
$numtitles = scalar(@codetitles);
if (@code_order > 0) {
my $message;
foreach my $item (@code_order) {
$instcode .= $env{'form.instcode_'.$item};
}
}
return ($instcode,$numtitles);
}
sub print_request_form {
my ($r,$action,$state,$page,$states,$dom,$newinstcode,$codechk,$checkedcode,
$description,$invalidcrosslist) = @_;
my $formname = 'requestcrs';
my ($next,$prev,$message,$output,$codepicker,$crstype);
$prev = $states->{$action}[$page-1];
$next = $states->{$action}[$page+1];
my %navtxt = &Apache::lonlocal::texthash (
prev => 'Back',
next => 'Next',
);
$crstype = $env{'form.crstype'};
$r->print(' ');
&display_navbuttons($r,$dom,$formname,$prev,$navtxt{'prev'},$next,
$navtxt{'next'},$state);
return;
}
sub get_cancreate_status {
my ($persondom,$personname,$dom) = @_;
my ($rules,$ruleorder) =
&Apache::lonnet::inst_userrules($persondom,'username');
my $usertype = &Apache::lonuserutils::check_usertype($persondom,$personname,
$rules);
return &Apache::lonuserutils::can_create_user($dom,'requestcrs',$usertype);
}
sub check_newuser_rules {
my ($persondom,$personname,$alerts,$rulematch,$inst_results,$curr_rules,
$got_rules) = @_;
my $allowed = 1;
my $newuser = 1;
my ($checkhash,$userchkmsg);
my $checks = { 'username' => 1 };
$checkhash->{$personname.':'.$persondom} = { 'newuser' => $newuser };
&Apache::loncommon::user_rule_check($checkhash,$checks,$alerts,$rulematch,
$inst_results,$curr_rules,$got_rules);
if (ref($alerts->{'username'}) eq 'HASH') {
if (ref($alerts->{'username'}{$persondom}) eq 'HASH') {
my $domdesc =
&Apache::lonnet::domain($persondom,'description');
if ($alerts->{'username'}{$persondom}{$personname}) {
if (ref($curr_rules->{$persondom}) eq 'HASH') {
$userchkmsg =
&Apache::loncommon::instrule_disallow_msg('username',
$domdesc,1).
&Apache::loncommon::user_rule_formats($persondom,
$domdesc,$curr_rules->{$persondom}{'username'},
'username');
}
$allowed = 0;
}
}
}
return ($allowed,$userchkmsg);
}
sub get_excluded_elements {
my ($dom,$states,$action,$state) = @_;
my @excluded = ('counter');
my %elements = &form_elements($dom);
if (ref($states) eq 'HASH') {
if (ref($states->{$action}) eq 'ARRAY') {
my @items = @{$states->{$action}};
my $numitems = scalar(@items);
if ($numitems) {
for (my $i=$numitems-1; $i>=0; $i--) {
if (ref($elements{$action}) eq 'HASH') {
if (ref($elements{$action}{$items[$i]}) eq 'HASH') {
foreach my $key (keys(%{$elements{$action}{$items[$i]}})) {
push(@excluded,$key);
}
}
}
last if ($items[$i] eq $state);
}
}
}
}
if (grep(/^instcode_/,@excluded)) {
push(@excluded,'instcode');
}
return @excluded;
}
sub print_enrollment_menu {
my ($formname,$instcode,$dom,$codetitles,$cat_titles,$cat_order,$code_order,
$invalidcrosslist) =@_;
my ($sections,$autoenroll,$access_dates,$output,$hasauto);
my $starttime = time;
my $endtime = time+(6*30*24*60*60); # 6 months from now, approx
my %accesstitles = (
'start' => 'Default start access',
'end' => 'Default end access',
);
my %enrolltitles = (
'start' => 'Start auto-enrollment',
'end' => 'End auto-enrollment',
);
if ($env{'form.crstype'} eq 'official') {
if (&Apache::lonnet::auto_run('',$dom)) {
$output = &show_invalid_crosslists($invalidcrosslist);
my ($section_form,$crosslist_form);
$section_form = &inst_section_selector($dom,$instcode);
if ($section_form) {
$sections = &Apache::lonhtmlcommon::row_headline().
'
'.&Apache::loncommon::help_open_topic('Course_Request_Sections').
' '.&mt('Sections for auto-enrollment').'
'.
&Apache::lonhtmlcommon::row_closure(1).
$section_form;
}
my $crosslisttotal = $env{'form.crosslisttotal'};
if (!$crosslisttotal) {
$crosslisttotal = 1;
}
if ($env{'form.addcrosslist'}) {
$crosslisttotal ++;
}
for (my $i=0; $i<$crosslisttotal; $i++) {
$crosslist_form .= &coursecode_form($dom,'crosslist',$codetitles,
$cat_titles,$cat_order,$i);
}
if ($crosslist_form) {
$crosslist_form .=
&Apache::lonhtmlcommon::row_title(&mt('Add another')).
''.
''.&mt('Add?').&Apache::lonhtmlcommon::row_closure();
$sections .= &Apache::lonhtmlcommon::row_headline.
'
'.&mt('Crosslisted courses for auto-enrollment').'
';
}
sub show_invalid_crosslists {
my ($invalidcrosslist) = @_;
my $output;
if (ref($invalidcrosslist) eq 'ARRAY') {
if (@{$invalidcrosslist} > 0) {
$output = '
'.
&mt('The following crosslisted courses were invalid:').'
';
foreach my $item (@{$invalidcrosslist}) {
$output .= '
'.$item.'
';
}
$output .= '
';
}
}
return $output;
}
sub inst_section_selector {
my ($dom,$instcode) = @_;
my @sections = &Apache::lonnet::auto_get_sections(undef,$dom,$instcode);
my $sectotal = scalar(@sections);
my $output;
if ($sectotal) {
$output .= &Apache::lonhtmlcommon::row_title(&mt('Sections of [_1]',$instcode)).
&Apache::loncommon::start_data_table().
&Apache::loncommon::start_data_table_row().
'
'.&mt('You may also add users later, once the community has been created, by using the "Manage community users" link, accessible from the "Main Menu".').'
';
} else {
$output .= '
'.&mt('You may also add users later, once the course has been created, by using the "Manage course users" link, accessible from the "Main Menu".').'
';
}
return $output;
}
sub current_lc_sections {
my @currsecs;
if ($env{'form.sectotal'}) {
for (my $i=0; $i<$env{'form.sectotal'}; $i++) {
if ($env{'form.sec_'.$i}) {
if (defined($env{'form.loncapasec_'.$i})) {
my $lcsec = $env{'form.loncapasec_'.$i};
unless (grep(/^\Q$lcsec\E$/,@currsecs)) {
push(@currsecs,$lcsec);
}
}
}
}
}
return @currsecs;
}
sub print_request_status {
my ($dom) = @_;
my %statusinfo = &Apache::lonnet::dump('courserequests',$env{'user.domain'},
$env{'user.name'},'^status:'.$dom);
my ($output,$formname,%queue_by_date);
my ($types,$typenames) = &Apache::loncommon::course_types();
foreach my $key (keys(%statusinfo)) {
if (($statusinfo{$key} eq 'approval') || ($statusinfo{$key} eq 'pending')) {
(undef,my($cdom,$cnum)) = split(':',$key);
next if ($cdom ne $dom);
my $requestkey = $cdom.'_'.$cnum;
if ($requestkey =~ /^($match_domain)_($match_courseid)$/) {
my %history = &Apache::lonnet::restore($requestkey,'courserequests',
$env{'user.domain'},$env{'user.name'});
my $entry;
my $timestamp = $history{'reqtime'};
my $crstype = $history{'crstype'};
my $disposition = $history{'disposition'};
next if ((exists($history{'status'})) && ($history{'status'} eq 'created'));
next unless (($env{'form.crstype'} eq 'any') ||
($env{'form.crstype'} eq $crstype));
next unless (($disposition eq 'approval') ||
($disposition eq 'pending'));
if (ref($history{'details'}) eq 'HASH') {
$entry = $requestkey.':'.$crstype.':'.
&escape($history{'details'}{'cdescr'});
if ($crstype eq 'official') {
$entry .= ':'.&escape($history{'details'}{'instcode'});
}
}
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);
}
}
}
}
}
$formname = 'requestcrs';
my @sortedtimes = sort {$a <=> $b} (keys(%queue_by_date));
$output = ''."\n".
''."\n".
''."\n".
''."\n".
''."\n";
if (@sortedtimes > 0) {
my $desctitle;
if ($env{'form.crstype'} eq 'any') {
$desctitle = &mt('Course/Community Description')
} elsif ($env{'form.crstype'} eq 'community') {
$desctitle = &mt('Community Description')
} else {
$desctitle = &mt('Course Description');
}
$output .= &Apache::loncommon::start_data_table().
&Apache::loncommon::start_data_table_header_row().
'
'.&mt('Action').'
'.
'
'.$desctitle.'
'.
'
'.&mt('Domain').'
';
if ($env{'form.crstype'} eq 'any') {
$output .= '
'.&mt('You have no matching course or community requests awaiting approval by a Domain Coordinator or held in a queue pending administrative action at your institution.').'
'.&mt('You have no matching community requests awaiting approval by a Domain Coordinator or held in a queue pending administrative action at your institution.').'
';
} else {
$output .= '
'.&mt('You have no matching course requests awaiting approval by a Domain Coordinator or held in a queue pending administrative action at your institution.').'
';
}
}
$output .= '
';
return $output;
}
sub print_cancel_request {
my ($dom,$cnum) = @_;
my $requestkey = $dom.'_'.$cnum;
my ($result,$output);
if ($requestkey =~ /^($match_domain)_($match_courseid)$/) {
my %history = &Apache::lonnet::restore($requestkey,'courserequests',
$env{'user.domain'},$env{'user.name'});
my $timestamp = $history{'reqtime'};
my $crstype = $history{'crstype'};
my $status = $history{'status'};
if (($status eq 'cancelled') || ($status eq 'created')) {
if ($status eq 'cancelled') {
$output = &mt('This request has already been cancelled.');
} elsif ($status eq 'created') {
$output = &mt('This request has already been processed, and a course created.');
}
$output = &mt('No further action will be taken');
} elsif (ref($history{'details'}) eq 'HASH') {
my ($types,$typename) = &Apache::loncommon::course_types();
my $showtype = $crstype;
if (defined($typename->{$crstype})) {
$showtype = $typename->{$crstype};
}
$output = '
';
if ($crstype eq 'community') {
$output .= &mt('Cancelling the request will remove it from the queue of pending community requests').'
';
} else {
$output .= &mt('Cancelling the request will remove it from the queue of pending course requests').'';
}
$result = 'ok';
} else {
$output = '
'.&mt('No record exists for the course ID').'
';
}
} else {
$output = '
'.&mt('Invalid course ID').'
';
}
return ($result,$output);
}
sub viewrequest_javascript {
my ($formname,$next) = @_;
return <<"ENDJS";
function chooseRequest(cdom,cnum) {
document.$formname.showdom.value = cdom;
document.$formname.cnum.value = cnum;
nextPage(document.$formname,'$next');
}
ENDJS
}
sub viewdetails_javascript {
my ($formname) = @_;
return << "ENDJS";
function nextPage(formname,nextstate) {
if (nextstate == "modify") {
formname.state.value = "personnel";
formname.action.value = "new";
} else {
formname.state.value = nextstate;
}
formname.submit();
}
function backPage(formname,prevstate) {
formname.state.value = prevstate;
formname.submit();
}
ENDJS
}
sub viewcancel_javascript {
my $alert = &mt('Are you sure you want to cancel this request?\\n'.
'Your request will be removed.');
return << "ENDJS";
function nextPage(formname,nextstate) {
if (confirm('$alert')) {
formname.state.value = nextstate;
formname.submit();
}
return;
}
ENDJS
}
sub print_request_logs {
my ($jscript,$loaditems,$crumb) = @_;
return;
}
sub print_review {
my ($dom,$codetitles,$cat_titles,$cat_order,$code_order,$uname,$udom,
$disallowed,$disallowmsg) = @_;
my ($types,$typename) = &Apache::loncommon::course_types();
my ($owner,$ownername,$owneremail);
if ($uname eq '' || $udom eq '') {
$uname = $env{'user.name'};
$udom = $env{'user.domain'};
}
$owner = $uname.':'.$udom;
$ownername = &Apache::loncommon::plainname($uname,$udom,'first');
my %emails = &Apache::loncommon::getemails($uname,$udom);
foreach my $email ('permanentemail','critnotification','notification') {
$owneremail = $emails{$email};
last if ($owneremail ne '');
}
my ($inst_headers,$inst_values,$crstypename,$enroll_headers,$enroll_values,
$section_headers,$section_values,$personnel_headers,$personnel_values);
$crstypename = $env{'form.crstype'};
if (ref($typename) eq 'HASH') {
unless ($typename->{$env{'form.crstype'}} eq '') {
$crstypename = &mt($typename->{$env{'form.crstype'}});
}
}
my $category = 'Course';
if ($env{'form.crstype'} eq 'community') {
$category = 'Community';
}
$inst_headers = '
'.&mt('Description').'
'.&mt('Type').'
';
$inst_values = '
'.$env{'form.cdescr'}.'
'.$crstypename.'
';
my $enrollrow_title = &mt('Default Access Dates').' '.
'('.&Apache::lonnet::plaintext('st',$category).')';
if ($env{'form.crstype'} eq 'official') {
if ((ref($codetitles) eq 'ARRAY') && (ref($cat_titles) eq 'HASH')) {
foreach my $title (@{$codetitles}) {
if ($env{'form.instcode_'.$title} ne '') {
$inst_headers .= '
'.$title.'
';
my $longitem = $env{'form.instcode_'.$title};
if (ref($cat_titles->{$title}) eq 'HASH') {
if ($cat_titles->{$title}{$env{'form.instcode_'.$title}} ne '') {
$longitem = $cat_titles->{$title}{$env{'form.instcode_'.$title}};
}
}
$inst_values .= '
';
for (my $i=0; $i<$env{'form.persontotal'}; $i++) {
if ($env{'form.person_'.$i.'_uname'} ne '') {
if (ref($disallowed) eq 'ARRAY') {
next if (grep(/^$i$/,@{$disallowed}));
}
my @officialsecs = &Apache::loncommon::get_env_multiple('form.person_'.$i.'_sec');
my @allsecs;
foreach my $sec (@officialsecs) {
next unless ($sec =~ /\w/);
next if ($sec =~ /\W/);
next if ($sec eq 'none');
push(@allsecs,$sec);
}
my $newsec = $env{'form.person_'.$i.'_newsec'};
$newsec =~ s/^\s+//;
$newsec =~s/\s+$//;
my @newsecs = split(/\s*[\s,;:]\s*/,$newsec);
foreach my $sec (@newsecs) {
next unless ($sec =~ /\w/);
next if ($sec =~ /\W/);
next if ($sec eq 'none');
if ($sec ne '') {
unless (grep(/^\Q$sec\E$/,@allsecs)) {
push(@allsecs,$sec);
}
}
}
my $showsec;
if (@allsecs) {
$showsec = join(', ',@allsecs);
}
if ($showsec eq '') {
$showsec = &mt('None');
}
if ($env{'form.person_'.$i.'_role'} eq $ccrole) {
$showsec = &mt('None');
}
my $role = $env{'form.person_'.$i.'_role'};
$personnel_values .=
'
';
return $output;
}
sub dates_from_form {
my ($startname,$endname) = @_;
my $startdate = &Apache::lonhtmlcommon::get_date_from_form($startname);
my $enddate = &Apache::lonhtmlcommon::get_date_from_form($endname);
if ($endname eq 'accessend') {
if (exists($env{'form.no_end_date'}) ) {
$enddate = 0;
}
}
return ($startdate,$enddate);
}
sub courseinfo_form {
my ($dom,$formname,$crstype,$next,$description) = @_;
my %lt = &Apache::lonlocal::texthash(
official => 'You must provide a (brief) course description.',
community => 'You must provide a (brief) community description.'
);
$lt{'unofficial'} = $lt{'official'};
my $js_validate = <<"ENDJS";
ENDJS
my $title = &mt('Brief Course Description');
my $clonetitle = &mt('Clone content and settings from an existing course?');
if ($crstype eq 'community') {
$title = &mt('Brief Community Description');
$clonetitle = &mt('Clone content and settings from an existing community?');
}
my $output .= $js_validate."\n".'
'.$showrole;
if ($usec ne '') {
$future .= ' - '.&mt('section:').' '.$usec;
}
$future .= '
';
$numfuture ++;
}
}
}
}
}
}
if ($active) {
if ($numactive == 1) {
if ($crstype eq 'Community') {
$output = &mt('Use the following link to enter the community:');
} else {
$output = &mt('Use the following link to enter the course:');
}
} else {
if ($crstype eq 'Community') {
$output = &mt('Use the following links to your new roles to enter the community:');
} else {
$output = &mt('Use the following links to your new roles to enter the course:');
}
}
$output .= '
'.$active.'
';
}
if ($future) {
if ($crstype eq 'Community') {
$output .= &mt('The following community [quant,_1,role] will become available for selection from your [_2]roles page[_3], once the default student access start date - [_4] - has been reached:',$numfuture,'','',&Apache::lonlocal::locallocaltime($details->{'accessstart'}))
} else {
$output .= &mt('The following course [quant,_1,role] will become available for selection from your [_2]roles page[_3], once the default student access start date - [_4] - has been reached:',$numfuture,'','',&Apache::lonlocal::locallocaltime($details->{'accessstart'}));
}
$output .= '
'.$future.'
';
}
return $output;
}
sub notification_information {
my ($disposition,$req_notifylist,$cnum,$now) = @_;
my %emails = &Apache::loncommon::getemails();
my $address;
if (($emails{'permanentemail'} ne '') || ($emails{'notification'} ne '')) {
$address = $emails{'permanentemail'};
if ($address eq '') {
$address = $emails{'notification'};
}
}
my $output;
if ($disposition eq 'approval') {
$output .= &mt('A message will be sent to your LON-CAPA account when a domain coordinator takes action on your request.').' '.
&mt('To access your LON-CAPA message, go to the Main Menu and click on "Send and Receive Messages".').' ';
if ($address ne '') {
$output.= &mt('An e-mail will also be sent to: [_1] when this occurs.',$address).' ';
}
if ($req_notifylist) {
my $fullname = &Apache::loncommon::plainname($env{'user.name'},
$env{'user.domain'});
my $sender = $env{'user.name'}.':'.$env{'user.domain'};
&Apache::loncoursequeueadmin::send_selfserve_notification($req_notifylist,"$fullname ($env{'user.name'}:$env{'user.domain'})",$cnum,$env{'form.cdescr'},$now,'coursereq',$sender);
}
} elsif ($disposition eq 'pending') {
$output .= '
'.
&mt('Your request has been placed in a queue pending administrative action.').' '.
&mt("Usually this means that your institution's information systems do not list you among the instructional personnel for this course.").' '.
&mt('The list of instructional personnel for the course will be automatically checked daily, and once you are listed the request will be processed.').
'
';
} else {
$output .= '
'.
&mt('Your request status is: [_1].',$disposition).
'
'
}
return $output;
}
sub check_autolimit {
my ($uname,$udom,$dom,$crstype,$limit,$message) = @_;
my %crsroles = &Apache::lonnet::get_my_roles($env{'user.name'},$env{'user.domain'},
'userroles',['active','future'],['cc','co'],[$dom]);
my ($types,$typename) = &Apache::loncommon::course_types();
my %requests = &Apache::lonnet::dumpstore('courserequests',$udom,$uname);
my $count = 0;
foreach my $key (keys(%requests)) {
my ($cdom,$cnum) = split('_',$key);
if (ref($requests{$key}) eq 'HASH') {
next if ($requests{$key}{'crstype'} ne $crstype);
if (($crstype eq 'community') &&
(exists($crsroles{$cnum.':'.$cdom.':co'}))) {
$count ++;
} elsif ((($crstype eq 'official') || ($crstype eq 'unofficial')) &&
(exists($crsroles{$cnum.':'.$cdom.':cc'}))) {
$count ++;
}
}
}
if ($count < $limit) {
return 'process';
} else {
if (ref($typename) eq 'HASH') {
if ($crstype eq 'community') {
$$message = &mt('Your request has not been processed because you have reached the limit for the number of communities.').
' '.&mt("Your limit is [_1].",$limit);
} else {
$$message = &mt('Your request has not been processed because you have reached the limit for the number of courses of this type.').
' '.&mt("Your $typename->{$crstype} limit is [_1].",$limit);
}
}
return 'rejected';
}
return;
}
sub retrieve_settings {
my ($dom,$cnum,$udom,$uname) = @_;
if ($udom eq '' || $uname eq '') {
$udom = $env{'user.domain'};
$uname = $env{'user.name'};
}
my ($result,%reqinfo) = &get_request_settings($dom,$cnum,$udom,$uname);
if ($result eq 'ok') {
if (($udom eq $reqinfo{'domain'}) && ($uname eq $reqinfo{'owner'})) {
$env{'form.chome'} = $reqinfo{'coursehome'};
$env{'form.cdescr'} = $reqinfo{'cdescr'};
$env{'form.crstype'} = $reqinfo{'crstype'};
&generate_date_items($reqinfo{'accessstart'},'accessstart');
&generate_date_items($reqinfo{'accessend'},'accessend');
if ($reqinfo{'accessend'} == 0) {
$env{'form.no_end_date'} = 1;
}
if (($reqinfo{'crstype'} eq 'official') && (&Apache::lonnet::auto_run('',$dom))) {
&generate_date_items($reqinfo{'enrollstart'},'enrollstart');
&generate_date_items($reqinfo{'enrollend'},'enrollend');
}
$env{'form.clonecrs'} = $reqinfo{'clonecrs'};
$env{'form.clonedom'} = $reqinfo{'clonedom'};
$env{'form.datemode'} = $reqinfo{'datemode'};
$env{'form.dateshift'} = $reqinfo{'dateshift'};
if (($reqinfo{'crstype'} eq 'official') && ($reqinfo{'instcode'} ne '')) {
$env{'form.sectotal'} = $reqinfo{'sectotal'};
$env{'form.crosslisttotal'} = $reqinfo{'crosslisttotal'};
$env{'form.autoadds'} = $reqinfo{'autoadds'};
$env{'form.autdrops'} = $reqinfo{'autodrops'};
$env{'form.instcode'} = $reqinfo{'instcode'};
my $crscode = {
$cnum => $reqinfo{'instcode'},
};
&extract_instcode($dom,'instcode',$crscode,$cnum);
}
my @currsec;
if (ref($reqinfo{'sections'}) eq 'HASH') {
foreach my $i (sort(keys(%{$reqinfo{'sections'}}))) {
if (ref($reqinfo{'sections'}{$i}) eq 'HASH') {
my $sec = $reqinfo{'sections'}{$i}{'inst'};
$env{'form.secnum_'.$i} = $sec;
$env{'form.sec_'.$i} = '1';
if (!grep(/^\Q$sec\E$/,@currsec)) {
push(@currsec,$sec);
}
$env{'form.loncapasec_'.$i} = $reqinfo{'sections'}{$i}{'loncapa'};
}
}
}
if (ref($reqinfo{'crosslists'}) eq 'HASH') {
foreach my $i (sort(keys(%{$reqinfo{'crosslists'}}))) {
if (ref($reqinfo{'crosslists'}{$i}) eq 'HASH') {
$env{'form.crosslist_'.$i} = '1';
$env{'form.crosslist_'.$i.'_instsec'} = $reqinfo{'crosslists'}{$i}{'instsec'};
$env{'form.crosslist_'.$i.'_lcsec'} = $reqinfo{'crosslists'}{$i}{'loncapa'};
if ($reqinfo{'crosslists'}{$i}{'instcode'} ne '') {
my $key = $cnum.$i;
my $crscode = {
$key => $reqinfo{'crosslists'}{$i}{'instcode'},
};
&extract_instcode($dom,'crosslist',$crscode,$key,$i);
}
}
}
}
if (ref($reqinfo{'personnel'}) eq 'HASH') {
my $i = 0;
foreach my $user (sort(keys(%{$reqinfo{'personnel'}}))) {
my ($uname,$udom) = split(':',$user);
if (ref($reqinfo{'personnel'}{$user}) eq 'HASH') {
if (ref($reqinfo{'personnel'}{$user}{'roles'}) eq 'ARRAY') {
foreach my $role (sort(@{$reqinfo{'personnel'}{$user}{'roles'}})) {
$env{'form.person_'.$i.'_role'} = $role;
$env{'form.person_'.$i.'_firstname'} = $reqinfo{'personnel'}{$user}{'firstname'};
$env{'form.person_'.$i.'_lastname'} = $reqinfo{'personnel'}{$user}{'lastname'}; ;
$env{'form.person_'.$i.'_emailaddr'} = $reqinfo{'personnel'}{$user}{'emailaddr'};
$env{'form.person_'.$i.'_uname'} = $uname;
$env{'form.person_'.$i.'_dom'} = $udom;
if (ref($reqinfo{'personnel'}{$user}{$role}) eq 'HASH') {
if (ref($reqinfo{'personnel'}{$user}{$role}{'usec'}) eq 'ARRAY') {
my @usecs = @{$reqinfo{'personnel'}{$user}{$role}{'usec'}};
my @newsecs;
if (@usecs > 0) {
foreach my $sec (@usecs) {
if (grep(/^\Q$sec\E/,@currsec)) {
$env{'form.person_'.$i.'_sec'} = $sec;
} else {
push(@newsecs,$sec);
}
}
}
if (@newsecs > 0) {
$env{'form.person_'.$i.'_newsec'} = join(',',@newsecs);
}
}
}
$i ++;
}
}
}
}
$env{'form.persontotal'} = $i;
}
}
}
return $result;
}
sub get_request_settings {
my ($dom,$cnum,$udom,$uname) = @_;
my $requestkey = $dom.'_'.$cnum;
my ($result,%reqinfo);
if ($requestkey =~ /^($match_domain)_($match_courseid)$/) {
my %history = &Apache::lonnet::restore($requestkey,'courserequests',$udom,$uname);
my $disposition = $history{'disposition'};
if (($disposition eq 'approval') || ($disposition eq 'pending')) {
if (ref($history{'details'}) eq 'HASH') {
%reqinfo = %{$history{'details'}};
$result = 'ok';
} else {
$result = 'nothash';
}
} else {
$result = 'notqueued';
}
} else {
$result = 'invalid';
}
return ($result,%reqinfo);
}
sub extract_instcode {
my ($cdom,$element,$crscode,$crskey,$counter) = @_;
my (%codes,@codetitles,%cat_titles,%cat_order);
if (&Apache::lonnet::auto_instcode_format('requests',$cdom,$crscode,\%codes,
\@codetitles,\%cat_titles,
\%cat_order) eq 'ok') {
if (ref($codes{$crskey}) eq 'HASH') {
if (@codetitles > 0) {
my $sel = $element;
if ($element eq 'crosslist') {
$sel .= '_'.$counter;
}
foreach my $title (@codetitles) {
$env{'form.'.$sel.'_'.$title} = $codes{$crskey}{$title};
}
}
}
}
return;
}
sub generate_date_items {
my ($currentval,$item) = @_;
if ($currentval =~ /\d+/) {
my ($tzname,$sec,$min,$hour,$mday,$month,$year) =
&Apache::lonhtmlcommon::get_timedates($currentval);
$env{'form.'.$item.'_day'} = $mday;
$env{'form.'.$item.'_month'} = $month+1;
$env{'form.'.$item.'_year'} = $year;
}
return;
}
1;