--- loncom/interface/courseprefs.pm 2017/02/07 15:44:23 1.84 +++ loncom/interface/courseprefs.pm 2022/02/01 23:13:19 1.99 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set configuration settings for a course # -# $Id: courseprefs.pm,v 1.84 2017/02/07 15:44:23 raeburn Exp $ +# $Id: courseprefs.pm,v 1.99 2022/02/01 23:13:19 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -365,9 +365,29 @@ sub handler { } my %values=&Apache::lonnet::dump('environment',$cdom,$cnum); + my %lti=&Apache::lonnet::dump('lti',$cdom,$cnum,undef,undef,undef,1); + my %ltienc = &Apache::lonnet::dump('nohist_ltienc',$cdom,$cnum,undef,undef,undef,1); + foreach my $id (keys(%lti)) { + if (ref($lti{$id}) eq 'HASH') { + if (ref($ltienc{$id}) eq 'HASH') { + $values{'linkprotection'}{$id} = { %{$lti{$id}}, %{$ltienc{$id}} }; + } else { + $values{'linkprotection'}{$id} = $lti{$id}; + } + } + unless ($phase eq 'process') { + if (ref($values{'linkprotection'}{$id}) eq 'HASH') { + delete($values{'linkprotection'}{$id}{'secret'}); + } + } + } + if ($lti{'lock'}) { + delete($lti{'lock'}); + } my @prefs_order = ('courseinfo','localization','feedback','discussion', 'classlists','appearance','grading','printouts', - 'spreadsheet','bridgetasks','other'); + 'menuitems','linkprotection','spreadsheet','bridgetasks', + 'lti','other'); my %prefs = ( 'courseinfo' => @@ -375,7 +395,7 @@ sub handler { help => 'Course_Prefs_General', ordered => ['owner','co-owners','loncaparev','description', 'clonedfrom','courseid','uniquecode','categories', - 'hidefromcat','externalsyllabus','cloners','url', + 'hidefromcat','syllabus','cloners','url', 'rolenames'], itemtext => { 'owner' => $lt{'ownr'}, @@ -386,7 +406,7 @@ sub handler { 'categories' => $lt{'catg'}, 'hidefromcat' => $lt{'excc'}, 'cloners' => $lt{'clon'}, - 'externalsyllabus' => 'Syllabus status', + 'syllabus' => 'Syllabus status', 'url' => 'Top Level Map', 'rolenames' => $lt{'rept'}, 'loncaparev' => $lt{'lcrv'}, @@ -467,7 +487,7 @@ sub handler { help => 'Course_Prefs_Display', ordered => ['default_xml_style','pageseparators', 'disable_receipt_display','texengine', - 'tthoptions','uselcmath','usejsme'], + 'tthoptions','uselcmath','usejsme','inline_chem'], itemtext => { default_xml_style => 'Default XML style file', pageseparators => 'Visibly Separate Items on Pages', @@ -476,6 +496,7 @@ sub handler { tthoptions => 'Default set of options to pass to tth/m when converting TeX', uselcmath => 'Student formula entry uses inline preview, not DragMath pop-up', usejsme => 'Molecule editor uses JSME (HTML5) in place of JME (Java)', + inline_chem => 'Chemical reaction response uses inline preview, not pop-up', }, }, 'grading' => @@ -530,6 +551,41 @@ sub handler { suppress_embed_prompt => 'Hide upload references prompt if uploading file to portfolio', }, }, + 'lti' => + { + text => 'LTI provider settings', + help => 'Course_Prefs_LTIProvider', + ordered => ['lti.override','lti.topmenu','lti.inlinemenu','lti.lcmenu'], + itemtext => { + 'lti.override' => 'Override domain defaults', + 'lti.topmenu' => 'Display LON-CAPA page header', + 'lti.inlinemenu' => 'Display LON-CAPA inline menu', + 'lti.lcmenu' => 'Menu items', + }, + }, + 'menuitems' => + { + text => 'Menu display', + help => 'Course_Prefs_Menus', + header => [{col1 => 'Default Menu', + col2 => 'Value',}, + {col1 => 'Menu collections', + col2 => 'Settings', + }], + ordered => ['menudefault','menucollections'], + itemtext => { + menudefault => 'Choose default collection of menu items for course', + menucollections => 'Menu collections', + }, + }, + 'linkprotection' => + { + text => 'Link protection', + help => 'Course_Prefs_Linkprotection', + header => [{col1 => 'Item', + col2 => 'Settings', + }], + }, 'other' => { text => 'Other settings', help => 'Course_Prefs_Other', @@ -545,10 +601,16 @@ sub handler { $cnum,undef,\@allitems, 'coursepref',$parm_permission); } elsif (($phase eq 'display') && ($parm_permission->{'display'})) { - my $jscript = &get_jscript($cid,$cdom,$phase,$crstype,\%values); + my $noedit; + if (ref($parm_permission) eq 'HASH') { + unless ($parm_permission->{'process'}) { + $noedit = 1; + } + } + my $jscript = &get_jscript($cid,$cdom,$phase,$crstype,\%values,$noedit); my @allitems = &get_allitems(%prefs); &Apache::lonconfigsettings::display_settings($r,$cdom,$phase,$context, - \@prefs_order,\%prefs,\%values,undef,$jscript,\@allitems,$crstype, + \@prefs_order,\%prefs,\%values,$cnum,$jscript,\@allitems,$crstype, 'coursepref',$parm_permission); } else { &Apache::lonconfigsettings::display_choices($r,$phase,$context, @@ -601,7 +663,7 @@ sub get_allitems { } sub print_config_box { - my ($r,$cdom,$phase,$action,$item,$settings,$allitems,$crstype,$parm_permission) = @_; + my ($r,$cdom,$cnum,$phase,$action,$item,$settings,$allitems,$crstype,$parm_permission) = @_; my $ordered = $item->{'ordered'}; my $itemtext = $item->{'itemtext'}; my $noedit; @@ -622,7 +684,7 @@ sub print_config_box { } $output .= ''."\n". ''; - if (($action eq 'feedback') || ($action eq 'classlists')) { + if (($action eq 'feedback') || ($action eq 'classlists') || ($action eq 'menuitems')) { $output .= ' @@ -647,6 +709,8 @@ sub print_config_box { $output .= &print_feedback('top',$cdom,$settings,$ordered,$itemtext,\$rowtotal,$noedit); } elsif ($action eq 'classlists') { $output .= &print_classlists('top',$cdom,$settings,$itemtext,\$rowtotal,$crstype,$noedit); + } elsif ($action eq 'menuitems') { + $output .= &print_menuitems('top',$cdom,$settings,$itemtext,\$rowtotal,$crstype,$noedit); } $output .= ' @@ -725,6 +789,12 @@ sub print_config_box { $output .= &print_spreadsheet($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit); } elsif ($action eq 'bridgetasks') { $output .= &print_bridgetasks($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit); + } elsif ($action eq 'lti') { + $output .= &print_lti($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit); + } elsif ($action eq 'menuitems') { + $output .= &print_menuitems('bottom',$cdom,$settings,$itemtext,\$rowtotal,$crstype,$noedit); + } elsif ($action eq 'linkprotection') { + $output .= &print_linkprotection($cdom,$cnum,$settings,\$rowtotal,$crstype,$noedit); } elsif ($action eq 'other') { $output .= &print_other($cdom,$settings,$allitems,\$rowtotal,$crstype,$noedit); } @@ -737,8 +807,8 @@ sub print_config_box { } sub process_changes { - my ($cdom,$action,$values,$item,$changes,$allitems,$disallowed,$crstype) = @_; - my %newvalues; + my ($cdom,$cnum,$action,$values,$item,$changes,$allitems,$disallowed,$crstype) = @_; + my (%newvalues,%lti,%ltienc,$errors); if (ref($item) eq 'HASH') { if (ref($changes) eq 'HASH') { my @ordered; @@ -755,6 +825,21 @@ sub process_changes { } } } + } elsif ($action eq 'linkprotection') { + if (ref($values->{$action}) eq 'HASH') { + foreach my $id (keys(%{$values->{$action}})) { + if ($id =~ /^\d+$/) { + push(@ordered,$id); + unless (ref($values->{$action}->{$id}) eq 'HASH') { + $lti{$id} = ''; + } + } + } + } + @ordered = sort { $a <=> $b } @ordered; + if (($env{'form.linkprot_add'}) && ($env{'form.linkprot_maxnum'} =~ /^\d+$/)) { + push(@ordered,$env{'form.linkprot_maxnum'}); + } } elsif (ref($item->{'ordered'}) eq 'ARRAY') { if ($action eq 'courseinfo') { my ($can_toggle_cat,$can_categorize) = @@ -766,7 +851,8 @@ sub process_changes { (!$can_categorize)); next if (($entry eq 'loncaparev') || ($entry eq 'owner') || - ($entry eq 'clonedfrom')); + ($entry eq 'clonedfrom') || + ($entry eq 'syllabus')); push(@ordered,$entry); } } elsif ($action eq 'classlists') { @@ -816,6 +902,181 @@ sub process_changes { $changes->{$ext_entry} = $newvalues{$ext_entry}; } } + } elsif ($action eq 'menuitems') { + my (%current,@colls); + my $next = 1; + if ($values->{'menucollections'}) { + foreach my $item (split(/;/,$values->{'menucollections'})) { + my ($num,$value) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + unless (grep(/^$num$/,@colls)) { + push(@colls,$num); + } + my @entries = split(/\&/,$value); + foreach my $entry (@entries) { + my ($name,$fields) = split(/=/,$entry); + $current{$num}{$name} = $fields; + } + } + } + } + if (@colls) { + @colls = sort { $a <=> $b } @colls; + $next += $colls[-1]; + } + if ($env{'form.menucollections_add'} eq $next) { + push(@colls,$next); + } + my $currdef = $values->{'menudefault'}; + my $possdef = $env{'form.menudefault'}; + if (($possdef =~ /^\d+$/) && (grep(/^$possdef$/,@colls))) { + if ($currdef ne $possdef) { + $changes->{'menudefault'} = $possdef; + } + } elsif ($currdef) { + $changes->{'menudefault'} = ''; + } + my $menucoll; + if (@colls) { + my ($ordered,$cats) = &menuitems_categories(); + my %shortcats = &menuitems_abbreviations(); + foreach my $num (@colls) { + my ($entry,%include); + map { $include{$_}= 1; } &Apache::loncommon::get_env_multiple('form.menucollections_'.$num); + foreach my $item (@{$ordered}) { + if ($item eq 'shown') { + foreach my $type (@{$cats->{$item}}) { + $entry .= $type.'='; + if ($include{$type}) { + $entry .= 'y'; + } else { + $entry .= 'n'; + } + $entry .= '&'; + } + } else { + $entry .= $shortcats{$item}.'='; + foreach my $type (@{$cats->{$item}}) { + if ($include{$type}) { + $entry .= $type.','; + } + } + $entry =~ s/,$//; + $entry .= '&'; + } + } + $entry =~ s/\&$//; + if ($menucoll) { + $menucoll .= ';'; + } + $menucoll .= $num.'%'.$entry; + } + if ($menucoll ne $values->{'menucollections'}) { + $changes->{'menucollections'} = $menucoll; + } + } elsif ($values->{'menucollections'}) { + $changes->{'menucollections'} = ''; + } + } elsif ($action eq 'linkprotection') { + my %menutitles = <imenu_titles(); + my $switchserver = &check_switchserver($cdom,$cnum); + my (@items,%deletions,%itemids,%haschanges); + if ($env{'form.linkprot_add'}) { + my $name = $env{'form.linkprot_name_add'}; + $name =~ s/(`)/'/g; + my ($newid,$error) = &get_courselti_id($cdom,$cnum,$name); + if ($newid) { + $itemids{'add'} = $newid; + push(@items,'add'); + $haschanges{$newid} = 1; + } else { + $errors .= ''. + &mt('Failed to acquire unique ID for link protection'). + ''; + } + } + if (ref($values->{$action}) eq 'HASH') { + my @todelete = &Apache::loncommon::get_env_multiple('form.linkprot_del'); + my $maxnum = $env{'form.linkprot_maxnum'}; + for (my $i=0; $i<=$maxnum; $i++) { + my $itemid = $env{'form.linkprot_id_'.$i}; + $itemid =~ s/\D+//g; + if ($itemid) { + if (ref($values->{$action}->{$itemid}) eq 'HASH') { + push(@items,$i); + $itemids{$i} = $itemid; + if ((@todelete > 0) && (grep(/^$i$/,@todelete))) { + $deletions{$itemid} = $values->{$action}->{$itemid}->{'name'}; + } + } + } + } + } + + foreach my $idx (@items) { + my $itemid = $itemids{$idx}; + next unless ($itemid); + if (exists($deletions{$itemid})) { + $lti{$itemid} = $deletions{$itemid}; + $haschanges{$itemid} = 1; + next; + } + my %current; + if (ref($values->{$action}) eq 'HASH') { + if (ref($values->{$action}->{$itemid}) eq 'HASH') { + foreach my $key (keys(%{$values->{$action}->{$itemid}})) { + $current{$key} = $values->{$action}->{$itemid}->{$key}; + } + } + } + foreach my $inner ('name','lifetime','version') { + my $formitem = 'form.linkprot_'.$inner.'_'.$idx; + $env{$formitem} =~ s/(`)/'/g; + if ($inner eq 'lifetime') { + $env{$formitem} =~ s/[^\d.]//g; + } + unless ($idx eq 'add') { + if ($current{$inner} ne $env{$formitem}) { + $haschanges{$itemid} = 1; + } + } + if ($env{$formitem} ne '') { + $lti{$itemid}{$inner} = $env{$formitem}; + } + } + unless ($switchserver) { + my $keyitem = 'form.linkprot_key_'.$idx; + $env{$keyitem} =~ s/(`)/'/g; + unless ($idx eq 'add') { + if ($current{'key'} ne $env{$keyitem}) { + $haschanges{$itemid} = 1; + } + } + if ($env{$keyitem} ne '') { + $lti{$itemid}{'key'} = $env{$keyitem}; + } + my $secretitem = 'form.linkprot_secret_'.$idx; + $env{$secretitem} =~ s/(`)/'/g; + if ($current{'usable'}) { + if ($env{'form.linkprot_changesecret_'.$idx}) { + if ($env{$secretitem} ne '') { + $lti{$itemid}{'secret'} = $env{$secretitem}; + $haschanges{$itemid} = 1; + } + } else { + $lti{$itemid}{'secret'} = $current{'secret'}; + } + } elsif ($env{$secretitem} ne '') { + $lti{$itemid}{'secret'} = $env{$secretitem}; + $haschanges{$itemid} = 1; + } + } + } + if (keys(%haschanges)) { + foreach my $entry (keys(%haschanges)) { + $changes->{$entry} = $lti{$entry}; + } + } } else { foreach my $entry (@ordered) { if ($entry eq 'cloners') { @@ -856,7 +1117,7 @@ sub process_changes { my $clonedom = $env{'form.cloners_newdom'}; if (&check_clone($clonedom,$disallowed) eq 'ok') { my $newdom = '*:'.$env{'form.cloners_newdom'}; - if (@clonedoms) { + if (@clonedoms) { if (!grep(/^\Q$newdom\E$/,@clonedoms)) { $newvalues{$entry} .= ','.$newdom; } @@ -945,7 +1206,9 @@ sub process_changes { $autocoowner = $domconf{'autoenroll'}{'co-owners'}; } } - unless ($autocoowner) { + if ($autocoowner) { + $newvalues{'co-owners'} = $values->{'internal.co-owners'}; + } else { my @keepcoowners = &Apache::loncommon::get_env_multiple('form.coowners'); my @pendingcoowners = &Apache::loncommon::get_env_multiple('form.pendingcoowners'); my @invitecoowners = &Apache::loncommon::get_env_multiple('form.invitecoowners'); @@ -969,19 +1232,19 @@ sub process_changes { my $udom = $env{'user.domain'}; my $pendingcoowners = $values->{'internal.pendingco-owners'}; my @pendingcoown = split(',',$pendingcoowners); - if ($env{'form.pending_coowoner'}) { + if ($env{'form.pending_coowner'}) { foreach my $item (@pendingcoown) { unless ($item eq $uname.':'.$udom) { push(@newpending,$item); } } @newcoown = @currcoown; - if ($env{'form.pending_coowoner'} eq 'accept') { + if ($env{'form.pending_coowner'} eq 'accept') { unless (grep(/^\Q$uname\E:\Q$udom\E$/,@currcoown)) { push(@newcoown,$uname.':'.$udom); } } - } elsif ($env{'form.remove_coowoner'}) { + } elsif ($env{'form.remove_coowner'}) { foreach my $item (@currcoown) { unless ($item eq $uname.':'.$udom) { push(@newcoown,$item); @@ -990,6 +1253,8 @@ sub process_changes { if ($pendingcoowners ne '') { @newpending = @pendingcoown; } + } else { + @newcoown = @currcoown; } $newvalues{'pendingco-owners'} = join(',',sort(@newpending)); $newvalues{'co-owners'} = join(',',sort(@newcoown)); @@ -1257,6 +1522,38 @@ sub process_changes { $settings =~ s/,$//; } $newvalues{$entry} = $settings; + } elsif ($action eq 'lti') { + if ($entry eq 'lti.override') { + $newvalues{$entry} = $env{'form.'.$entry}; + } elsif (($entry eq 'lti.topmenu') || ($entry eq 'lti.inlinemenu')) { + if ($env{'form.lti.override'}) { + $newvalues{$entry} = $env{'form.'.$entry}; + } else { + $newvalues{$entry} = ''; + } + } elsif ($entry eq 'lti.lcmenu') { + if (($env{'form.lti.override'}) && + (($env{'form.lti.topmenu'}) || ($env{'form.lti.inlinemenu'}))) { + my @lcmenu = &Apache::loncommon::get_env_multiple('form.lti.lcmenu'); + my @newlcmenu; + if (@lcmenu) { + my @menuitems = ('fullname','coursetitle','role','logout','grades'); + foreach my $item (@menuitems) { + next if (($item eq 'grades') && (!$newvalues{'lti.inlinemenu'})); + if (grep(/^\Q$item\E$/,@lcmenu)) { + push(@newlcmenu,$item); + } + } + } + if (@newlcmenu) { + $newvalues{$entry} = join(',',@newlcmenu); + } else { + $newvalues{$entry} = 'none'; + } + } else { + $newvalues{$entry} = ''; + } + } } else { $newvalues{$entry} = $env{'form.'.$entry}; } @@ -1270,7 +1567,51 @@ sub process_changes { } } } - return; + return $errors; +} + +sub get_courselti_id { + my ($cdom,$cnum,$name) = @_; + # get lock on lti db in course + my $lockhash = { + lock => $env{'user.name'}. + ':'.$env{'user.domain'}, + }; + my $tries = 0; + my $gotlock = &Apache::lonnet::newput('lti',$lockhash,$cdom,$cnum); + my ($id,$error); + while (($gotlock ne 'ok') && ($tries<10)) { + $tries ++; + sleep (0.1); + $gotlock = &Apache::lonnet::newput('lti',$lockhash,$cdom,$cnum); + } + if ($gotlock eq 'ok') { + my %currids = &Apache::lonnet::dump('lti',$cdom,$cnum,undef,undef,undef,1); + if ($currids{'lock'}) { + delete($currids{'lock'}); + if (keys(%currids)) { + my @curr = sort { $a <=> $b } keys(%currids); + if ($curr[-1] =~ /^\d+$/) { + $id = 1 + $curr[-1]; + } else { + $id = 1; + } + } else { + $id = 1; + } + if ($id) { + unless (&Apache::lonnet::newput('lti',{ $id => $name },$cdom,$cnum) eq 'ok') { + $error = 'nostore'; + } + } else { + $error = 'nonumber'; + } + } + my $dellockoutcome = &Apache::lonnet::del('lti',['lock'],$cdom,$cnum); + } else { + $error = 'nolock'; + } + return ($id,$error); } sub get_sec_str { @@ -1315,8 +1656,12 @@ sub check_clone { sub store_changes { my ($cdom,$cnum,$prefs_order,$actions,$prefs,$values,$changes,$crstype) = @_; my ($chome,$output); - my (%storehash,@delkeys,@need_env_update,@oldcloner); + my (%storehash,@delkeys,@need_env_update,@oldcloner,%oldlinkprot); if ((ref($values) eq 'HASH') && (ref($changes) eq 'HASH')) { + if (ref($values->{'linkprotection'}) eq 'HASH') { + %oldlinkprot = %{$values->{'linkprotection'}}; + } + delete($values->{'linkprotection'}); %storehash = %{$values}; } else { if ($crstype eq 'Community') { @@ -1326,6 +1671,20 @@ sub store_changes { } return $output; } + my ($numchanges,$skipstore); + if (ref($changes) eq 'HASH') { + $numchanges = scalar(keys(%{$changes})); + if (($numchanges == 1) && (exists($changes->{'linkprotection'}))) { + $skipstore = 1; + } elsif (!$numchanges) { + if ($crstype eq 'Community') { + $output = &mt('No changes made to community settings.'); + } else { + $output = &mt('No changes made to course settings.'); + } + return $output; + } + } my %yesno = ( hidefromcat => '1', problem_stream_switch => '1', @@ -1351,6 +1710,103 @@ sub store_changes { "'$storehash{$key}'")).''; } } + } elsif ($item eq 'linkprotection') { + my (%ltienc,$lti_save_error); + if (ref($changes->{$item}) eq 'HASH') { + foreach my $id (sort { $a <=> $b } keys(%{$changes->{$item}})) { + if (ref($changes->{$item}->{$id}) eq 'HASH') { + if (exists($changes->{$item}->{$id}->{'key'})) { + $ltienc{$id}{'key'} = $changes->{$item}->{$id}->{'key'}; + delete($changes->{$item}->{$id}->{'key'}); + } + if (exists($changes->{$item}->{$id}->{'secret'})) { + $ltienc{$id}{'secret'} = $changes->{$item}->{$id}->{'secret'}; + delete($changes->{$item}->{$id}->{'secret'}); + } elsif (ref($oldlinkprot{$id}) eq 'HASH') { + if (exists($oldlinkprot{$id}{'usable'})) { + $changes->{$item}->{$id}->{'usable'} = 1; + } + } + } + } + } + if (keys(%ltienc) > 0) { + if (&Apache::lonnet::put('nohist_ltienc',\%ltienc,$cdom,$cnum,1) eq 'ok') { + foreach my $id (keys(%ltienc)) { + if (exists($ltienc{$id}{'secret'})) { + $changes->{$item}->{$id}->{'usable'} = 1; + } + } + } else { + $lti_save_error = 1; + } + } + unless ($lti_save_error) { + if (&Apache::lonnet::put('lti',$changes->{$item},$cdom,$cnum,1) eq 'ok') { + my $hashid=$cdom.'_'.$cnum; + &Apache::lonnet::devalidate_cache_new('courselti',$hashid); + $chome = &Apache::lonnet::homeserver($cnum,$cdom); + unless (($chome eq 'no_host') || ($chome eq '')) { + my @ids=&Apache::lonnet::current_machine_ids(); + unless (grep(/^\Q$chome\E$/,@ids)) { + &Apache::lonnet::devalidate_cache_new('courseltienc',$hashid); + } + } + foreach my $id (sort { $a <=> $b } %{$changes->{$item}}) { + if (ref($changes->{$item}->{$id}) eq 'HASH') { + my %values = %{$changes->{$item}->{$id}}; + my %desc = &linkprot_names(); + my $display; + foreach my $title ('name','lifetime','version','key','secret') { + if (($title eq 'key') || ($title eq 'secret')) { + if (ref($ltienc{$id}) eq 'HASH') { + if (exists($ltienc{$id}{$title})) { + if ($title eq 'secret') { + my $length = length($ltienc{$id}{$title}); + $display .= $desc{$title}.': '.('*' x $length); + } else { + $display .= $desc{$title}.': '.$ltienc{$id}{$title}.', '; + } + } + } + } elsif ($title eq 'version') { + if ($values{$title} eq 'LTI-1p0') { + $display .= $desc{$title}.': 1.1, '; + } + } else { + $display .= $desc{$title}.': '.$values{$title}.', '; + } + } + $display =~ s/, $//; + $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('[_1] set to [_2]',''.$id.'', + "'$display'")).'
  • '; + } elsif (ref($oldlinkprot{$id}) eq 'HASH') { + my $oldname = $oldlinkprot{$id}{'name'}; + $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('Deleted setting for [_1]',''."$id ($oldname)".'')).'
  • '; + } + } + } else { + $lti_save_error = 1; + } + } + unless ($lti_save_error) { + my @deletions; + foreach my $id (sort { $a <=> $b } keys(%{$changes->{$item}})) { + unless (ref($changes->{$item}->{$id}) eq 'HASH') { + push (@deletions,$id); + } + } + if (@deletions) { + &Apache::lonnet::del('nohist_ltienc',\@deletions,$cdom,$cnum); + } + } + if ($lti_save_error) { + $output .= '
  • '. + ''. + &mt('An error occurred when saving changes to link protection settings, which remain unchanged.'). + ''. + '
  • '; + } } else { if (ref($prefs->{$item}->{'ordered'}) eq 'ARRAY') { my @settings = @{$prefs->{$item}->{'ordered'}}; @@ -1467,6 +1923,32 @@ sub store_changes { } } $displayname = &mt($text); + } elsif ($item eq 'lti') { + if ($key eq 'lti.lcmenu') { + if ($changes->{$item}{$key} eq 'none') { + $displayval = &mt('None of the configurable menu items displayed'); + } else { + my %ltititles = <imenu_titles(); + $displayval = join(', ', map { $ltititles{$_}; } split(/,/,$changes->{$item}{$key})); + } + } else { + if ($changes->{$item}{$key} eq '1') { + $displayval = &mt('Yes'); + } elsif ($changes->{$item}{$key} eq '') { + $displayval = &mt('No'); + } + } + $displayname = &mt($text); + } elsif ($item eq 'menuitems') { + unless ($changes->{$item}{$key} eq '') { + if ($key eq 'menudefault') { + $displayname = &mt('Default collection of menu items'); + $displayval = &mt('Collection: [_1]', + $changes->{$item}{$key}); + } elsif ($key eq 'menucollections') { + $displayval = &menucollections_display($changes->{$item}{$key}); + } + } } else { $displayname = &mt($text); } @@ -1479,7 +1961,8 @@ sub store_changes { $displayval = &Apache::lonlocal::locallocaltime($displayval); } elsif ($key eq 'categories') { $displayval = $env{'form.categories_display'}; - } elsif (($key eq 'canuse_pdfforms') || ($key eq 'usejsme') || ($key eq 'uselcmath')) { + } elsif (($key eq 'canuse_pdfforms') || ($key eq 'usejsme') || + ($key eq 'uselcmath') || ($key eq 'inline_chem')) { if ($changes->{$item}{$key} eq '1') { $displayval = &mt('Yes'); } elsif ($changes->{$item}{$key} eq '0') { @@ -1511,12 +1994,12 @@ sub store_changes { } } unless (&Apache::lonnet::is_course_owner($cdom,$cnum)) { - if ($env{'form.pending_coowoner'} eq 'accept') { + if ($env{'form.pending_coowner'} eq 'accept') { $displayval = &mt('on'); - } elsif ($env{'form.pending_coowoner'} eq 'decline') { + } elsif ($env{'form.pending_coowner'} eq 'decline') { $displayval = ''; $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('Invitation to be co-owner declined')).'
  • '; - } elsif ($env{'form.remove_coowoner'}) { + } elsif ($env{'form.remove_coowner'}) { $displayval = &mt('off'); } if ($displayval) { @@ -1529,8 +2012,32 @@ sub store_changes { } } elsif ($changes->{$item}{$key} eq '') { push(@delkeys,$key); - $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('Deleted setting for [_1]', - ''.$displayname.'')).'
  • '; + if ($item eq 'lti') { + if (($key eq 'lti.override') || (($key ne 'lti.override') && ($env{'form.lti.override'} ne ''))) { + if (($key eq 'lti.lcmenu') && + ((!$env{'form.lti.topmenu'}) && (!$env{'form.lti.inlinemenu'}))) { + $output .= '
  • '.&mt('LTI settings for menu items only saved if page header and/or inline menu is set to be displayed').'
  • '; + } else { + $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('[_1] set to [_2]', + ''.$displayname.'', + "'$displayval'")).'
  • '; + } + } elsif (!exists($changes->{$item}{'lti.override'})) { + $output .= '
  • '.&mt('LTI settings only saved if Override is set to "Yes"').'
  • '; + } + } elsif ($item eq 'menuitems') { + if ($key eq 'menudefault') { + $output .= '
  • '.&mt("Default collection of menu items set to: 'Standard' (all menus shown)").'
  • '; + } elsif ($key eq 'menucollections') { + $output .= '
  • '.&mt('Specific collections of menus no longer available').'
  • '; + } + } else { + $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('Deleted setting for [_1]', + ''.$displayname.'')).'
  • '; + } + } elsif ($key eq 'menucollections') { + $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('Numbered menu collections:')).'
    '. + $displayval.'
  • '; } else { $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('[_1] set to [_2]', ''.$displayname.'', @@ -1569,7 +2076,9 @@ sub store_changes { } if (($key eq 'description') || ($key eq 'cloners') || ($key eq 'hidefromcat') || ($key eq 'categories') || - ($key eq 'co-owners')) { + ($key eq 'co-owners') || ($key eq 'lti.override') || + ($key eq 'lti.topmenu') || ($key eq 'lti.inlinemenu') || + ($key eq 'lti.lcmenu')) { push(@need_env_update,$key); } } @@ -1586,6 +2095,9 @@ sub store_changes { } } } + if ($skipstore) { + return $output; + } if (&Apache::lonnet::put('environment',\%storehash,$cdom,$cnum) eq 'ok') { if (ref($changes) eq 'HASH') { if (ref($changes->{'courseinfo'}) eq 'HASH') { @@ -1653,6 +2165,8 @@ sub update_env { my @coowners = split(',',$storehash->{'internal.'.$key}); $crsinfo{$env{'request.course.id'}}{'co-owners'} = \@coowners; $count ++; + } elsif ($key =~ /^lti\./) { + &Apache::lonnet::appenv({'course.'.$env{'request.course.id'}.'.'.$key => $storehash->{$key}}); } } if ($count) { @@ -1756,7 +2270,7 @@ sub get_course { } sub get_jscript { - my ($cid,$cdom,$phase,$crstype,$settings) = @_; + my ($cid,$cdom,$phase,$crstype,$settings,$noedit) = @_; my ($can_toggle_cat,$can_categorize) = &can_modify_catsettings($cdom,$crstype); my ($jscript,$categorize_js,$loncaparev_js,$instcode_js); my $stubrowse_js = &Apache::loncommon::studentbrowser_javascript(); @@ -1882,11 +2396,128 @@ function syllabusinfo() { } } ENDSCRIPT + my $menuitems_js; + unless ($noedit) { + my $collections; + my $next = 1; + if (ref($settings) eq 'HASH') { + if ($settings->{'menucollections'} ne '') { + my @current; + foreach my $item (split(/;/,$settings->{'menucollections'})) { + my ($num) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + push(@current,$num); + } + } + $collections = join("','",sort { $a <=> $b } @current); + if ($collections) { + $collections = "'$collections'"; + } + $next += $current[-1]; + } + } + my $deftext = &mt('Standard (all menus shown)'); + $menuitems_js = < 0) { + var setvis; + for (var i=0; i'."\n". + $syllabus_js."\n".$menuitems_js."\n". + $linkprotector_js."\n".'//]]>'."\n". ''."\n".$stubrowse_js."\n"; return $jscript; } @@ -2112,8 +2743,8 @@ sub print_courseinfo { size => '20', advanced => 1 }, - 'externalsyllabus' => { - text => ''.&mt($itemtext->{'externalsyllabus'}).'
    '. + 'syllabus' => { + text => ''.&mt($itemtext->{'syllabus'}).'
    '. &mt('(Syllabus type in use)').(' ' x2). $editsyllabus, }, @@ -2358,7 +2989,7 @@ sub print_courseinfo { $datatable .= &manage_coownership($cnum,$cdom,\@currcoown,$noedit); } } - } elsif ($item eq 'externalsyllabus') { + } elsif ($item eq 'syllabus') { my $external = $env{'course.'.$env{'request.course.id'}.'.externalsyllabus'}; my $uploaded = $env{'course.'.$env{'request.course.id'}.'.uploadedsyllabus'}; my $minimal = $env{'course.'.$env{'request.course.id'}.'.minimalsyllabus'}; @@ -2580,7 +3211,7 @@ sub display_loncaparev_constraints { $Apache::lonrelrequtils::checkcrstypes{$crstype}).''; } my (%fromparam,%rowspan,%bymap,%byresource,@scopeorder,%toshow,%allmaps, - %byresponsetype,%bysubmission,%fromblocks); + %byresponsetype,%bysubmission,%fromblocks,%bycrsrestype); @scopeorder = ('all','section/group','user'); my $now = time; my $resourcedata = &Apache::lonparmset::readdata($cnum,$cdom); @@ -2854,15 +3485,22 @@ sub display_loncaparev_constraints { my $rev_anonsurv=$Apache::lonnet::needsrelease{'parameter:type:anonsurvey::'}; my %randtrysubms=&Apache::lonnet::dump('nohist_randomizetry',$cdom,$cnum); my $rev_randtry=$Apache::lonnet::needsrelease{'parameter:type:randomizetry::'}; + my $rev_exttool=$Apache::lonnet::needsrelease{'course:courserestype:exttool'}; my $stdtype=&Apache::lonparmset::standard_parameter_types('type'); my $stdname=&Apache::lonparmset::standard_parameter_names('type'); my $valanon=&get_param_description($stdtype,'anonsurvey'); my $valrandtry=&get_param_description($stdtype,'randomizetry'); my %checkedrev; - foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() },1,0)) { + foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() || $_[0]->is_tool() },1,0)) { my @parts = @{$res->parts()}; my $symb = $res->symb(); my $enclosing_map = &Apache::lonnet::declutter($res->enclosing_map_src()); + if ($res->is_tool()) { + my $rev = $rev_exttool; + $bycrsrestype{$symb}{$rev} = &mt('External Tool'); + $allmaps{$enclosing_map} = 1; + next; + } foreach my $part (@parts) { if (exists($anonsubms{$symb."\0".$part})) { my $rev = $rev_anonsurv; @@ -2950,6 +3588,10 @@ sub display_loncaparev_constraints { } } } + my $suppmap = 'supplemental.sequence'; + my ($suppcount,$supptools,$errors) = (0,0,0); + ($suppcount,$supptools,$errors) = &Apache::loncommon::recurse_supplemental($cnum,$cdom, + $suppmap,$suppcount,$supptools,$errors); my $mapres_header = '

    '. &mt('Requirements for specific folders or resources'). '

    '; @@ -2964,9 +3606,9 @@ sub display_loncaparev_constraints { $r->print($start_page.$top_header); if ($output) { $r->print($output); - if (defined($navmap)) { + if (defined($navmap) || $supptools) { &show_contents_view($r,$navmap,$cid,\%toshow,\%bymap,\%byresource,\%bysubmission, - \%byresponsetype,\@scopeorder,\%lt); + \%byresponsetype,\%bycrsrestype,\@scopeorder,\%lt,$supptools); } $r->print('

    '); } else { @@ -2984,22 +3626,30 @@ sub display_loncaparev_constraints { sub show_contents_view { my ($r,$navmap,$cid,$toshow,$bymap,$byresource,$bysubmission,$byresponsetype, - $scopeorder,$lt) = @_; - if ((keys(%{$toshow}) > 0) || (keys(%{$byresource}) > 0) || - (keys(%{$bysubmission}) > 0) || (keys(%{$byresponsetype}) > 0)) { - my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons"); - my $whitespace = ''; - my $icon = ''; + $bycrsrestype,$scopeorder,$lt,$supptools) = @_; + my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons"); + my $whitespace = ''; + my $showmaincontent; + if ((ref($navmap)) && + ((keys(%{$toshow}) > 0) || (keys(%{$byresource}) > 0) || + (keys(%{$bysubmission}) > 0) || (keys(%{$byresponsetype}) > 0) || + (keys(%{$bycrsrestype}) > 0))) { + $showmaincontent = 1; + } + if ($showmaincontent || $supptools) { + $r->print(&Apache::loncommon::start_data_table()); + } + if ($showmaincontent) { + my $icon = ''; my $topmap = $env{'course.'.$cid.'.url'}; - $r->print(&Apache::loncommon::start_data_table(). - &Apache::loncommon::start_data_table_header_row(). + $r->print(&Apache::loncommon::start_data_table_header_row(). ''.&mt('Location').''.&mt('Release').''. ''.&mt('Attribute/Setting').''. &Apache::loncommon::end_data_table_header_row(). &Apache::loncommon::start_data_table_row(). ' '.$icon.' '.&mt('Main Content').''); - &releases_by_map($r,$bymap,$topmap,$scopeorder,$lt); - $r->print(&Apache::loncommon::end_data_table_row()); + &releases_by_map($r,$bymap,$topmap,$scopeorder,$lt); + $r->print(&Apache::loncommon::end_data_table_row()); my $it=$navmap->getIterator(undef,undef,undef,1,1,undef); my $curRes; my $depth = 0; @@ -3050,6 +3700,9 @@ sub show_contents_view { if (ref($byresponsetype->{$symb}) eq 'HASH') { $rowspan += scalar(keys(%{$byresponsetype->{$symb}})); } + if (ref($bycrsrestype->{$symb}) eq 'HASH') { + $rowspan += scalar(keys(%{$bycrsrestype->{$symb}})); + } next if (!$rowspan); $icon = ''.
                             &mt('Problem').''; @@ -3114,10 +3767,37 @@ sub show_contents_view { $r->print(''); } $r->print(&Apache::loncommon::end_data_table_row()); + $newrow = 1; + } + if (ref($bycrsrestype->{$symb}) eq 'HASH') { + foreach my $rev (sort(keys(%{$bycrsrestype->{$symb}}))) { + if ($newrow) { + $r->print(&Apache::loncommon::continue_data_table_row()); + } + $r->print(''.$rev.''.$bycrsrestype->{$symb}{$rev}.''); + } + $r->print(&Apache::loncommon::end_data_table_row()); } } } } + } + if ($supptools) { + my $icon = ''; + $r->print(&Apache::loncommon::start_data_table_header_row(). + ''.&mt('Location').''.&mt('Release').''. + ''.&mt('Resource Type').''. + &Apache::loncommon::end_data_table_header_row(). + &Apache::loncommon::start_data_table_row(). + ' '.$icon.' '.&mt('Supplemental Content').''. + ''. + $Apache::lonrelrequtils::exttool{'major'}.'.'. + $Apache::lonrelrequtils::exttool{'minor'}. + ''.&mt('[quant,_1,External Tool,External Tools]', + $supptools).''. + &Apache::loncommon::end_data_table_row()); + } + if ($showmaincontent || $supptools) { $r->print(&Apache::loncommon::end_data_table()); } return; @@ -3268,7 +3948,7 @@ sub coowner_invitations { @pendingcoown = split(',',$pendingcoowners); } if (ref($currcoownref) eq 'ARRAY') { - @currcoown == @{$currcoownref}; + @currcoown = @{$currcoownref}; } my $disabled; if ($noedit) { @@ -3332,7 +4012,7 @@ sub manage_coownership { @pendingcoown = split(',',$pendingcoowners); } if (ref($currcoownref) eq 'ARRAY') { - @currcoown == @{$currcoownref}; + @currcoown = @{$currcoownref}; } my $disabled; if ($noedit) { @@ -3367,10 +4047,10 @@ sub manage_coownership { } $output .= ''; if ($is_coowner) { - $output .= &mt('You are currently a co-owner:').' '; + $output .= &mt('You are currently a co-owner:').' '; } else { - $output .= &mt('The course owner has invited you to become a co-owner:').' '.(' 'x2). - ''; + $output .= &mt('The course owner has invited you to become a co-owner:').' '.(' 'x2). + ''; } $output .= ''; if (@currcoown) { @@ -3540,8 +4220,8 @@ sub print_feedback { } if ($position eq 'top') { my $includeempty = 0; - $datatable .= ''. - &user_table($cdom,$item,\@sections, + $datatable .= ''. + &user_table($cdom,$item,\@sections, $settings->{$item},\%lt,$noedit); } else { $datatable .= &Apache::lonhtmlcommon::textbox($item.'.text', @@ -3628,7 +4308,7 @@ sub select_recipient { my $domform = &Apache::loncommon::select_dom_form($cdom,$item.'_udom_'.$num,$includeempty); my $selectlink = &Apache::loncommon::selectstudent_link('display',$item.'_uname_'.$num, - $item.'_udom_'.$num,1); + $item.'_udom_'.$num,'only'); my $output = ''. @@ -3764,8 +4444,8 @@ sub print_discussion { '
    '.&mt('Username').'
    '. '
    '.&role_checkboxes($cdom,$cnum,$item,$settings,undef,undef,$noedit). '
    '; } elsif ($item eq 'plc.users.denied') { - $datatable .= ''. - &user_table($cdom,$item,undef, + $datatable .= ''. + &user_table($cdom,$item,undef, $settings->{$item},\%lt,$noedit); } elsif ($item eq 'pch.roles.denied') { $datatable .= ''. @@ -4135,6 +4815,10 @@ sub print_appearance { text => ''.&mt($itemtext->{'usejsme'}).'', input => 'radio', }, + 'inline_chem' => { + text => ''.&mt($itemtext->{'inline_chem'}).'', + input => 'radio', + }, ); return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype,'appearance',$noedit); } @@ -4326,6 +5010,559 @@ sub print_bridgetasks { return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype,'bridgetasks',$noedit); } +sub print_lti { + my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype,$noedit) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { + return; + } + my @menuitems = ('fullname','coursetitle','role','logout','grades'); + my %menutitles = <imenu_titles(); + my ($domdefs,$displaydefs); + if ($env{'request.lti.login'}) { + my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider'); + my @domdefsmenu; + if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') { + if ($lti{$env{'request.lti.login'}}{'topmenu'}) { + $domdefs = &mt('Header shown').', '; + } + if ($lti{$env{'request.lti.login'}}{'inlinemenu'}) { + $domdefs .= &mt('Inline menu shown'); + } + $domdefs =~ s/,\s$//; + if (!$domdefs) { + $domdefs = &mt('No header or inline menu shown'); + } + $domdefs = ' ('.&mt('Domain default').': '.$domdefs.')'; + if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') { + @domdefsmenu = @{$lti{$env{'request.lti.login'}}{'lcmenu'}}; + } + } + $displaydefs = &mt('Domain defaults'); + if (@domdefsmenu) { + my @diffs = &Apache::loncommon::compare_arrays(\@menuitems,\@domdefsmenu); + if (@diffs) { + my $missing; + foreach my $item (@menuitems) { + unless (grep(/^\Q$item\E$/,@domdefsmenu)) { + $missing .= ' '.$menutitles{$item}.','; + } + } + $missing =~ s/,$//; + $displaydefs .= ': '.&mt('Not shown').' --'.$missing; + } else { + $displaydefs .= ': '.&mt('All shown'); + } + } else { + $displaydefs .= ': '.&mt('Not shown').' -- '.join(', ', map { $menutitles{$_}; } @menuitems); + } + } else { + $displaydefs = &mt('Domain defaults only available in LTI context'); + } + my %items = ( + 'lti.override' => { + text => ''.&mt($itemtext->{'lti.override'}).''.$domdefs, + input => 'radio', + }, + 'lti.topmenu' => { + text => ''.&mt($itemtext->{'lti.topmenu'}).'', + input => 'radio', + }, + 'lti.inlinemenu' => { + text => ''.&mt($itemtext->{'lti.inlinemenu'}).'', + input => 'radio', + }, + 'lti.lcmenu' => { + text => ''.&mt($itemtext->{'lti.lcmenu'}).'
    '.$displaydefs, + input => 'custom', + }, + ); + return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype,'lti',$noedit); +} + +sub lcmenu_checkboxes { + my ($cdom,$caller,$settings,$crstype,$noedit) = @_; + my @menuitems = ('fullname','coursetitle','role','logout','grades'); + my %menutitles = <imenu_titles(); + my (@current,@domdefs); + if ($env{'request.lti.login'}) { + my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider'); + if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') { + if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') { + @domdefs = @{$lti{$env{'request.lti.login'}}{'lcmenu'}}; + } + } + } + if (ref($settings) eq 'HASH') { + if ($settings->{'lti.lcmenu'}) { + unless ($settings->{'lti.lcmenu'} eq 'none') { + @current = split(',',$settings->{'lti.lcmenu'}); + } + } else { + @current = @domdefs; + } + } else { + @current = @domdefs; + } + my $numinrow = 3; + my $count = 0; + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } + my $output = ''; + foreach my $item (@menuitems) { + my $checked = ''; + if (grep(/^\Q$item\E$/,@current)) { + $checked = ' checked="checked" '; + } + my $rem = $count%($numinrow); + if ($rem == 0) { + if ($count > 0) { + $output .= ''; + } + $output .= ''; + } + $output .= ''; + $count ++; + } + my $rem = $count%($numinrow); + my $colsleft = $numinrow - $rem; + if ($colsleft > 1 ) { + $output .= ''; + } elsif ($colsleft == 1) { + $output .= ''; + } + $output .= '
    '. + '  
    '; +} + +sub ltimenu_titles { + return &Apache::lonlocal::texthash( + fullname => 'Full name', + coursetitle => 'Course title', + role => 'Role', + logout => 'Logout', + grades => 'Grades', + ); +} + +sub print_menuitems { + my ($position,$cdom,$settings,$itemtext,$rowtotal,$crstype,$noedit) = @_; + unless ((ref($settings) eq 'HASH') && (ref($itemtext) eq 'HASH')) { + return; + } + if ($position eq 'top') { + my (%defaultmenu_options,@defaultmenu_order,$addcollection); + if ($settings->{'menucollections'} ne '') { + foreach my $item (split(/;/,$settings->{'menucollections'})) { + my ($num,$value) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + $defaultmenu_options{$num} = $num; + } + } + @defaultmenu_order = sort { $a <=> $b } keys(%defaultmenu_options); + $addcollection = $defaultmenu_order[-1] + 1; + } else { + $addcollection = 1; + } + $defaultmenu_options{$addcollection} = $addcollection; + my %items = ( + 'menudefault' => { + text => ''.&mt($itemtext->{'menudefault'}).'
    '. + &mt("(can be overriden in deep-link context)"), + input => 'selectbox', + options => \%defaultmenu_options, + order => \@defaultmenu_order, + nullval => &mt('Standard (all menus shown)'), + }, + ); + return &make_item_rows($cdom,\%items,['menudefault'],$settings,$rowtotal,$crstype,'menuitems',$noedit); + } else { + my %menu; + my $count = 0; + my $next = 1; + my ($datatable,$disabled); + if ($noedit) { + $disabled = ' disabled="disabled"'; + } + + my ($ordered,$cats) = &menuitems_categories(); + my @order = @{$ordered}; + my %categories = %{$cats}; + my %menutitles = &menuitems_titles(); + my %menufields = &menuitems_fields(); + + if ($settings->{'menucollections'} ne '') { + foreach my $item (split(/;/,$settings->{'menucollections'})) { + my ($num,$value) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + my @entries = split(/\&/,$value); + foreach my $entry (@entries) { + my ($name,$fields) = split(/=/,$entry); + $menu{$num}{$name} = $fields; + } + } + } + if (keys(%menu)) { + my @current = sort { $a <=> $b } keys(%menu); + $next += $current[-1]; + foreach my $num (@current) { + my %checked; + my $on = ' checked="checked"'; + foreach my $key (keys(%{$menu{$num}})) { + if (($key eq 'top') || ($key eq 'inline') || ($key eq 'foot') || ($key eq 'main')) { + if ($menu{$num}{$key} eq 'y') { + $checked{$key} = $on; + } + } else { + foreach my $field (split(/,/,$menu{$num}{$key})) { + if (exists($menufields{$field})) { + $checked{$field} = $on; + } + } + } + } + if (ref($menu{$num}) eq 'HASH') { + $datatable .= &item_table_row_start(''.$num.'',$count,'','','','LC_left_item'); + foreach my $category (@order) { + if ((ref($categories{$category}) eq 'ARRAY') && (@{$categories{$category}} > 0)) { + $datatable .= '
    '.$menutitles{$category}.''."\n"; + if ($category eq 'text') { + $datatable .= ''.&mt('Header').'
    '; + } + foreach my $field (@{$categories{$category}}) { + if ($field eq 'disc') { + $datatable .= '
    '.&mt('Footer').'
    '; + } + $datatable .= '
    '; + } + $datatable .= '
    '; + } + } + $datatable .= &item_table_row_end(); + $count ++; + } + } + } + } elsif ($noedit) { + my $text = &mt('No menu collections defined for this course.'); + $datatable .= &item_table_row_start($text,$count); + } + unless ($noedit) { + my $add = ''; + $datatable .= &item_table_row_start($add,$count,'','','','LC_left_item'); + foreach my $category (@order) { + if ((ref($categories{$category}) eq 'ARRAY') && (@{$categories{$category}} > 0)) { + $datatable .= ''; + } + } + $datatable .= &item_table_row_end(); + $count ++; + } + return $datatable; + } +} + +sub menuitems_abbreviations { + my %briefcats = ( + text => 'pt', + links => 'p', + list => 'ps', + inline => 's', + ); + return %briefcats; +} + +sub menuitems_categories { + my @order = ('shown','text','links','list','inline'); + my %categories = ( + shown => ['top','inline','foot','main'], + text => ['name','role','crs','disc','fdbk'], + links => ['pers','logo','menu','comm','roles','help','logout'], + list => ['about','prefs','port','wish','anno','rss'], + inline => ['cont','grades','chat','people','groups','resv','syll','feeds'], + ); + return (\@order,\%categories); +} + +sub menuitems_titles { + return &Apache::lonlocal::texthash ( + shown => 'Hierarchy', + text => 'Text', + links => 'Header links', + list => 'Drop-down list', + inline => 'Inline links', + ); +} + +sub menuitems_fields { + return &Apache::lonlocal::texthash ( + top => 'Display header', + inline => 'Display inline menu', + foot => 'Display footer', + main => 'Access to main menu', + pers => 'Personal', + logo => 'LON-CAPA', + menu => 'Home', + comm => 'Messages', + roles => 'Roles/Courses', + help => 'Help', + logout => 'Logout', + name => 'Fullname', + crs => 'Course Title', + role => 'Current Role', + disc => 'Discussion', + fdbk => 'Feedback', + about => 'Information', + prefs => 'Preferences', + port => 'Portfolio', + wish => 'Stored Links', + anno => 'Calendar', + rss => 'RSS Feeds', + cont => 'Contents', + grades => 'Grades', + chat => 'Chat', + people => 'People', + groups => 'Groups', + resv => 'Reservations', + syll => 'Syllabus', + feeds => 'Feeds', + ); +} + +sub menucollections_display { + my ($collections) = @_; + my %menu; + my ($ordered,$cats) = &menuitems_categories(); + my @order = @{$ordered}; + my %categories = %{$cats}; + my %menutitles = &menuitems_titles(); + my %menufields = &menuitems_fields(); + foreach my $item (split(/;/,$collections)) { + my ($num,$value) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + my @entries = split(/\&/,$value); + foreach my $entry (@entries) { + my ($name,$fields) = split(/=/,$entry); + $menu{$num}{$name} = $fields; + } + } + } + my $output = ''; + if (keys(%menu)) { + my @current = sort { $a <=> $b } keys(%menu); + foreach my $num (@current) { + my %checked; + foreach my $key (keys(%{$menu{$num}})) { + if (($key eq 'top') || ($key eq 'inline') || ($key eq 'foot') || ($key eq 'main')) { + if ($menu{$num}{$key} eq 'y') { + $checked{$key} = 1; + } + } else { + foreach my $field (split(/,/,$menu{$num}{$key})) { + if (exists($menufields{$field})) { + $checked{$field} = 1; + } + } + } + } + if (ref($menu{$num}) eq 'HASH') { + $output .= '
    '.&mt('Collection [_1]',$num).''; + foreach my $category (@order) { + if ((ref($categories{$category}) eq 'ARRAY') && (@{$categories{$category}} > 0)) { + $output .= '
    '. + ''.$menutitles{$category}.''."\n"; + if ($category eq 'text') { + $output .= ''.&mt('Header Text').'

    '; + } + foreach my $field (@{$categories{$category}}) { + if ($field eq 'disc') { + $output .= '
    '.&mt('Footer Text').'

    '; + } + if ($checked{$field}) { + $output .= &Apache::lonhtmlcommon::confirm_success($menufields{$field}); + } else { + $output .= &Apache::lonhtmlcommon::confirm_success($menufields{$field},1); + } + $output .= '
    '; + } + $output .= '
    '; + } + } + $output .= '
    '; + } + } + } + return $output; +} + +sub print_linkprotection { + my ($cdom,$cnum,$settings,$rowtotal,$crstype,$noedit) = @_; + unless (ref($settings) eq 'HASH') { + return; + } + + my %linkprotection; + my $count = 0; + my $next = 1; + my ($datatable,$disabled,$css_class); + if ($noedit) { + $disabled = ' disabled="disabled"'; + } + my %lt = &linkprot_names(); + my $itemcount = 0; + + my $switchserver = &check_switchserver($cdom,$cnum); + + if (ref($settings->{'linkprotection'}) eq 'HASH') { + if (keys(%{$settings->{'linkprotection'}})) { + my @current = sort { $a <=> $b } keys(%{$settings->{'linkprotection'}}); + $next += $current[-1]; + for (my $i=0; $i<@current; $i++) { + my $num = $current[$i]; + my %values; + if (ref($settings->{'linkprotection'}->{$num}) eq 'HASH') { + %values = %{$settings->{'linkprotection'}->{$num}}; + } else { + next; + } + my $selected; + if (($values{'version'} eq 'LTI-1p0') || ($values{'version'} eq '')) { + $selected = ' selected="selected"'; + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= + ''. + ''. + ''.$lt{'name'}. + ': '. + (' 'x2). + ''.$lt{'version'}.': '."\n". + (' 'x2). + ''.$lt{'lifetime'}.':'. + '

    '; + if ($values{'key'} ne '') { + $datatable .= ''.$lt{'key'}; + if ($noedit) { + $datatable .= ': ['.&mt('not shown').']'; + } elsif ($switchserver) { + $datatable .= ': ['.&mt('[_1] to view/edit',$switchserver).']'; + } else { + $datatable .= ':'; + } + $datatable .= ' '.(' 'x2); + } elsif (!$switchserver) { + $datatable .= ''.$lt{'key'}.':'. + ''. + ' '.(' 'x2); + } + if ($switchserver) { + if ($values{'usable'} ne '') { + $datatable .= '
    '. + $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'
    '. + ''.&mt('Change secret?'). + ''. + (' 'x2). + ''.(' 'x2). + ''; + } elsif ($values{'key'} eq '') { + $datatable .= ''.&mt('Key and Secret are required').' - '.&mt("submit from course's home server: [_1].",$switchserver).''."\n"; + } else { + $datatable .= ''.&mt('Secret required').' - '.&mt("submit from course's home server: [_1].",$switchserver).''."\n"; + } + } else { + if ($values{'usable'} ne '') { + $datatable .= '
    '. + $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'
    '. + ''.&mt('Change?'). + ''. + (' 'x2). + '  '; + } else { + $datatable .= + ''.$lt{'secret'}.':'. + ''. + ''. + ''; + } + } + $datatable .= ''; + $itemcount ++; + } + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''."\n". + ''."\n". + ''.&mt('Add').''."\n". + ''. + ''.$lt{'name'}. + ': '."\n". + (' 'x2). + ''.$lt{'version'}.': '."\n". + (' 'x2). + ''.$lt{'lifetime'}.': '."\n". + '

    '; + if ($switchserver) { + $datatable .= ''.&mt('Key and Secret are required').' - '.&mt("submit from course's home server: [_1].",$switchserver).''."\n"; + } else { + $datatable .= ''.$lt{'key'}.': '."\n". + (' 'x2). + ''.$lt{'secret'}.':'. + ' '."\n"; + } + $datatable .= ''; + $$rowtotal ++; + return $datatable;; +} + +sub linkprot_names { + my %lt = &Apache::lonlocal::texthash( + 'version' => 'LTI Version', + 'key' => 'Key', + 'lifetime' => 'Nonce lifetime (s)', + 'name' => 'Launcher Application Name', + 'secret' => 'Secret', + ); + return %lt; +} + +sub check_switchserver { + my ($cdom,$cnum) = @_; + my ($allowed,$switchserver); + my $home = &Apache::lonnet::homeserver($cnum,$cdom); + unless ($home eq 'no_host') { + my @ids=&Apache::lonnet::current_machine_ids(); + foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } } + if (!$allowed) { + $switchserver=''.&mt('Switch Server').''; + } + } + return $switchserver; +} + sub print_other { my ($cdom,$settings,$allitems,$rowtotal,$crstype,$noedit) = @_; unless ((ref($settings) eq 'HASH') && (ref($allitems) eq 'ARRAY')) { @@ -4348,7 +5585,7 @@ sub print_other { input => 'textbox', size => '30', }; - my $output = &make_item_rows($cdom,\%items,\@ordered,$settings,$rowtotal,$crstype,'other',$noedit); + return &make_item_rows($cdom,\%items,\@ordered,$settings,$rowtotal,$crstype,'other',$noedit); } sub get_other_items { @@ -4377,17 +5614,23 @@ sub get_other_items { } sub item_table_row_start { - my ($text,$count,$add_class,$colspan) = @_; + my ($text,$count,$add_class,$colspan,$leftclass,$rightclass) = @_; my $output; my $css_class = ($count % 2) ? 'LC_odd_row' : 'LC_even_row'; $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq ''); + if ($leftclass eq '') { + $leftclass = 'LC_left_item'; + } + if ($rightclass eq '') { + $rightclass = 'LC_right_item'; + } $output .= ''."\n". - ''.$text. + ''.$text. ''; - if ($colspan) { - $output .= ''; + if ($colspan > 1) { + $output .= ''; } else { - $output .= ''; + $output .= ''; } return $output; } @@ -4428,7 +5671,7 @@ sub yesno_radio { } sub select_from_options { - my ($item,$order,$options,$curr,$nullval,$multiple,$maxsize,$onchange,$noedit) = @_; + my ($item,$order,$options,$curr,$nullval,$multiple,$maxsize,$onchange,$noedit,$id) = @_; my $output; my $disabled; if ($noedit) { @@ -4444,6 +5687,9 @@ sub select_from_options { $output .= ' size="'.$maxsize.'"'; } } + if ($id ne '') { + $output .= ' id="'.$id.'"'; + } $output .= $disabled.'>'."\n"; if ($nullval ne '') { $output .= '