--- loncom/interface/lonmodifycourse.pm 2019/04/29 22:19:24 1.94 +++ loncom/interface/lonmodifycourse.pm 2022/02/06 21:36:59 1.97 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # handler for DC-only modifiable course settings # -# $Id: lonmodifycourse.pm,v 1.94 2019/04/29 22:19:24 raeburn Exp $ +# $Id: lonmodifycourse.pm,v 1.97 2022/02/06 21:36:59 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -39,6 +39,9 @@ use Apache::lonpickcourse; use lib '/home/httpd/lib/perl'; use LONCAPA qw(:DEFAULT :match); +my $registered_cleanup; +my $modified_dom; + sub get_dc_settable { my ($type,$cdom) = @_; if ($type eq 'Community') { @@ -427,6 +430,7 @@ sub print_modification_menu { 'setanon' => 'View/Modify responders threshold for anonymous survey submissions display', 'selfenroll' => 'View/Modify Self-Enrollment configuration', 'setpostsubmit' => 'View/Modify submit button behavior, post-submission', + 'setltiauth' => 'View/Modify re-authentication requirement for LTI launch of deep-linked item', ); } else { %linktext = ( @@ -434,6 +438,7 @@ sub print_modification_menu { 'setanon' => 'View responders threshold for anonymous survey submissions display', 'selfenroll' => 'View Self-Enrollment configuration', 'setpostsubmit' => 'View submit button behavior, post-submission', + 'setltiauth' => 'View re-authentication requirement for LTI launch of deep-linked item', ); } if ($type eq 'Community') { @@ -472,6 +477,7 @@ sub print_modification_menu { my $anon_text = &mt('Responder threshold required to display anonymous survey submissions.'); my $postsubmit_text = &mt('Override defaults for submit button behavior post-submission for this specific course.'); my $mysqltables_text = &mt('Override default for lifetime of "temporary" MySQL tables containing student performance data.'); + my $ltiauth_text = &mt('Override default for requirement for re-authentication for LTI-limited launch of deep-linked item'); $linktext{'viewparms'} = 'Display current settings for automated enrollment'; my %domconf = &Apache::lonnet::get_dom('configuration',['coursecategories'],$dom); @@ -553,6 +559,14 @@ sub print_modification_menu { permission => $permission->{'setpostsubmit'}, linktitle => '', }, + { + linktext => $linktext{'setltiauth'}, + icon => 'system-lock-screen.png', + #help => '', + url => &phaseurl('setltiauth'), + permission => $permission->{'setltiauth'}, + linktitle => '', + }, ] }, ); @@ -583,7 +597,8 @@ sub print_modification_menu { '<li>'.$setquota_text.'</li>'."\n". '<li>'.$setuploadquota_text.'</li>'."\n". '<li>'.$anon_text.'</li>'."\n". - '<li>'.$postsubmit_text.'</li>'."\n"; + '<li>'.$postsubmit_text.'</li>'."\n". + '<li>'.$ltiauth_text.'</li>'."\n"; my ($categories_link_start,$categories_link_end); if ($permission->{'catsettings'} eq 'edit') { $categories_link_start = '<a href="/adm/domainprefs?actions=coursecategories&phase=display">'; @@ -681,9 +696,34 @@ sub print_settings_display { "<th>$lt{'dcon'}</th>\n". &Apache::loncommon::end_data_table_header_row()."\n"; foreach my $item (@items) { + my $shown = $enrollvar{$item}; + if ($item eq 'crosslistings') { + my (@xlists,@lcsecs); + foreach my $entry (split(/,/,$enrollvar{$item})) { + my ($xlist,$lc_sec) = split(/:/,$entry); + push(@xlists,$xlist); + push(@lcsecs,$lc_sec); + } + if (@xlists) { + my $crskey = $cnum.':'.$enrollvar{'coursecode'}; + my %reformatted = + &Apache::lonnet::auto_instsec_reformat($cdom,'declutter', + {$crskey => \@xlists}); + if (ref($reformatted{$crskey}) eq 'ARRAY') { + my @show; + my @xlcodes = @{$reformatted{$crskey}}; + for (my $i=0; $i<@xlcodes; $i++) { + push(@show,$xlcodes[$i].':'.$lcsecs[$i]); + } + if (@show) { + $shown = join(',',@show); + } + } + } + } $disp_table .= &Apache::loncommon::start_data_table_row()."\n". "<td><b>$longtype{$item}</b></td>\n". - "<td>$enrollvar{$item}</td>\n"; + "<td>$shown</td>\n"; if (grep(/^\Q$item\E$/,@modifiable_params)) { $disp_table .= '<td align="right">'.&mt('Yes').'</td>'."\n"; } else { @@ -1233,6 +1273,79 @@ sub print_selfenrollconfig { return; } +sub print_set_ltiauth { + my ($r,$cdom,$cnum,$cdesc,$type,$readonly) = @_; + my %lt = &Apache::lonlocal::texthash( + 'requ' => 'Requirement for re-authentication for student LTI-limited launch of deep-linked item', + 'link' => 'Link protection can be set to accept username for an enrolled student (if sent by Consumer)', + 'logi' => 'Login needed, regardless of user information sent by LTI Consumer in (signed) parameters', + 'used' => 'Use domain default', + 'cour' => 'Use course-specific setting', + 'curd' => 'Current domain default is', + 'valu' => 'Value for this course', + 'modi' => 'Save', + 'back' => 'Pick another action', + ); + my ($domdef,$checkeddom,$checkedcrs,$domdefdisplay,$divsty,$authok,$authno); + $domdef = 0; + $checkeddom = 'checked="checked "'; + $domdefdisplay = $lt{'logi'}; + $divsty = 'display:none'; + $authno = 'checked="checked" '; + my %domconfig = + &Apache::lonnet::get_dom('configuration',['coursedefaults'],$cdom); + if (ref($domconfig{'coursedefaults'}) eq 'HASH') { + $domdef = $domconfig{'coursedefaults'}{'ltiauth'}; + } + if ($domdef) { + $domdefdisplay = $lt{'link'}; + } + my %settings = &Apache::lonnet::get('environment',['internal.ltiauth'],$cdom,$cnum); + my $ltiauth = $settings{'internal.ltiauth'}; + + if ($ltiauth ne '') { + $checkedcrs = $checkeddom; + $checkeddom = ''; + $divsty = 'display:inline-block'; + if ($ltiauth) { + $authok = 'checked="checked "'; + } + } + &print_header($r,$type); + my $hidden_elements = &hidden_form_elements(); + my ($disabled,$submit); + if ($readonly) { + $disabled = ' disabled="disabled"'; + } else { + $submit = '<input type="button" onclick="javascript:changePage(this.form,'."'processltiauth'".');" value="'.$lt{'modi'}.'" />'; + } + my $helpitem = &Apache::loncommon::help_open_topic('Modify_Course_LTI_Authen'); + $r->print(<<ENDDOCUMENT); +<form action="/adm/modifycourse" method="post" name="setltiauth"> +<h3>$helpitem $lt{'requ'} <span class="LC_nobreak">$cdesc</span></h3> +<p><span class="LC_nobreak">$lt{'curd'}: <span style="font-style:italic">'.$domdefdisplay</span></span</p> +<p><span class="LC_nobreak"> +<label><input type="radio" name="ltiauthset" value="dom" onclick="toggleLTIOptions(this.form);" $checkeddom$disabled/>$lt{'used'}</label></span><br /> +<span class="LC_nobreak"> +<label><input type="radio" name="ltiauthset" value="course" onclick="toggleLTIOptions(this.form);" $checkedcrs$disabled/>$lt{'cour'}</label></span> +<fieldset id="crsltiauth" style="$divsty"> +<legend>$lt{'valu'}</legend> +<span class="LC_nobreak"> +<label><input type="radio" name="ltiauth" value="0" $authno$disabled/>$lt{'logi'}</label> +</span><br /> +<span class="LC_nobreak"> +<label><input type="radio" name="ltiauth" value="1" $authok$disabled/>$lt{'link'}</label> +</span> +</fieldset> +$submit +</p> +$hidden_elements +<a href="javascript:changePage(document.setltiauth,'menu')">$lt{'back'}</a> +</form> +ENDDOCUMENT + return; +} + sub modify_selfenrollconfig { my ($r,$type,$cdesc,$coursehash) = @_; return unless(ref($coursehash) eq 'HASH'); @@ -1484,6 +1597,18 @@ sub modify_course { my $putres = &Apache::lonnet::courseidput($cdom,\%crsinfo,$chome,'notime'); if (($putres eq 'ok') && (($changed{'owner'} || $changed{'code'}))) { &update_coowners($cdom,$cnum,$chome,\%settings,\%newattr); + if ($changed{'code'}) { + &Apache::lonnet::devalidate_cache_new('instcats',$cdom); + # Update cache of self-cataloging courses on institution's server(s). + if (&Apache::lonnet::shared_institution($cdom)) { + unless ($registered_cleanup) { + my $handlers = $r->get_handlers('PerlCleanupHandler'); + $r->set_handlers('PerlCleanupHandler' => [\&devalidate_remote_instcats,@{$handlers}]); + $registered_cleanup=1; + $modified_dom = $cdom; + } + } + } } } } @@ -2218,6 +2343,109 @@ sub modify_catsettings { return; } +sub modify_ltiauth { + my ($r,$cdom,$cnum,$cdesc,$domdesc,$type) = @_; + my %lt = &Apache::lonlocal::texthash( + 'requ' => 'Requirement for re-authentication for student LTI-limited launch of deep-linked item', + 'link' => 'Link protection can be set to accept username for an enrolled student (if sent by Consumer)', + 'logi' => 'Login needed, regardless of user information sent by LTI Consumer in (signed) parameters', + 'used' => 'Use domain default', + 'cour' => 'Use course-specific setting', + 'modi' => 'Save', + 'back' => 'Pick another action', + ); + &print_header($r,$type); + $r->print('<form action="/adm/modifycourse" method="post" name="processltiauth">'."\n". + '<h3>'.$lt{'requ'}. + ' <span class="LC_nobreak">'.$cdesc.'</span></h3>'); + my %oldsettings = &Apache::lonnet::get('environment',['internal.ltiauth'],$cdom,$cnum); + my $oldltiauth = $oldsettings{'internal.ltiauth'}; + my $domdef; + my %domconfig = + &Apache::lonnet::get_dom('configuration',['coursedefaults'],$cdom); + if (ref($domconfig{'coursedefaults'}) eq 'HASH') { + $domdef = $domconfig{'coursedefaults'}{'ltiauth'}; + } + my ($newltiauth,$nochange,$change,$status,$error,$ltiauth); + if ($env{'form.ltiauthset'} eq 'dom') { + if ($oldltiauth eq '') { + $nochange = 1; + } else { + $change = 1; + } + } elsif ($env{'form.ltiauthset'} eq 'course') { + if ($env{'form.ltiauth'} =~ /^0|1$/) { + $newltiauth = $env{'form.ltiauth'}; + } + if ($oldltiauth == $newltiauth) { + $nochange = 1; + } else { + $change = 1; + } + } + if ($change) { + if ($newltiauth ne '') { + my %cenv = ( + 'internal.ltiauth' => $newltiauth, + ); + if (&Apache::lonnet::put('environment',\%cenv,$cdom,$cnum) eq 'ok') { + if ($env{'course.'.$cdom.'_'.$cnum.'.description'} ne '') { + &Apache::lonnet::appenv( + {'course.'.$cdom.'_'.$cnum.'.internal.ltiauth' => $newltiauth}); + } + } else { + $error = 1; + } + } else { + if (&Apache::lonnet::del('environment',['internal.ltiauth'],$cdom,$cnum) eq 'ok') { + if (exists($env{'course.'.$cdom.'_'.$cnum.'.internal.ltiauth'})) { + &Apache::lonnet::delenv('course.'.$cdom.'_'.$cnum.'.internal.ltiauth'); + } + } else { + $error = 1; + } + } + } + if ($error) { + $nochange = 1; + } + if ($nochange) { + $ltiauth = $oldltiauth; + } else { + $ltiauth = $newltiauth; + } + if ($ltiauth eq '') { + $status = $lt{'used'}.': '; + if ($domdef) { + $status .= '<span style="font-style:italic">'.$lt{'link'}.'</span>'; + } else { + $status .= '<span style="font-style:italic">'.$lt{'logi'}.'</span>'; + } + } else { + $status = $lt{'cour'}.': '; + if ($ltiauth) { + $status .= '<span style="font-style:italic">'.$lt{'link'}.'</span>'; + } else { + $status .= '<span style="font-style:italic">'.$lt{'logi'}.'</span>'; + } + } + if ($error) { + $r->print('<p class="LC_warning">'.&mt('An error occurred when saving your changes').'</p>'); + } + $r->print('<p>'); + if ($nochange) { + $r->print(&mt('Re-authentication requirement for LTI launch of deep-linked item is unchanged')); + } elsif ($change) { + $r->print(&mt('Re-authentication requirement for LTI launch of deep-linked changed')); + } + $r->print('<br />'.$status); + $r->print('</p><p>'. + '<a href="javascript:changePage(document.processltiauth,'."'menu'".')">'. + &mt('Pick another action').'</a></p>'); + $r->print(&hidden_form_elements().'</form>'); + return; +} + sub print_header { my ($r,$type,$javascript_validations) = @_; my $phase = "start"; @@ -2373,6 +2601,35 @@ function togglePostsubmit(caller) { ENDSCRIPT + } elsif ($phase eq 'setltiauth') { + $js .= <<"ENDJS"; +function toggleLTIOptions(form) { + var radioname = 'ltiauthset'; + var divid = 'crsltiauth'; + var num = form.elements[radioname].length; + if (num) { + var setvis = ''; + for (var i=0; i<num; i++) { + if (form.elements[radioname][i].checked) { + if (form.elements[radioname][i].value == 'course') { + if (document.getElementById(divid)) { + document.getElementById(divid).style.display = 'inline-block'; + } + setvis = 1; + } + break; + } + } + if (!setvis) { + if (document.getElementById(divid)) { + document.getElementById(divid).style.display = 'none'; + } + } + } + return; +} + +ENDJS } my $starthash; if ($env{'form.phase'} eq 'adhocrole') { @@ -2383,6 +2640,10 @@ ENDSCRIPT $starthash = { add_entries => {'onload' => "hide_searching(); courseSet(document.filterpicker.official, 'load');"}, }; + } elsif ($env{'form.phase'} eq 'setltiauth') { + $starthash = { + add_entries => {'onload' => "toggleLTIOptions(document.setltiauth);"}, + }; } $r->print(&Apache::loncommon::start_page('View/Modify Course/Community Settings', &Apache::lonhtmlcommon::scripttag($js), @@ -2482,7 +2743,7 @@ sub hidden_form_elements { 'locarg','krbarg','krbver','counter','hidefromcat','usecategory', 'threshold','postsubmit','postsubtimeout','defaultcredits','uploadquota', 'selfenrollmgrdc','selfenrollmgrcc','action','state','currsec_st', - 'sections','newsec','mysqltables','nopasswdchg'], + 'sections','newsec','mysqltables','nopasswdchg','ltiauth','ltiauthset'], ['^selfenrollmgr_','^selfenroll_'])."\n". '<input type="hidden" name="prevphase" value="'.$env{'form.phase'}.'" />'; return $hidden_elements; @@ -2516,6 +2777,8 @@ sub get_permission { processcat => 'edit', selfenroll => 'edit', adhocrole => 'coord', + setltiauth => 'edit', + processltiauth => 'edit', ); if ($passwdconf{'crsownerchg'}) { $permission{passwdchg} = 'edit'; @@ -2531,6 +2794,7 @@ sub get_permission { catsettings => 'view', selfenroll => 'view', adhocrole => 'custom', + setltiauth => 'view', ); if ($passwdconf{'crsownerchg'}) { $permission{passwdchg} = 'view'; @@ -2539,6 +2803,22 @@ sub get_permission { return ($allowed,\%permission); } +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 handler { my $r = shift; if ($r->header_only) { @@ -2547,6 +2827,9 @@ sub handler { return OK; } + $registered_cleanup=0; + $modified_dom = ''; + my $dom = $env{'request.role.domain'}; my $domdesc = &Apache::lonnet::domain($dom,'description'); my ($allowed,$permission) = &get_permission($dom); @@ -2715,6 +2998,19 @@ sub handler { text=>"Result"}); &modify_selfenrollconfig($r,$type,$cdesc,$coursehash); } + } elsif (($phase eq 'setltiauth') && ($permission->{'setltiauth'})) { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>"javascript:changePage(document.$phase,'$phase')", + text=>"Requirement for re-authentication for LTI launch of deep-linked item"}); + &print_set_ltiauth($r,$cdom,$cnum,$cdesc,$type,$readonly); + } elsif (($phase eq 'processltiauth') && ($permission->{'processltiauth'})) { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>"javascript:changePage(document.$phase,'setltiauth')", + text=>"Requirement for re-authentication for LTI launch of deep-linked item"}); + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>"javascript:changePage(document.$phase,'$phase')", + text=>"Result"}); + &modify_ltiauth($r,$cdom,$cnum,$cdesc,$domdesc,$type); } } } else {