--- loncom/interface/lonmodifycourse.pm 2018/03/23 01:01:21 1.93 +++ 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.93 2018/03/23 01:01:21 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') { @@ -49,6 +52,10 @@ sub get_dc_settable { if (&showcredits($cdom)) { push(@items,'defaultcredits'); } + my %passwdconf = &Apache::lonnet::get_passwdconf($cdom); + if (($passwdconf{'crsownerchg'}) && ($type ne 'Placement')) { + push(@items,'nopasswdchg'); + } return @items; } } @@ -101,6 +108,7 @@ sub get_enrollment_settings { @items = map { 'internal.'.$_; } (@{$internals}); push(@items,@{$accessdates}); } + push(@items,'internal.nopasswdchg'); my %settings = &Apache::lonnet::get('environment',\@items,$cdom,$cnum); my %enrollvar; $enrollvar{'autharg'} = ''; @@ -126,7 +134,7 @@ sub get_enrollment_settings { } elsif ($type eq "authtype" || $type eq "autharg" || $type eq "coursecode" || $type eq "crosslistings" || $type eq "selfenrollmgr" - || $type eq "autodropfailsafe") { + || $type eq "autodropfailsafe" || $type eq 'nopasswdchg') { $enrollvar{$type} = $settings{$item}; } elsif ($type eq 'defaultcredits') { if (&showcredits($cdom)) { @@ -422,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 = ( @@ -429,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') { @@ -467,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); @@ -548,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 => '', + }, ] }, ); @@ -578,7 +597,8 @@ sub print_modification_menu { '
  • '.$setquota_text.'
  • '."\n". '
  • '.$setuploadquota_text.'
  • '."\n". '
  • '.$anon_text.'
  • '."\n". - '
  • '.$postsubmit_text.'
  • '."\n"; + '
  • '.$postsubmit_text.'
  • '."\n". + '
  • '.$ltiauth_text.'
  • '."\n"; my ($categories_link_start,$categories_link_end); if ($permission->{'catsettings'} eq 'edit') { $categories_link_start = ''; @@ -676,9 +696,34 @@ sub print_settings_display { "$lt{'dcon'}\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". "$longtype{$item}\n". - "$enrollvar{$item}\n"; + "$shown\n"; if (grep(/^\Q$item\E$/,@modifiable_params)) { $disp_table .= ''.&mt('Yes').''."\n"; } else { @@ -1033,6 +1078,7 @@ sub print_course_modification_page { my @specific_managebydc = split(/,/,$settings{'internal.selfenrollmgrdc'}); my @specific_managebycc = split(/,/,$settings{'internal.selfenrollmgrcc'}); my %domdefaults = &Apache::lonnet::get_domain_defaults($cdom); + my %passwdconf = &Apache::lonnet::get_passwdconf($cdom); my @default_managebydc = split(/,/,$domdefaults{$type.'selfenrolladmdc'}); if ($crstype eq 'Community') { $ccrole = 'co'; @@ -1107,7 +1153,8 @@ sub print_course_modification_page { if ($crstype eq 'Community') { $r->print(&Apache::lonhtmlcommon::row_title( &Apache::loncommon::help_open_topic('Modify_Community_Owner'). - ' '.&mt('Community Owner'))."\n"); + ' '.&mt('Community Owner'))."\n". + $ownertable."\n".&Apache::lonhtmlcommon::row_closure()); } else { $r->print(&Apache::lonhtmlcommon::row_title( &Apache::loncommon::help_open_topic('Modify_Course_Instcode'). @@ -1117,7 +1164,7 @@ sub print_course_modification_page { if (($crstype eq 'Course') && (&showcredits($cdom))) { $r->print(&Apache::lonhtmlcommon::row_title( &Apache::loncommon::help_open_topic('Modify_Course_Credithours'). - ' '.&mt('Credits (students)'))."\n". + ' '.&mt('Credits (students)'))."\n". ''. &Apache::lonhtmlcommon::row_closure()); } @@ -1127,8 +1174,21 @@ sub print_course_modification_page { $authenitems."\n". &Apache::lonhtmlcommon::row_closure(). &Apache::lonhtmlcommon::row_title( - &Apache::loncommon::help_open_topic('Modify_Course_Owner'). - ' '.&mt('Course Owner'))."\n"); + &Apache::loncommon::help_open_topic('Modify_Course_Owner'). + ' '.&mt('Course Owner'))."\n". + $ownertable."\n".&Apache::lonhtmlcommon::row_closure()); + if (($passwdconf{'crsownerchg'}) && ($type ne 'Placement')) { + my $checked; + if ($enrollvar{'nopasswdchg'}) { + $checked = ' checked="checked"'; + } + $r->print(&Apache::lonhtmlcommon::row_title( + &Apache::loncommon::help_open_topic('Modify_Course_Chgpasswd'). + ' '.&mt('Changing passwords (internal)'))."\n". + '$lt{'back'} + +ENDDOCUMENT + return; +} + sub modify_selfenrollconfig { my ($r,$type,$cdesc,$coursehash) = @_; return unless(ref($coursehash) eq 'HASH'); @@ -1321,6 +1453,10 @@ sub modify_course { if (&showcredits($cdom)) { push(@items,'internal.defaultcredits'); } + my %passwdconf = &Apache::lonnet::get_passwdconf($cdom); + if ($passwdconf{'crsownerchg'}) { + push(@items,'internal.nopasswdchg'); + } } my %settings = &Apache::lonnet::get('environment',\@items,$cdom,$cnum); my $description = $settings{'description'}; @@ -1336,6 +1472,7 @@ sub modify_course { } else { %changed = ( code => 0, owner => 0, + passwd => 0, ); $ccrole = 'cc'; unless ($settings{'internal.sectionnums'} eq '') { @@ -1390,9 +1527,21 @@ sub modify_course { $newattr{'mysqltables'} = $env{'form.mysqltables'}; $newattr{'mysqltables'} =~ s/\D+//g; } - if (($type ne 'Placement') && (&showcredits($cdom) && exists($env{'form.defaultcredits'}))) { - $newattr{'defaultcredits'}=$env{'form.defaultcredits'}; - $newattr{'defaultcredits'} =~ s/[^\d\.]//g; + if ($type ne 'Placement') { + if (&showcredits($cdom) && exists($env{'form.defaultcredits'})) { + $newattr{'defaultcredits'}=$env{'form.defaultcredits'}; + $newattr{'defaultcredits'} =~ s/[^\d\.]//g; + } + if (grep(/^nopasswdchg$/,@modifiable_params)) { + if ($env{'form.nopasswdchg'}) { + $newattr{'nopasswdchg'} = 1; + unless ($currattr{'nopasswdchg'}) { + $changed{'passwd'} = 1; + } + } elsif ($currattr{'nopasswdchg'}) { + $changed{'passwd'} = 1; + } + } } } @@ -1427,7 +1576,7 @@ sub modify_course { } } - if ($changed{'owner'} || $changed{'code'}) { + if ($changed{'owner'} || $changed{'code'} || $changed{'passwd'}) { my %crsinfo = &Apache::lonnet::courseiddump($cdom,'.',1,'.','.',$cnum, undef,undef,'.'); if (ref($crsinfo{$env{'form.pickedcourse'}}) eq 'HASH') { @@ -1437,10 +1586,29 @@ sub modify_course { if ($changed{'owner'}) { $crsinfo{$env{'form.pickedcourse'}}{'owner'} = $env{'form.courseowner'}; } + if ($changed{'passwd'}) { + if ($env{'form.nopasswdchg'}) { + $crsinfo{$env{'form.pickedcourse'}}{'nopasswdchg'} = 1; + } else { + delete($crsinfo{'nopasswdchg'}); + } + } my $chome = &Apache::lonnet::homeserver($cnum,$cdom); my $putres = &Apache::lonnet::courseidput($cdom,\%crsinfo,$chome,'notime'); - if ($putres eq 'ok') { + 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; + } + } + } } } } @@ -1486,6 +1654,12 @@ sub modify_course { $shown = &mt('None'); } elsif (($attr eq 'mysqltables') && ($shown eq '')) { $shown = &mt('domain default'); + } elsif ($attr eq 'nopasswdchg') { + if ($shown) { + $shown = &mt('Yes'); + } else { + $shown = &mt('No'); + } } $chgresponse .= '
  • '.&mt('[_1] now set to: [_2]',$longtype{$attr},$shown).'
  • '; } else { @@ -1498,6 +1672,12 @@ sub modify_course { $shown = &mt('None'); } elsif (($attr eq 'mysqltables') && ($shown eq '')) { $shown = &mt('domain default'); + } elsif ($attr eq 'nopasswdchg') { + if ($shown) { + $shown = &mt('Yes'); + } else { + $shown = &mt('No'); + } } $nochgresponse .= '
  • '.&mt('[_1] still set to: [_2]',$longtype{$attr},$shown).'
  • '; } @@ -2163,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('
    '."\n". + '

    '.$lt{'requ'}. + ' '.$cdesc.'

    '); + 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 .= ''.$lt{'link'}.''; + } else { + $status .= ''.$lt{'logi'}.''; + } + } else { + $status = $lt{'cour'}.': '; + if ($ltiauth) { + $status .= ''.$lt{'link'}.''; + } else { + $status .= ''.$lt{'logi'}.''; + } + } + if ($error) { + $r->print('

    '.&mt('An error occurred when saving your changes').'

    '); + } + $r->print('

    '); + 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('
    '.$status); + $r->print('

    '. + ''. + &mt('Pick another action').'

    '); + $r->print(&hidden_form_elements().'
    '); + return; +} + sub print_header { my ($r,$type,$javascript_validations) = @_; my $phase = "start"; @@ -2318,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 {'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), @@ -2414,6 +2730,7 @@ sub course_settings_descrip { 'selfenrollmgrdc' => "Course-specific self-enrollment configuration by Domain Coordinator", 'selfenrollmgrcc' => "Course-specific self-enrollment configuration by Course personnel", 'mysqltables' => '"Temporary" student performance tables lifetime (seconds)', + 'nopasswdchg' => 'Disable changing password for users with student role by course owner', ); } return %longtype; @@ -2426,7 +2743,8 @@ sub hidden_form_elements { 'locarg','krbarg','krbver','counter','hidefromcat','usecategory', 'threshold','postsubmit','postsubtimeout','defaultcredits','uploadquota', 'selfenrollmgrdc','selfenrollmgrcc','action','state','currsec_st', - 'sections','newsec','mysqltables'],['^selfenrollmgr_','^selfenroll_'])."\n". + 'sections','newsec','mysqltables','nopasswdchg','ltiauth','ltiauthset'], + ['^selfenrollmgr_','^selfenroll_'])."\n". ''; return $hidden_elements; } @@ -2442,6 +2760,7 @@ sub showcredits { sub get_permission { my ($dom) = @_; my ($allowed,%permission); + my %passwdconf = &Apache::lonnet::get_passwdconf($dom); if (&Apache::lonnet::allowed('ccc',$dom)) { $allowed = 1; %permission = ( @@ -2458,7 +2777,12 @@ sub get_permission { processcat => 'edit', selfenroll => 'edit', adhocrole => 'coord', + setltiauth => 'edit', + processltiauth => 'edit', ); + if ($passwdconf{'crsownerchg'}) { + $permission{passwdchg} = 'edit'; + } } elsif (&Apache::lonnet::allowed('rar',$dom)) { $allowed = 1; %permission = ( @@ -2470,11 +2794,31 @@ sub get_permission { catsettings => 'view', selfenroll => 'view', adhocrole => 'custom', + setltiauth => 'view', ); + if ($passwdconf{'crsownerchg'}) { + $permission{passwdchg} = 'view'; + } } 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) { @@ -2483,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); @@ -2651,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 {