--- loncom/interface/lonrequestcourse.pm 2009/09/10 03:00:31 1.34 +++ loncom/interface/lonrequestcourse.pm 2025/01/10 22:45:55 1.118 @@ -1,7 +1,7 @@ # The LearningOnline Network # Request a course # -# $Id: lonrequestcourse.pm,v 1.34 2009/09/10 03:00:31 raeburn Exp $ +# $Id: lonrequestcourse.pm,v 1.118 2025/01/10 22:45:55 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -52,10 +52,6 @@ described at http://www.lon-capa.org. =item onload_action() -=item check_can_request() - -=item course_types() - =item print_main_menu() =item request_administration() @@ -98,8 +94,6 @@ described at http://www.lon-capa.org. =item print_request_outcome() -=item get_processtype() - =item check_autolimit() =item retrieve_settings() @@ -125,6 +119,10 @@ use Apache::loncoursequeueadmin; use Apache::lonuserutils; use LONCAPA qw(:DEFAULT :match); +my $registered_flush; +my $registered_instcats; +my $modified_dom; + sub handler { my ($r) = @_; &Apache::loncommon::content_type($r,'text/html'); @@ -133,18 +131,105 @@ sub handler { return OK; } + $registered_flush = 0; + $registered_instcats = 0; + $modified_dom = ''; + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, - ['action','showdom','cnum','state']); + ['action','showdom','cnum','state','crstype','queue','tabs']); &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); + my ($jscript,$uname,$udom,$result,$warning,$showcredits,$instcredits,%can_request, + %request_domains,@incdoms); + my %domdefs = &Apache::lonnet::get_domain_defaults($dom); + if ($domdefs{'officialcredits'} || $domdefs{'unofficialcredits'} || $domdefs{'textbookcredits'}) { + $showcredits = 1; + } + + my $canreq = + &Apache::lonnet::check_can_request($dom,\%can_request,\%request_domains); + + foreach my $item (keys(%request_domains)) { + if (ref($request_domains{$item}) eq 'ARRAY') { + foreach my $possdom (@{$request_domains{$item}}) { + unless(grep(/^\Q$possdom\E$/,@incdoms)) { + push(@incdoms,$possdom); + } + } + } + } + + if ($canreq) { + if (($env{'form.crstype'} eq 'lti') && ($env{'request.lti.login'}) && + ($env{'form.lti.reqrole'} eq 'cc') && ($env{'form.lti.reqcrs'}) && + ($env{'form.lti.sourcecrs'} ne '')) { + if ($action eq 'process') { + if ($can_request{'lti'}) { + my %domconfig = &Apache::lonnet::get_dom('configuration',['requestcourses'],$dom); + &process_textbook_request($r,$dom,$action,\%domdefs,\%domconfig,\%can_request,'lti'); + } else { + $r->print(&header('Course Request','','','',{ 'only_body' => 1}). + '
'.&mt('You do not have privileges to request creation of LTI courses.').'
'. + ''.&mt('Request creation of a new course, or manage pending course requests.').'
'. - ''.$pageinfo.'
'); + if ($earlyout) { + $r->print(&Apache::loncommon::end_page()); + return; + } + $r->print(''.&mt('Previous [_1] changes',$curr{'show'}).' | '); + } + if ($more_records) { + $r->print(''.&mt('Next [_1] changes',$curr{'show'}).' | '); + } + $r->print('
'. + &mt('There are no records to display'). + '
'); + } + if ($usetabs) { + $r->print(''); + } + $r->print(''.
+ ''.&mt('Records/page:').' '. + &Apache::lonmeta::selectbox('show',$curr->{'show'},'',undef, + (&mt('all'),5,10,20,50,100,1000,10000)). + ' | '; + my $startform = + &Apache::lonhtmlcommon::date_setter($formname,'requested_after_date', + $curr->{'requested_after_date'},undef, + undef,undef,undef,undef,undef,undef,$nolink); + my $endform = + &Apache::lonhtmlcommon::date_setter($formname,'requested_before_date', + $curr->{'requested_before_date'},undef, + undef,undef,undef,undef,undef,undef,$nolink); + $output .= ' | '.&mt('Window during which course/community was requested:').' '. + '
| '.
+ ''; + my ($types,$typenames) = &Apache::loncommon::course_types(); + if (ref($types) eq 'ARRAY') { + if (@{$types} > 1) { + $output .= ' | '.
+ &mt('Course Type:').' | ';
+ }
+ }
+ my ($statuses,$statusnames) = &reqstatus_names($curr->{'crstype'});
+ if (ref($statuses) eq 'ARRAY') {
+ if (@{$statuses} > 1) {
+ $output .= ''.
+ &mt('Request Status:').' | ';
+ }
+ }
+ $output .= '
'. + ''. + '
'.&mt('Include?').' '. @@ -2418,6 +3354,26 @@ sub coursecode_form { return $output; } +sub sections_form { + my ($dom,$instcode,$num) = @_; + my $rowtitle; + if ($instcode eq '') { + $rowtitle = &mt('Sections'); + } else { + $rowtitle = &mt('Sections of [_1]',$instcode); + } + return &Apache::lonhtmlcommon::row_title($rowtitle). + '
'.$message.' ';
}
$storeresult = 'rejected';
} elsif ($disposition eq 'process') {
my %domdefs = &Apache::lonnet::get_domain_defaults($dom);
- my ($logmsg,$newusermsg,$addresult,$enrollcount,$response,$keysmsg,%longroles);
- my @roles = &Apache::lonuserutils::roles_by_context('course');
+ my ($logmsg,$newusermsg,$addresult,$enrollcount,$response,
+ $keysmsg,%longroles,$code);
+ my $clonemsg = [];
my $type = 'Course';
if ($crstype eq 'community') {
$type = 'Community';
}
+ my @roles = &Apache::lonuserutils::roles_by_context('course','',$type);
foreach my $role (@roles) {
$longroles{$role}=&Apache::lonnet::plaintext($role,$type);
}
- my $result = &Apache::loncoursequeueadmin::course_creation($dom,$cnum,
- 'autocreate',$details,\$logmsg,\$newusermsg,\$addresult,
- \$enrollcount,\$response,\$keysmsg,\%domdefs,\%longroles);
+ my $preamble = ''.
+ ' '.
+ '';
+ my $closure = <'. + &mt("Please be patient while your request is processed"). + ' '.&mt('Your course request has been processed and the course has been created.').
- ' '.&mt('Your community request has been processed and the community has been created.'); + } else { + $output = ' '.&mt('Your course request has been processed and the course has been created.');
+ }
+ if (($code) || ((ref($postprocess) eq 'HASH') &&
+ (($postprocess->{'createdweb'}) || ($postprocess->{'createdmsg'})))) {
+ $output .= ¬ification_information($disposition,$env{'user.name'}.':'.$env{'user.domain'},
+ $dom,$cnum,$now,$code,$postprocess,$crstype);
+ }
+ if ($code) {
+ $reqhash{'code'} = $code;
+ }
+ if (ref($postprocess) eq 'HASH') {
+ if (ref($postprocess->{'createdactions'}) eq 'HASH') {
+ if (ref($postprocess->{'createdactions'}{'environment'}) eq 'HASH') {
+ &Apache::loncoursequeueadmin::postprocess_crsenv($dom,$cnum,
+ $postprocess->{'createdactions'}{'environment'});
+ }
+ }
+ }
+ unless ($customized) {
+ $output .= ' '.$logmsg.' '; + } + if ((ref($clonemsg) eq 'ARRAY') && (@{$clonemsg})) { + $output .= '';
+ my $user_lh = &Apache::loncommon::user_lang($env{'user.name'},$env{'user.domain'});
+ foreach my $item (@{$clonemsg}) {
+ if (ref($item) eq 'HASH') {
+ $output .= &mt_user($user_lh,$item->{mt},
+ @{$item->{args}}).' '. - &mt('You may want to review the request details and submit the request again.'). + $output = ''; + if ($crstype eq 'community') { + $output .= &mt('An error occurred when processing your community request.'); + } else { + $output .= &mt('An error occurred when processing your course request.'); + } + $output .= ' '. + &mt('You may want to review the request details and submit the request again.'). ''; $creationresult = 'error'; } } else { my $requestid = $cnum.'_'.$disposition; - my $request = { + my $request = { $requestid => { timestamp => $now, crstype => $crstype, ownername => $env{'user.name'}, ownerdom => $env{'user.domain'}, - description => $env{'form.cdescr'}, + description => $env{'form.cdescr'}, + lonhost => $lonhost, }, }; + if ($crstype eq 'official') { + $request->{$requestid}->{'instcode'} = $instcode; + } my $statuskey = 'status:'.$dom.':'.$cnum; my %userreqhash = &Apache::lonnet::get('courserequests',[$statuskey], $env{'user.domain'},$env{'user.name'}); @@ -2798,15 +4008,32 @@ sub print_request_outcome { if (($queuehash{$cnum.'_approval'} ne '') || ($queuehash{$cnum.'_pending'} ne '')) { $queued = 1; + if (ref($queuehash{$cnum.'_pending'}) eq 'HASH') { + $token = $queuehash{$cnum.'_pending'}{'token'}; + } } } unless ($queued) { + if (($disposition eq 'pending') && ($crstype ne 'official')) { + my %reqinfo = ( + $cnum.':'.$dom => $now.':'.$env{'user.name'}.':'.$env{'user.domain'}, + ); + $token = &Apache::lonnet::tmpput(\%reqinfo,$lonhost); + $request->{$requestid}->{'token'} = $token; + } my $putresult = &Apache::lonnet::newput_dom('courserequestqueue',$request, $dom); if ($putresult eq 'ok') { - $output = &mt('Your course request has been recorded.').' '. - ¬ification_information($disposition,$req_notifylist, - $cnum,$now); + if ($crstype eq 'community') { + $output .= &mt('Your community request has been recorded.'); + } else { + $output .= &mt('Your course request has been recorded.') + } + unless ($disposition eq 'pending') { + $output .= ' '. + ¬ification_information($disposition,$req_notifylist, + $dom,$cnum,$now,'','',$crstype); + } } else { $reqstatus = 'domainerror'; $reqhash{'disposition'} = $disposition; @@ -2815,38 +4042,106 @@ sub print_request_outcome { } } } - my ($statusresult); - if ($requestkey =~ /^($match_domain)_($match_courseid)$/) { - $storeresult = &Apache::lonnet::store_userdata(\%reqhash,$requestkey, - 'courserequests'); - if ($storeresult eq 'ok') { - my %status = ( - 'status:'.$dom.':'.$cnum => $reqstatus, - ); - $statusresult = &Apache::lonnet::put('courserequests',\%status); + ($storeresult,my $updateresult) = + &Apache::loncoursequeueadmin::update_coursereq_status(\%reqhash,$dom, + $cnum,$reqstatus,'request',$env{'user.domain'},$env{'user.name'}); + if ($storeresult eq 'ok') { + my $postprocess; + if (($disposition eq 'approval') || ($disposition eq 'pending')) { + my $updateaction = $disposition; + if ($disposition eq 'approval') { + $updateaction = 'queued'; + } + my $fullname = &Apache::loncommon::plainname($env{'user.name'}, + $env{'user.domain'}); + $postprocess = + &Apache::lonnet::auto_crsreq_update($dom,$cnum,$crstype,$updateaction,$env{'user.name'}, + $env{'user.domain'},$fullname,$env{'form.cdescr'}); + } + if ($modified && $queued) { + if ($crstype eq 'community') { + $output .= ' '.&mt('Your community request has been updated').' '; + } else { + $output .= ''.&mt('Your course request has been updated').' '; + } + if ($disposition eq 'approval') { + $output .= ¬ification_information($disposition,$req_notifylist,$dom,$cnum,$now,'','',$crstype); + } + } + if ($disposition eq 'approval') { + if ((ref($postprocess) eq 'HASH') && + ((ref($postprocess->{'queuedmsg'}) eq 'HASH') || ($postprocess->{'queuedweb'}))) { + ¬ification_information($disposition,undef,$dom,$cnum,$now,undef,$postprocess,$crstype); + $customized = $postprocess->{'createdcustomized'}; + } + } elsif ($disposition eq 'pending') { + my $pendingform; + if ($crstype ne 'official') { + $pendingform = &pending_validation_form($r,$dom,$cnum,$crstype,$now,$token, + $lonhost,$env{'form.cdescr'}); + } + if ($pendingform) { + $output .= $pendingform; + } else { + $output .= ¬ification_information($disposition,undef,$dom,$cnum,$now,undef,$postprocess,$crstype); + } + if (ref($postprocess) eq 'HASH') { + $customized = $postprocess->{'createdcustomized'}; + } } - } else { - $storeresult = 'error: invalid requestkey format'; - } - if ($storeresult ne 'ok') { - $output .= ''.&mt('An error occurred saving a record of the details of your request: [_1].',$storeresult).''; - &Apache::lonnet::logthis("Error saving course request - $requestkey for $env{'user.name'}:$env{'user.domain'} - $storeresult"); - } elsif ($statusresult ne 'ok') { - $output .= ''.&mt('An error occurred saving a record of the status of your request: [_1].',$statusresult).' '; - &Apache::lonnet::logthis("Error saving course request status for $requestkey (for $env{'user.name'}:$env{'user.domain'}) - $statusresult"); - } - if ($modified && $queued && $storeresult eq 'ok') { - $output .= ' '.&mt('Your course request has been updated').' '. - ¬ification_information($disposition,$req_notifylist,$cnum,$now); } if ($validationerror ne '') { - $output .= ''.&mt('An error occurred validating your request with institutional data sources: [_1].',$validationerror).''; + $output .= ''.&mt('An error occurred validating your request with institutional data sources: [_1].',$validationerror).' '; + } + if ($updateresult) { + $output .= $updateresult; } } if ($creationresult ne '') { - return ($creationresult,$output); + return ($creationresult,$output,$customized); } else { - return ($storeresult,$output); + return ($storeresult,$output,$customized); + } +} + +sub devalidate_remote_instcats { + if ($modified_dom ne '') { + my %servers = &Apache::lonnet::internet_dom_servers($modified_dom); + my %thismachine; + map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids(); + if (keys(%servers)) { + foreach my $server (keys(%servers)) { + next if ($thismachine{$server}); + &Apache::lonnet::remote_devalidate_cache($server,['instcats:'.$modified_dom]); + } + } + $modified_dom = ''; + } + return; +} + +sub custom_formitems { + my ($preprocess,$customhash) = @_; + return unless ((ref($preprocess) eq 'HASH') && (ref($customhash) eq 'HASH')); + if (ref($preprocess->{'formitems'}) eq 'HASH') { + foreach my $key (keys(%{$preprocess->{'formitems'}})) { + if ($preprocess->{'formitems'}->{$key} eq 'multiple') { + if (exists($env{'form.'.$key})) { + my @items = &Apache::loncommon::get_env_multiple($env{'form.'.$key}); + foreach my $item (@items) { + $item =~ s/(`)/'/g; + $item =~ s/\$/\(\$\)/g; + push(@{$customhash->{$key}},$item); + } + } + } else { + if (exists($env{'form.'.$key})) { + $customhash->{$key} = $env{'form.'.$key}; + $customhash->{$key} =~ s/(`)/'/g; + $customhash->{$key} =~ s/\$/\(\$\)/g; + } + } + } } } @@ -2857,85 +4152,101 @@ sub update_requestors_roles { my $owner = $env{'user.name'}.':'.$env{'user.domain'}; if (ref($details) eq 'HASH') { if (ref($details->{'personnel'}) eq 'HASH') { - if (ref($details->{'personnel'}{$owner}) eq 'HASH') { - my @roles; - if (ref($details->{'personnel'}{$owner}{'roles'}) eq 'ARRAY') { - @roles = sort(@{$details->{'personnel'}{$owner}{'roles'}}); - unless (grep(/^cc$/,@roles)) { - push(@roles,'cc'); - } - } else { - @roles = ('cc'); - } - foreach my $role (@roles) { - my $start = $now; - my $end = '0'; - if ($role eq 'st') { - if ($details->{'accessstart'} ne '') { - $start = $details->{'accessstart'}; - } - if ($details->{'accessend'} ne '') { - $end = $details->{'accessend'}; - } - } - my @usecs; - if ($role ne 'cc') { - if (ref($details->{'personnel'}{$owner}{$role}{'usec'}) eq 'ARRAY') { - @usecs = @{$details->{'personnel'}{$owner}{$role}{'usec'}}; - } - } - if ($role eq 'st') { - if (@usecs > 1) { - my $firstsec = $usecs[0]; - @usecs = ($firstsec); - } - } - if (@usecs == 0) { - push(@usecs,''); - } - foreach my $usec (@usecs) { - my (%userroles,%newrole,%newgroups,$spec,$area); - my $area = '/'.$dom.'/'.$cnum; - my $spec = $role.'.'.$area; - if ($usec ne '') { - $spec .= '/'.$usec; - $area .= '/'.$usec; - } + my $ccrole = 'cc'; + if ($crstype eq 'community') { + $ccrole = 'co'; + } + unless (ref($details->{'personnel'}{$owner}) eq 'HASH') { + $details->{'personnel'}{$owner} = { + 'roles' => [$ccrole], + $ccrole => { 'usec' => [] }, + }; + } + my @roles; + if (ref($details->{'personnel'}{$owner}{'roles'}) eq 'ARRAY') { + @roles = sort(@{$details->{'personnel'}{$owner}{'roles'}}); + unless (grep(/^\Q$ccrole\E$/,@roles)) { + push(@roles,$ccrole); + } + } else { + @roles = ($ccrole); + } + foreach my $role (@roles) { + my $refresh=$env{'user.refresh.time'}; + if ($refresh eq '') { + $refresh = $env{'user.login.time'}; + } + if ($refresh eq '') { + $refresh = $now; + } + my $start = $refresh-1; + my $end = '0'; + if ($role eq 'st') { + if ($details->{'accessstart'} ne '') { + $start = $details->{'accessstart'}; + } + if ($details->{'accessend'} ne '') { + $end = $details->{'accessend'}; + } + } + my @usecs; + if ($role ne $ccrole) { + if (ref($details->{'personnel'}{$owner}{$role}{'usec'}) eq 'ARRAY') { + @usecs = @{$details->{'personnel'}{$owner}{$role}{'usec'}}; + } + } + if ($role eq 'st') { + if (@usecs > 1) { + my $firstsec = $usecs[0]; + @usecs = ($firstsec); + } + } + if (@usecs == 0) { + push(@usecs,''); + } + foreach my $usec (@usecs) { + my (%userroles,%newrole,%newgroups,$spec,$area); + my $area = '/'.$dom.'/'.$cnum; + my $spec = $role.'.'.$area; + if ($usec ne '') { + $spec .= '/'.$usec; + $area .= '/'.$usec; + } + if ($role =~ /^cr\//) { + &Apache::lonnet::custom_roleprivs(\%newrole,$role,$dom, + $cnum,$spec,$area); + } else { + &Apache::lonnet::standard_roleprivs(\%newrole,$role,$dom, + $spec,$cnum,$area); + } + &Apache::lonnet::set_userprivs(\%userroles,\%newrole, + \%newgroups); + $userroles{'user.role.'.$spec} = $start.'.'.$end; + &Apache::lonnet::appenv(\%userroles,[$role,'cm']); + if (($end == 0) || ($end > $now)) { + my $showrole = $role; if ($role =~ /^cr\//) { - &Apache::lonnet::custom_roleprivs(\%newrole,$role,$dom, - $cnum,$spec,$area); - } else { - &Apache::lonnet::standard_roleprivs(\%newrole,$role,$dom, - $spec,$cnum,$area); + $showrole = &Apache::lonnet::plaintext($role,$crstype); + } elsif (ref($longroles) eq 'HASH') { + if ($longroles->{$role} ne '') { + $showrole = $longroles->{$role}; + } } - &Apache::lonnet::set_userprivs(\%userroles,\%newrole, - \%newgroups); - $userroles{'user.role.'.$spec} = $start.'.'.$end; - &Apache::lonnet::appenv(\%userroles,[$role,'cm']); - if (($end == 0) || ($end > $now)) { - my $showrole = $role; - if ($role =~ /^cr\//) { - $showrole = &Apache::lonnet::plaintext($role,$crstype); - } elsif (ref($longroles) eq 'HASH') { - if ($longroles->{$role} ne '') { - $showrole = $longroles->{$role}; - } + if ($start <= $now) { + $active .= '
'; } if ($future) { - $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'})). - '
'; } + my %possemails; + my $fullname = &Apache::loncommon::plainname($env{'user.name'}, + $env{'user.domain'}); + my $emailto = &Apache::loncommon::build_recipient_list(undef,'requestsmail',$dom); + if ($emailto) { + map { $possemails{$_} = 1; } (split(/,/,$emailto)); + } if ($req_notifylist) { - my $fullname = &Apache::loncommon::plainname($env{'user.name'}, - $env{'user.domain'}); + if ($emailto) { + foreach my $recip (split(/,/,$req_notifylist)) { + my ($uname,$udom) = split(/:/,$recip); + my %emails = &Apache::loncommon::getemails($uname,$udom); + foreach my $type ('permanentemail','notification') { + if ((exists($emails{$type})) && ($emails{$type} ne '')) { + my @to = split(/,/,$emails{$type}); + foreach my $addr (@to) { + if (($addr ne '') && ($addr =~ m/\@/)) { + if (exists($possemails{$addr})) { + delete($possemails{$addr}); + } + } + } + } + } + } + } 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); + &Apache::loncoursequeueadmin::send_selfserve_notification($req_notifylist,"$fullname ($env{'user.name'}:$env{'user.domain'})", + undef,$env{'form.cdescr'},$now,'coursereq',$sender,'','',$crstype); + } +# +# If domain configuration for "E-mail addresses and helpform" has values set +# for "E-mail from course requests requiring approval", send email to those +# addresse(es) when a course request is queued, pending approval, unless +# the email address will already receive a notification email, because of +# values set for "Receive notification of course requests requiring approval" +# in "Request creation of courses" configuration item. +# + if ($emailto && keys(%possemails)) { + ¬ify_admin($dom,$crstype,$env{'form.cdescr'},"$fullname ($env{'user.name'}:$env{'user.domain'})",$now,\%possemails); + } + if (ref($postprocess) eq 'HASH') { + if (ref($postprocess->{'queuedmsg'}) eq 'ARRAY') { + if (scalar(@{$postprocess->{'queuedmsg'}}) > 0) { + my $recipient = $env{'user.name'}.':'.$env{'user.domain'}; + my $sender = $recipient; + my $addmsg = []; + foreach my $item (@{$postprocess->{'queuedmsg'}}) { + if (ref($item) eq 'HASH') { + if ($item->{'mt'} ne '') { + push(@{$addmsg},$item); + } + } + } + if (scalar(@{$addmsg}) > 0) { + &Apache::loncoursequeueadmin::send_selfserve_notification($recipient,$addmsg,undef, + $env{'form.cdescr'},$now, + 'queuedcrsreq',$sender); + } + } + } + if ($postprocess->{'queuedweb'}) { + $output .= $postprocess->{'queuedweb'}; + } } } elsif ($disposition eq 'pending') { - $output .= ' '.
+ my $pending_default = ' ';
+ if (ref($postprocess) eq 'HASH') {
+ if ($postprocess->{'pendingweb'}) {
+ $output .= $postprocess->{'pendingweb'};
+ } else {
+ $output .= $pending_default;
+ }
+ } else {
+ $output .= $pending_default;
+ }
+ } elsif ($disposition eq 'created') {
+ if (($code) || ((ref($postprocess) eq 'HASH') &&
+ ((ref($postprocess->{'createdmsg'}) eq 'ARRAY') || ($postprocess->{'createdweb'})))) {
+ my $addmsg = [];
+ my $recipient = $env{'user.name'}.':'.$env{'user.domain'};
+ my $sender = $recipient;
+ if ($code) {
+ push(@{$addmsg},{
+ mt => 'Students can automatically select your course: "[_1]" by entering this code: [_2]',
+ args => [$env{'form.cdescr'},$code],
+ });
+ $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.'). - ' '.
+ &mt('Students can automatically select your course by entering this code: [_1].',''.$code.'').
+ ' '.
- &mt('Your request status is: [_1].',$disposition).
- ' '
+ &mt('Your request status is: [_1].',$disposition).
+ '';
}
return $output;
}
-sub get_processtype {
- my ($dom,$crstype,$inststatuses,$domconfig) = @_;
- return unless ((ref($inststatuses) eq 'ARRAY') && (ref($domconfig) eq 'HASH'));
- my (%userenv,%settings,$val);
- my @options = ('autolimit','validate','approval');
- if ($dom eq $env{'user.domain'}) {
- %userenv =
- &Apache::lonnet::userenvironment($env{'user.domain'},$env{'user.name'},
- 'requestcourses.'.$crstype,'inststatus');
- if ($userenv{'requestcourses.'.$crstype}) {
- $val = $userenv{'requestcourses.'.$crstype};
- @{$inststatuses} = ('_custom_');
- } else {
- my ($task,%alltasks);
- if (ref($domconfig->{'requestcourses'}) eq 'HASH') {
- %settings = %{$domconfig->{'requestcourses'}};
- if (ref($settings{$crstype}) eq 'HASH') {
- if (($env{'user.adv'}) && ($settings{$crstype}{'_LC_adv'} ne '')) {
- $val = $settings{$crstype}{'_LC_adv'};
- @{$inststatuses} = ('_LC_adv_');
- } else {
- if ($userenv{'inststatus'} ne '') {
- @{$inststatuses} = split(',',$userenv{'inststatus'});
- } else {
- @{$inststatuses} = ('default');
- }
- foreach my $status (@{$inststatuses}) {
- if (exists($settings{$crstype}{$status})) {
- my $value = $settings{$crstype}{$status};
- next unless ($value);
- unless (exists($alltasks{$value})) {
- if (ref($alltasks{$value}) eq 'ARRAY') {
- unless(grep(/^\Q$status\E$/,@{$alltasks{$value}})) {
- push(@{$alltasks{$value}},$status);
- }
- } else {
- @{$alltasks{$value}} = ($status);
- }
- }
- }
- }
- my $maxlimit = 0;
-
- foreach my $key (sort(keys(%alltasks))) {
- if ($key =~ /^autolimit=(\d*)$/) {
- if ($1 eq '') {
- $val ='autolimit=';
- last;
- } elsif ($1 > $maxlimit) {
- $maxlimit = $1;
- }
- }
- }
- if ($maxlimit) {
- $val = 'autolimit='.$maxlimit;
- } else {
- foreach my $option (@options) {
- if ($alltasks{$option}) {
- $val = $option;
- last;
- }
- }
- }
- }
+sub notify_admin {
+ my ($dom,$crstype,$contextdesc,$textstr,$timestamp,$emailsref) = @_;
+ if ((ref($emailsref) eq 'HASH') && (keys(%{$emailsref}))) {
+ my $emailto = join(',',sort(keys(%{$emailsref})));
+ my (@rawmsg,$rawsubj,$msgtxt);
+ if ($crstype eq 'community') {
+ $rawsubj = 'Community request to review';
+ $msgtxt = 'Creation of the following community: [_1]was requested by [_2] on [_3].';
+ } else {
+ $rawsubj = 'Course request to review';
+ $msgtxt = 'Creation of the following course: [_1]was requested by [_2] on [_3].';
+ }
+ $timestamp =&Apache::lonlocal::locallocaltime($timestamp);
+ push(@rawmsg,{
+ mt => $msgtxt,
+ args => ["\n $contextdesc\n",$textstr,$timestamp],
+ },
+ {
+ mt =>'[_1]A Domain Coordinator will use: [_2]Main Menu -> Course and community creation -> Approve or reject requests[_3]to display a list of pending requests, which can either be approved or rejected.',
+ args => ["\n","\n\n","\n\n"],
+ });
+
+ my $sender_lh = &Apache::loncommon::user_lang($env{'user.name'},$env{'user.domain'});
+ my $subject = &mt_user($sender_lh,$rawsubj);
+ my $message = '';
+ foreach my $item (@rawmsg) {
+ if (ref($item) eq 'HASH') {
+ if (ref($item->{args}) eq 'ARRAY') {
+ $message .= &mt_user($sender_lh,$item->{mt},@{$item->{args}})."\n";
+ } else {
+ $message .= &mt_user($sender_lh,$item->{mt})."\n";
}
}
}
- } else {
- %userenv = &Apache::lonnet::userenvironment($env{'user.domain'},
- $env{'user.name'},'reqcrsotherdom.'.$env{'form.crstype'});
- if ($userenv{'reqcrsotherdom.'.$crstype}) {
- my @doms = split(',',$userenv{'reqcrsotherdom.'.$crstype});
- my $optregex = join('|',@options);
- foreach my $item (@doms) {
- my ($extdom,$extopt) = split(':',$item);
- if ($extdom eq $dom) {
- if ($extopt =~ /^($optregex)(=?\d*)$/) {
- $val = $1.$2;
+ my $chgmail = "To: $emailto\n".
+ "Subject: $subject\n".
+ "Content-type: text/plain\; charset=UTF-8\n".
+ "MIME-Version: 1.0\n\n".
+ "$message\n\n";
+ if (open(my $mailh, "|/usr/lib/sendmail -oi -t -odb")) {
+ print $mailh $chgmail;
+ close($mailh);
+ }
+ }
+}
+
+sub pending_validation_form {
+ my ($r,$cdom,$cnum,$crstype,$now,$token,$lonhost,$cdesc) = @_;
+ my $output;
+ my %postvalues = (
+ 'owner' => $env{'user.name'}.':'.$env{'user.domain'},
+ 'course' => $cdom.'_'.$cnum,
+ 'coursetype' => $crstype,
+ );
+ my %domconfig = &Apache::lonnet::get_dom('configuration',['requestcourses'],$cdom);
+
+ if (ref($domconfig{'requestcourses'}) eq 'HASH') {
+ my ($url,$buttontext,$code,@fields);
+ if (ref($domconfig{'requestcourses'}{'validation'}) eq 'HASH') {
+ $postvalues{'description'} = $cdesc;
+ $url = $domconfig{'requestcourses'}{'validation'}{'url'};
+ if (ref($domconfig{'requestcourses'}{'validation'}{'fields'}) eq 'ARRAY') {
+ @fields = @{$domconfig{'requestcourses'}{'validation'}{'fields'}};
+ }
+ $buttontext = $domconfig{'requestcourses'}{'validation'}{'button'};
+ $output .= $domconfig{'requestcourses'}{'validation'}{'markup'};
+ if (($url =~ m{^(https?\://|/)}) && (@fields > 0)) {
+ $output .= ''.&mt("Your $typename->{$crstype} limit is [_1].",$limit); + 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'; } @@ -3139,18 +4584,32 @@ sub retrieve_settings { } $env{'form.clonecrs'} = $reqinfo{'clonecrs'}; $env{'form.clonedom'} = $reqinfo{'clonedom'}; + if (($reqinfo{'clonecrs'} ne '') && ($reqinfo{'clonedom'} ne '')) { + $env{'form.cloning'} = 1; + } $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); + $env{'form.tinyurls'} = $reqinfo{'tinyurls'}; + if ($reqinfo{'crstype'} eq 'official') { + $env{'form.autoadds'} = $reqinfo{'autoadds'}; + $env{'form.autodrops'} = $reqinfo{'autodrops'}; + if ($reqinfo{'instcode'} ne '') { + $env{'form.sectotal'} = $reqinfo{'sectotal'}; + $env{'form.crosslisttotal'} = $reqinfo{'crosslisttotal'}; + $env{'form.instcode'} = $reqinfo{'instcode'}; + my $crscode = { + $cnum => $reqinfo{'instcode'}, + }; + &extract_instcode($dom,'instcode',$crscode,$cnum); + (undef,undef,my $instcredits) = + &Apache::lonnet::auto_validate_instcode(undef,$dom, + $reqinfo{'instcode'}); + if ($instcredits ne $reqinfo{'defaultcredits'}) { + $env{'form.coursecredits'} = $reqinfo{'defaultcredits'}; + } + } + } elsif (($reqinfo{'crstype'} eq 'unofficial') || ($reqinfo{'crstype'} eq 'textbook')) { + $env{'form.coursecredits'} = $reqinfo{'defaultcredits'}; } my @currsec; if (ref($reqinfo{'sections'}) eq 'HASH') { @@ -3281,5 +4740,821 @@ sub generate_date_items { return; } +sub print_textbook_form { + my ($r,$dom,$incdoms,$domdefs,$settings,$can_request,$crstype,$formhash) = @_; + my (%prefab,%ordered,%numprefab); + if ($crstype eq '') { + $crstype = 'textbook'; + } +# +# Retrieve list of prefabricated courses (textbook courses and templates) cloneable by user +# + foreach my $type ('textbooks','templates') { + $numprefab{$type} = 0; + if (ref($settings) eq 'HASH') { + $prefab{$type} = $settings->{$type}; + if (ref($prefab{$type}) eq 'HASH') { + foreach my $item (keys(%{$prefab{$type}})) { + my ($clonedom,$clonecrs) = split(/_/,$item); + if (ref($prefab{$type}{$item}) eq 'HASH') { + if (&Apache::loncoursequeueadmin::can_clone_course($env{'user.name'}, + $env{'user.domain'},$clonecrs,$clonedom,$crstype,$dom)) { + + my $num = $prefab{$type}{$item}{'order'}; + $ordered{$type}{$num} = $item; + $numprefab{$type} ++; + } + } + } + } + } + } + +# +# Check if domain has multiple library servers +# + my ($home_server_pick,$numlib) = + &Apache::loncommon::home_server_form_item($dom,'chome', + 'default','hide'); + if ($numlib > 1) { + $home_server_pick = &mt('Home Server for Course').': '.$home_server_pick.' '; + } + +# +# Retrieve information about courses owned by user, or in which user has an active +# Course Coordinator role +# + my $numcurrent; + my %cloneable = &Apache::lonnet::courseiddump($dom,'.',1,'.',$env{'user.name'}.':'.$env{'user.domain'}, + '.',undef,undef,'Course'); + my %ccroles = &Apache::lonnet::get_my_roles($env{'user.name'},$env{'user.domain'},'userroles', + ['active'],['cc']); + + my $cc_clone = ''; + foreach my $role (keys(%ccroles)) { + my ($cnum,$cdom,$rest) = split(/:/,$role,3); + $cc_clone .= $cdom.':'.$cnum.'&'; + unless (exists($cloneable{$cdom.'_'.$cnum})) { + my %courseinfo = &Apache::lonnet::coursedescription($cdom.'_'.$cnum,{'one_time' => 1}); + $cloneable{$cdom.'_'.$cnum} = { + context => $courseinfo{'internal.creationcontext'}, + created => $courseinfo{'internal.created'}, + creator => $courseinfo{'internal.creator'}, + description => $courseinfo{'description'}, + inst_code => $courseinfo{'coursecode'}, + owner => $courseinfo{'internal.courseowner'}, + releaserequired => $courseinfo{'internal.releaserequired'}, + type => $courseinfo{'type'}, + }; + } + } + + my $numcurrent = scalar(keys(%cloneable)); + +# +# Retrieve information about courses from user's domain which user can clone, but which not owned +# or cloneable based on Course Coordinator role. +# + my ($numdomcourses,%domcloneable); + my %allcloneable = &Apache::lonnet::courseiddump($dom,'.',1,'.','.','.',undef,undef,'Course', + undef,undef,undef,undef,undef, + $env{'user.name'}.':'.$env{'user.domain'}, + $cc_clone,1); + foreach my $cid (keys(%allcloneable)) { + unless (exists($cloneable{$cid})) { + $domcloneable{$cid} = $allcloneable{$cid}; + } + } + $numdomcourses = scalar(keys(%domcloneable)); + + my $fullname = &Apache::loncommon::plainname($env{'user.name'}, + $env{'user.domain'}); + +# +# Retrieve any custom form information prior to rendering page +# + + my $initprocess = &Apache::lonnet::auto_crsreq_update($dom,undef,$crstype,'initializereview',$env{'user.name'}, + $env{'user.domain'},$fullname); + my %custominit; + if (ref($initprocess) eq 'HASH') { + &custom_formitems($initprocess,\%custominit); + } + +# +# Retrieve any custom onload actions or javascript used for page before rendering +# + + my ($customonload,$customjs,$customvalidationjs); + my $inprocess = &Apache::lonnet::auto_crsreq_update($dom,undef,$crstype,'prereview',$env{'user.name'}, + $env{'user.domain'},$fullname,undef,undef, + undef,undef,\%custominit); + if (ref($inprocess) eq 'HASH') { + $customonload = $inprocess->{'onload'}; + $customjs = $inprocess->{'javascript'}; + $customvalidationjs = $inprocess->{'validationjs'}; + } + + my $postprocess = &Apache::lonnet::auto_crsreq_update($dom,undef,$crstype,'review', + $env{'user.name'}, + $env{'user.domain'},$fullname,undef,undef, + undef,undef,\%custominit); + + my $jscript = &textbook_request_javascript(\%numprefab,$numcurrent,$numdomcourses,$customvalidationjs); + $jscript .= $customjs; + my (%loaditems,$args); + $loaditems{'onload'} = 'javascript:uncheckAllRadio();'.$customonload; + if ($crstype eq 'lti') { + $args = { 'only_body' => 1}; + } + $r->print(&header('Course Request',$jscript,\%loaditems,undef,$args)); + + if (ref($can_request) eq 'HASH') { + unless (((scalar(keys(%{$can_request})) == 1) && ($can_request->{'textbook'})) || + ($crstype eq 'lti')) { + &Apache::lonhtmlcommon::add_breadcrumb( + { href => '/adm/requestcourse', + text => 'Pick action', + }); + } + } + unless ($crstype eq 'lti') { + &Apache::lonhtmlcommon::add_breadcrumb({text=>'Course Request'}); + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Course Requests','Course_Requests')); + + &startContentScreen($r,'textbookrequests'); +# +# Show domain selector form, if required. +# + if (@{$incdoms} > 1) { + my $onchange = 'this.form.submit()'; + $r->print(' ');
+ }
+ }
+
+#
+# Course request form
+#
+
+#
+# Course Title
+#
+ $r->print(' '.
+ ''.
+ ' ');
+
+#
+# Content source selection, if more than one available
+#
+ if (keys(%cloneable) || keys(%ordered) || keys(%domcloneable)) {
+ $r->print(''.
+ ' ');
+ }
+
+ my %accesstitles = (
+ 'start' => 'Default start access',
+ 'end' => 'Default end access',
+ );
+ my %help_item = (
+ start => 'Course_Request_Access_Start',
+ end => 'Course_Request_Access_End',
+ );
+ my $starttime = time;
+ my $endtime = time+(6*30*24*60*60); # 6 months from now, approx
+ my $startform = &Apache::lonhtmlcommon::date_setter('requestcourse','accessstart',
+ $starttime,'','','',1,'','','',1);
+ my $endform = &Apache::lonhtmlcommon::date_setter('requestcourse','accessend',
+ $endtime,'','','',1,'','','',1);
+#
+# Set default start and end dates for student access
+#
+ $r->print(''.
+ ' ');
+
+#
+# Display any custom fields for this course type
+#
+ if (ref($postprocess) eq 'HASH') {
+ if ($postprocess->{'reviewweb'}) {
+ $r->print($postprocess->{'reviewweb'});
+ }
+ }
+
+#
+# Submit button
+#
+ $r->print(''.
+ ''.
+ '');
+
+#
+# End request form
+#
+
+ if (($crstype eq 'lti') && (ref($formhash) eq 'HASH')) {
+ foreach my $item (keys(%{$formhash})) {
+ $r->print(''."\n");
+ }
+ }
+
+ $r->print(''. + &Apache::loncommon::help_open_topic($help_item{'end'}). + ' '.&mt($accesstitles{'end'}).$endform.' '.
+ ' | '.$namestr.' | '.
+ &Apache::loncommon::end_data_table_row();
+ }
+ }
+ }
+ if ($output) {
+ return &Apache::loncommon::start_data_table().
+ &Apache::loncommon::start_data_table_header_row().
+ ''.&mt('Title').' | '.
+ ''.&mt('Owner/co-owner(s)').' | '.
+ &Apache::loncommon::end_data_table_header_row().
+ $output.
+ &Apache::loncommon::end_data_table();
+ }
+ return;
+}
+
+sub process_textbook_request {
+ my ($r,$dom,$action,$domdefs,$domconfig,$can_request,$crstype) = @_;
+ my ($uniquecode,$req_notifylist);
+ if ($crstype eq '') {
+ $crstype = 'textbook';
+ }
+ if (ref($domconfig) eq 'HASH') {
+ if (ref($domconfig->{'requestcourses'}) eq 'HASH') {
+ if (ref($domconfig->{'requestcourses'}{'notify'}) eq 'HASH') {
+ $req_notifylist = $domconfig->{'requestcourses'}{'notify'}{'approval'};
+ }
+ if (ref($domconfig->{'requestcourses'}{'uniquecode'}) eq 'HASH') {
+ $uniquecode = $domconfig->{'requestcourses'}{'uniquecode'}{$crstype};
+ }
+ }
+ }
+ my $now = time;
+ my $reqtype = $env{'form.cloning'};
+ my (@inststatuses,$storeresult,$creationresult);
+ my $cnum = &Apache::lonnet::generate_coursenum($dom,'Course');
+ my ($clonefrom,$clonedom,$clonecrs);
+ if ($reqtype eq 'textbook') {
+ $clonefrom = $env{'form.book'};
+ } elsif ($reqtype eq 'template') {
+ $clonefrom = $env{'form.template'};
+ } elsif ($reqtype eq 'existing') {
+ $clonefrom = $env{'form.owned'};
+ } elsif ($reqtype eq 'colleague') {
+ $clonefrom = $env{'form.colleague'};
+ }
+ my ($accessstart,$accessend) = &dates_from_form('accessstart','accessend');
+ if ($clonefrom) {
+ ($clonedom,$clonecrs) = split(/_/,$clonefrom);
+ if (&Apache::lonnet::homeserver($clonecrs,$clonedom) ne 'no_host') {
+ my $canclone =
+ &Apache::loncoursequeueadmin::can_clone_course($env{'user.name'},
+ $env{'user.domain'},$clonecrs,$clonedom,$crstype,$dom);
+ unless ($canclone) {
+ undef($clonecrs);
+ undef($clonedom);
+ }
+ } else {
+ undef($clonecrs);
+ undef($clonedom);
+ }
+ }
+ my $args;
+ if ($crstype eq 'lti') {
+ $args = { 'only_body' => 1};
+ }
+ $r->print(&header('Course Creation','','',undef,$args));
+
+ unless ($crstype eq 'lti') {
+ if (ref($can_request) eq 'HASH') {
+ unless ((scalar(keys(%{$can_request})) == 1) && ($can_request->{'textbook'})) {
+ &Apache::lonhtmlcommon::add_breadcrumb(
+ { href => '/adm/requestcourse',
+ text => 'Pick action',
+ });
+ }
+ }
+ &Apache::lonhtmlcommon::add_breadcrumb(
+ { href => '/adm/requestcourse',
+ text => "Create Course",
+ }
+ );
+ &Apache::lonhtmlcommon::add_breadcrumb({text=>'Request Processed'});
+ $r->print(&Apache::lonhtmlcommon::breadcrumbs('Course Requests','Course_Requests'));
+ &startContentScreen($r,'textbookrequests');
+ }
+
+ my $details = {
+ owner => $env{'user.name'},
+ domain => $env{'user.domain'},
+ cdom => $dom,
+ cnum => $cnum,
+ coursehome => $env{'form.chome'},
+ cdescr => $env{'form.cdescr'},
+ crstype => $crstype,
+ uniquecode => $uniquecode,
+ clonedom => $clonedom,
+ clonecrs => $clonecrs,
+ accessstart => $accessstart,
+ accessend => $accessend,
+ personnel => {},
+ };
+ if (($clonecrs ne '') && ($clonedom ne '')) {
+ if ($reqtype eq 'existing') {
+ $details->{datemode} = $env{'form.owndatemode'};
+ if ($details->{datemode} eq 'shift') {
+ $details->{dateshift} = $env{'form.owndateshift'};
+ } else {
+ $details->{dateshift} = '';
+ }
+ $details->{tinyurls} = $env{'form.owntinyurls'};
+ } elsif ($reqtype eq 'colleague') {
+ $details->{datemode} = $env{'form.colldatemode'};
+ if ($details->{datemode} eq 'shift') {
+ $details->{dateshift} = $env{'form.colldateshift'};
+ } else {
+ $details->{dateshift} = '';
+ }
+ $details->{tinyurls} = $env{'form.colltinyurls'};
+ } elsif (($reqtype eq 'textbook') || ($reqtype eq 'template')) {
+ $details->{datemode} = 'delete';
+ $details->{dateshift} = '';
+ $details->{tinyurls} = '';
+ }
+ if ($details->{dateshift} ne '') {
+ $details->{dateshift} =~ s/[^\d\.]+//g;
+ }
+ } else {
+ $details->{datemode} = '';
+ $details->{dateshift} = '';
+ $details->{tinyurls} = '';
+ }
+ my $lonhost = $r->dir_config('lonHostID');
+ $r->rflush();
+ my ($result,$output,$customized) = &process_request($r,$lonhost,$dom,$cnum,$crstype,$now,$details,
+ '',$req_notifylist,[],$domconfig);
+ $r->print($output);
+ if ($crstype eq 'lti') {
+ my $storecrs;
+ if ($env{'request.lti.login'}) {
+ my %lti = &Apache::lonnet::get_domain_lti($dom,'provider');
+ if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') {
+ $storecrs = $lti{$env{'request.lti.login'}}{'storecrs'};
+ }
+ if ($storecrs) {
+ my %consumers = &Apache::lonnet::get_dom('lticonsumers',[$env{'form.sourcecrs'}],$dom);
+ if (($env{'form.lti.sourcecrs'} ne '') && ($consumers{$env{'form.lti.sourcecrs'}} eq '') && ($cnum ne '')) {
+ &Apache::lonnet::put_dom('lticonsumers',{ $env{'form.lti.sourcecrs'} => $env{'request.lti.login'}.':'.$cnum },$dom);
+ }
+ }
+ }
+ } elsif (&Apache::loncoursequeueadmin::author_prompt()) {
+ unless ($customized) {
+ &print_author_prompt($r,$action,$cnum,$dom,$crstype,$result);
+ }
+ } elsif ($result eq 'created') {
+ unless ($customized) {
+ $r->print(''.&mt('Create another course').' '); + } + } + unless ($crstype eq 'lti') { + &endContentScreen($r); + } + $r->print(&Apache::loncommon::end_page()); +} + +sub textbook_request_javascript { + my ($numprefab,$numcurrent,$numcolleague,$customvalidationjs) = @_; + return unless (ref($numprefab) eq 'HASH'); + return if (!$numprefab->{'textbooks'} && !$numprefab->{'templates'} && !$numcurrent && !$numcolleague); + my %js_lt = &Apache::lonlocal::texthash( + choose => 'Please select a content option.', + textbook => 'Please select a textbook, or choose a different option.', + template => 'Please select a template, or choose a different option.', + existing => 'Please select one of your existing courses to copy, or choose a different option.', + colleague => "Please select a colleague's course to copy, or choose a different option.", + title => 'Please enter a course title.', + ); + &js_escape(\%js_lt); + return <<"ENDSCRIPT"; +function cloneChoice() { + if (document.requestcourse.cloning) { + var radioLength = document.requestcourse.cloning.length; + if (radioLength == undefined) { + var val = document.requestcourse.cloning.value; + if ((val == 'textbook') || (val == 'template') || (val == 'existing') || (val == 'colleague')) { + var elem = document.getElementById('show'+val); + if (document.requestcourse.cloning.checked) { + elem.style.display = 'block'; + } else { + uncheckRadio(val); + elem.style.display = 'none'; + } + } + } else { + for (var i=0; i'.
+ ' '.
+ &Apache::loncommon::end_page());
+ return;
+}
+
+sub startContentScreen {
+ my ($r,$mode)=@_;
+ $r->print("\n".''.&mt('You do not have privileges to request creation of textbook courses.').' '); + if (ref($can_request) eq 'HASH') { + if (scalar(keys(%{$can_request})) > 1) { + $r->print(''.&mt('Go back').''); + } + } + $r->print('
');
+}
+
+sub endContentScreen {
+ my ($r)=@_;
+ $r->print(' |