--- loncom/interface/lonparmset.pm 2020/12/24 00:37:30 1.598 +++ loncom/interface/lonparmset.pm 2023/04/03 15:39:10 1.618 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set parameters for assessments # -# $Id: lonparmset.pm,v 1.598 2020/12/24 00:37:30 raeburn Exp $ +# $Id: lonparmset.pm,v 1.618 2023/04/03 15:39:10 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -329,6 +329,7 @@ use Apache::lonnavmaps; use Apache::longroup; use Apache::lonrss; use HTML::Entities; +use Text::Wrap(); use LONCAPA qw(:DEFAULT :match); @@ -978,7 +979,7 @@ sub storeparm_by_symb_inner { # @param {string} $type - the parameter type # @param {boolean} $editable - Set to true to get an icon when no value is defined. sub valout { - my ($value,$type,$name,$editable)=@_; + my ($value,$type,$editable)=@_; my $result = ''; # Values of zero are valid. if (! $value && $value ne '0') { @@ -1245,16 +1246,21 @@ function validateParms() { var ipallowRegExp = /^setipallow_/; var ipdenyRegExp = /^setipdeny_/; var deeplinkRegExp = /^deeplink_/; - var dlListScopeRegExp = /^deeplink_(listing|scope)_/; - var dlLinkUrlsRegExp = /^deeplink_urls_/; - var dlLtiRegExp = /^deeplink_lti_/; + var dlListScopeRegExp = /^deeplink_(state|others|listing|scope)_/; + var dlLinkProtectRegExp = /^deeplink_protect_/; + var dlLtidRegExp = /^deeplink_ltid_/; + var dlLticRegExp = /^deeplink_ltic_/; var dlKeyRegExp = /^deeplink_key_/; var dlMenusRegExp = /^deeplink_menus_/; var dlCollsRegExp = /^deeplink_colls_/; + var dlTargetRegExp = /^deeplink_target_/; + var dlExitRegExp = /^deeplink_exit_/; + var dlExitTextRegExp = /^deeplink_exittext_/; var patternIP = /[\[\]\*\.a-zA-Z\d\-]+/; - if ((document.parmform.elements.length != 'undefined') && (document.parmform.elements.length) != 'null') { - if (document.parmform.elements.length) { - for (i=0; i 0) { + var linktarget = document.parmform.elements[i].options[idx].value + linktarget = linktarget.replace(/^\s+|\s+$/g,''); + if (document.parmform.elements['set_'+identifier].value) { + linktarget = ','+linktarget; + } + document.parmform.elements['set_'+identifier].value += linktarget; + } + } else if (dlExitRegExp.test(name)) { + if (document.parmform.elements[i].checked) { + var identifier = name.replace(dlExitRegExp,''); + var posslinkexit = document.parmform.elements[i].value; + posslinkexit = posslinkexit.replace(/^\s+|\s+$/g,''); + if (document.parmform.elements['set_'+identifier].value) { + posslinkexit = ','+posslinkexit; + } + document.parmform.elements['set_'+identifier].value += posslinkexit; + } + } else if (dlExitTextRegExp.test(name)) { + var identifier = name.replace(dlExitTextRegExp,''); + if ((isRadioSet('deeplink_exit_'+identifier,'yes')) || + (isRadioSet('deeplink_exit_'+identifier,'url'))) { + var posstext = document.parmform.elements[i].value; + posstext = posstext.replace(/^\s+|\s+$/g,''); + var origlength = posstext.length; + posstext = posstext.replace(/[:;'",]/g,''); + var newlength = posstext.length; + if (newlength > 0) { + var change = origlength - newlength; + if (change) { + alert(change+' disallowed character(s) removed from Exit Button text'); + } + if (posstext !== 'Exit Tool') { + posstext = ':'+posstext; + document.parmform.elements['set_'+identifier].value += posstext; + } + } else { + document.parmform.elements['set_'+identifier].value = ''; + if (newlength < origlength) { + alert("An exit link type of 'In use' was selected but the button text value was blank, after removing disallowed characters.\nDisallowed characters are ,\":;'"); + } else { + alert("An exit link type of 'In use' was selected but the button text value was blank.\nPlease enter the text to use."); + } + return false; + } + } } } } @@ -1444,6 +1515,8 @@ END # Javascript function toggleSecret, for overview mode. sub done_proctor_js { + my $defaultdone = &mt('Done'); + &js_escape(\$defaultdone); return <<"END"; function toggleSecret(form,radio,key) { var radios = form[radio+key]; @@ -1460,6 +1533,15 @@ function toggleSecret(form,radio,key) { document.getElementById('done_'+key+'_proctorkey').value=''; } } + if (document.getElementById('done_'+key+'_buttontext')) { + if (radios[i].value == '') { + document.getElementById('done_'+key+'_buttontext').value = ''; + } else { + if (document.getElementById('done_'+key+'_buttontext').value == '') { + document.getElementById('done_'+key+'_buttontext').value = '$defaultdone'; + } + } + } } } } @@ -1478,38 +1560,68 @@ function toggleDeepLink(form,item,key) { if (document.getElementById('deeplink_key_'+item+'_'+key)) { keybox = document.getElementById('deeplink_key_'+item+'_'+key); } - var divoption; - if (item == 'urls') { - divoption = 'lti' + var divoptions = new Array(); + if (item == 'protect') { + divoptions = ['ltic','ltid']; } else { if (item == 'menus') { - divoption = 'colls'; + divoptions = ['colls']; } } - var seldiv; - if (document.getElementById('deeplinkdiv_'+divoption+'_'+item+'_'+key)) { - seldiv = document.getElementById('deeplinkdiv_'+divoption+'_'+item+'_'+key); + var seldivs = new Array(); + if ((item == 'protect') || (item == 'menus')) { + for (var i=0; i $b } (keys(%lti))) { + if (($item =~ /^\d+$/) && (ref($lti{$item}) eq 'HASH')) { + $domltistr .= $item.':'.&escape(&escape($lti{$item}{'name'})).','; } } + $domltistr =~ s/,$//; + if ($domltistr) { + $extra = 'ltid_'.$domltistr; + } } - if (keys(%posslti)) { - $extra = 'lti_'; - foreach my $lti (sort { $a <=> $b } keys(%posslti)) { - $extra .= $lti.':'.&escape(&escape($posslti{$lti})).','; + my %courselti = &Apache::lonnet::get_course_lti($cnum,$cdom); + if (keys(%courselti)) { + foreach my $item (sort { $a <=> $b } keys(%courselti)) { + if (($item =~ /^\d+$/) && (ref($courselti{$item}) eq 'HASH')) { + $crsltistr .= $item.':'.&escape(&escape($courselti{$item}{'name'})).','; + } + } + $crsltistr =~ s/,$//; + if ($crsltistr) { + if ($extra) { + $extra .= '&'; + } + $extra .= 'ltic_'.$crsltistr; } - $extra =~ s/,$//; } if ($env{'course.'.$env{'request.course.id'}.'.menucollections'}) { my @colls; @@ -1912,9 +2036,7 @@ sub print_td { $nolink = 1; } } elsif ($mprefix =~ /availablestudent\&$/) { - if ($which > 4) { - $nolink = 1; - } + $nolink = 1; } elsif ($mprefix =~ /examcode\&$/) { unless ($which == 2) { $nolink = 1; @@ -2035,6 +2157,7 @@ sub parm_control_group { # @param {hash reference} $uris - hash resource/map id -> resource src # @param {hash reference} $keyorder - hash parameter key -> appearance rank for this parameter when looking through every resource and every parameter, starting at 100 (integer) # @param {hash reference} $defkeytype - hash parameter name -> parameter type +# @param {string} $pssymb - resource symb (when a single resource is selected) sub extractResourceInformation { my $ids = shift; my $typep = shift; @@ -2048,11 +2171,22 @@ sub extractResourceInformation { my $uris=shift; my $keyorder=shift; my $defkeytype=shift; + my $pssymb=shift; my $keyordercnt=100; my $navmap = Apache::lonnavmaps::navmap->new(); - my @allres=$navmap->retrieveResources(undef,undef,1,undef,1); + return unless(ref($navmap)); + my @allres; + if ($pssymb ne '') { + my $res = $navmap->getBySymb($pssymb); + if (ref($res)) { + @allres = ($res); + } + } + if (!@allres) { + @allres=$navmap->retrieveResources(undef,undef,1,undef,1); + } foreach my $resource (@allres) { my $id=$resource->id(); my ($mapid,$resid)=split(/\./,$id); @@ -2176,7 +2310,7 @@ sub isdateparm { sub is_specialstring { my $type=shift; - return (($type=~/^string_/) && (($type ne 'string_yesno'))); + return (($type=~/^string_/) && ($type ne 'string_yesno')); } # Prints the HTML and Javascript to select parameters, with various shortcuts. @@ -2419,6 +2553,8 @@ sub parmboxes { &whatIsMyCategory($tempparameter, \%categoryList); } #part to print the parm-list + $Text::Wrap::columns=60; + $Text::Wrap::separator='
'; foreach my $key (sort { $category_order{$a} <=> $category_order{$b} } keys(%categoryList)) { next if (@{$categoryList{$key}} == 0); next if ($key eq ''); @@ -2432,8 +2568,9 @@ sub parmboxes { if ($$pscat[0] eq "all" || grep $_ eq $tempkey, @{$pscat}) { $r->print( ' checked="checked"'); } - $r->print(' />'.($$allparms{$tempkey}=~/\S/ ? $$allparms{$tempkey} - : $tempkey) + $r->print(' />'.($$allparms{$tempkey}=~/\S/ ? + Text::Wrap::wrap('',' 'x4,$$allparms{$tempkey}) + : $tempkey) .'
'."\n"); } $r->print(''); @@ -2692,8 +2829,9 @@ sub displaymenu { # @param {string} $pschp - selected map pc, or 'all' # @param {hash reference} $maptitles - hash map id or src -> map title # @param {hash reference} $symbp - hash map pc or resource/map id -> map src.'___(all)' or resource symb +# @param {string} $parmlev - parameter level (Resource:'full', Map:'map', Course:'general') sub mapmenu { - my ($r,$allmaps,$pschp,$maptitles,$symbp)=@_; + my ($r,$allmaps,$pschp,$maptitles,$symbp,$parmlev)=@_; my %allmaps_inverted = reverse %$allmaps; my $navmap = Apache::lonnavmaps::navmap->new(); my $tree=[]; @@ -2741,7 +2879,11 @@ sub mapmenu { } } # Show it ... - $r->print(&Apache::lonhtmlcommon::row_title(&mt('Select Enclosing Map or Folder'),'','',' id="mapmenu"')); + my $rowattr = ' id="mapmenu"'; + if ($parmlev eq 'general') { + $rowattr .= ' style="display:none"'; + } + $r->print(&Apache::lonhtmlcommon::row_title(&mt('Select Enclosing Map or Folder'),'','',$rowattr)); if ((ref($tree) eq 'ARRAY') && (ref($treeinfo) eq 'HASH')) { my $icon = ''; my $whitespace = @@ -2983,19 +3125,33 @@ sub keysindisplayorder { # # @param {Apache2::RequestRec} $r - the Apache request # @param {string} $sortorder - realmstudent|studentrealm +# @param {string} $context - newoverview|overview sub sortmenu { - my ($r,$sortorder)=@_; - $r->print('

'); } # Returns a hash parameter key -> order (integer) giving the order for some parameters. @@ -3325,7 +3481,7 @@ sub assessparms { # --------------------------------------------------------- Get all assessments &extractResourceInformation(\@ids, \%typep,\%keyp, \%allparms, \%allparts, \%allmaps, \%mapp, \%symbp,\%maptitles,\%uris, - \%keyorder); + \%keyorder,undef,$pssymb); %allmaps_inverted = reverse(%allmaps); @@ -3342,6 +3498,7 @@ sub assessparms { my $chome = $env{'course.'.$env{'request.course.id'}.'.home'}; my ($got_chostname,$chostname,$cmajor,$cminor); my $totalstored = 0; + my $totalskippeduser = 0; my $now = time; for (my $i=0;$i<=$#markers;$i++) { my ($needsrelease,$needsnewer,$name,$namematch); @@ -3350,6 +3507,11 @@ sub assessparms { } if ($markers[$i] =~ /\&(8|7|6|5)$/) { next if ($noeditgrp); + } elsif ($markers[$i] =~ /\&(4|3|2|1)$/) { + if ($uname eq '') { + $totalskippeduser ++; + next; + } } if ($markers[$i] =~ /\&(17|11|7|3)$/) { $namematch = 'maplevelrecurse'; @@ -3488,9 +3650,27 @@ sub assessparms { # ---------------------------------------------------------------- Done storing if ($totalstored) { $message.='

' + .&mt('Changes for [quant,_1,parameter] saved.',$totalstored) + .'
' .&mt('Changes can take up to 10 minutes before being active for all students.') .&Apache::loncommon::help_open_topic('Caching') .'

'; + } else { + $message.='

'.&mt('No parameter changes saved.').'

'; + } + if ($totalskippeduser) { + $message .= '

'; + if ($uhome eq 'no_host') { + $message .= &mt('Changes for [quant,_1,user-specific parameter] not saved because the username or ID was invalid.', + $totalskippeduser); + } elsif ($env{'form.userroles'} eq 'any') { + $message .= &mt('Changes for [quant,_1,user-specific parameter] not saved because the user does not have a course role.', + $totalskippeduser); + } else { + $message .= &mt('Changes for [quant,_1,user-specific parameter] not saved because the user is not a student.', + $totalskippeduser); + } + $message .= '

'; } } @@ -3521,13 +3701,20 @@ sub assessparms { # ----- Start Parameter Selection - # Hide parm selection? + # Hide parm selection and possibly table? + my ($tablejs,$tabledivsty); + if (((($env{'form.uname'} ne '') || ($env{'form.id'} ne '')) && ($uname eq '')) && + ($env{'form.dis'}) && ($pssymb eq '')) { + $tablejs = 'document.getElementById('."'parmtable'".').style.display = "";'; + $tabledivsty = ' style="display:none"'; + } $r->print(< // @@ -3554,7 +3741,7 @@ ENDPARMSELSCRIPT $r->print(&Apache::lonhtmlcommon::start_pick_box(undef,'parmlevel')); &levelmenu($r,\%alllevs,$parmlev); $r->print(&Apache::lonhtmlcommon::row_closure()); - &mapmenu($r,\%allmaps,$pschp,\%maptitles, \%symbp); + &mapmenu($r,\%allmaps,$pschp,\%maptitles,\%symbp,$parmlev); $r->print(&Apache::lonhtmlcommon::row_closure()); $r->print(&Apache::lonhtmlcommon::row_title(&mt('Select Parts to View'))); &partmenu($r,\%allparts,\@psprt); @@ -3634,6 +3821,7 @@ ENDPARMSELSCRIPT if ($parm_permission->{'edit'}) { undef($readonly); } + $r->print('
'); if ($parmlev eq 'full') { # @@ -3850,7 +4038,7 @@ ENDTABLEHEADFOUR #-------------------------------------------- for each map, gather information my $mapid; - foreach $mapid (sort {$maplist{$a} cmp $maplist{$b}} keys(%maplist)) { + foreach $mapid (sort { $a <=> $b } keys(%maplist)) { my $maptitle = $maplist{$mapid}; #----------------------- loop through ids and get all parameter types for map @@ -4067,6 +4255,7 @@ ENDMAPONE .'' ); } # end of $parmlev eq general + $r->print('
'); } $r->print(''); if ($numreclinks) { @@ -4604,9 +4793,12 @@ sub parse_listdata_key { # @param {string} $caller - name of the calling sub (overview|newoverview) # @param {hash reference} $classlist - from loncoursedata::get_classlist # @param {boolean} $readonly - true if editing not allowed +# @param {string} $parmlev - full|map +# @param {hash reference} $hash_for_realm - keys: realm, values: numeric order +# @param {string} $pschp - selected map pc, or 'all' # @returns{integer} - number of $listdata parameters processed sub listdata { - my ($r,$resourcedata,$listdata,$sortorder,$caller,$classlist,$readonly)=@_; + my ($r,$resourcedata,$listdata,$sortorder,$caller,$classlist,$readonly,$parmlev,$hash_for_realm,$pschp)=@_; # Start list output @@ -4632,6 +4824,20 @@ sub listdata { foreach my $key (sort { my ($astudent,$ares,$apart,$aparm) = &parse_listdata_key($a,$listdata); my ($bstudent,$bres,$bpart,$bparm) = &parse_listdata_key($b,$listdata); + my ($aid,$bid); + if ($caller eq 'newoverview') { + if (ref($hash_for_realm) eq 'HASH') { + if (($parmlev eq 'map') && ($pschp eq 'all')) { + my ($aurl) = ($ares =~ /^(.+\.(?:sequence|page))___\(all\)$/); + my ($burl) = ($bres =~ /^(.+\.(?:sequence|page))___\(all\)$/); + $aid = $hash_for_realm->{$aurl}; + $bid = $hash_for_realm->{$burl}; + } elsif ($parmlev eq 'full') { + $aid = $hash_for_realm->{$ares}; + $bid = $hash_for_realm->{$bres}; + } + } + } # get the numerical order for the param $aparm=$keyorder{'parameter_0_'.$aparm}; @@ -4641,7 +4847,21 @@ sub listdata { if ($sortorder eq 'realmstudent') { if ($ares ne $bres ) { - $result = ($ares cmp $bres); + if ($caller eq 'newoverview') { + if (ref($hash_for_realm) eq 'HASH') { + if (($parmlev eq 'map') && ($pschp eq 'all')) { + $result = ($aid <=> $bid); + } elsif ($parmlev eq 'full') { + $result = ($aid <=> $bid); + } else { + $result = ($ares cmp $bres); + } + } else { + $result = ($ares cmp $bres); + } + } else { + $result = ($ares cmp $bres); + } } elsif ($astudent ne $bstudent) { $result = ($astudent cmp $bstudent); } elsif ($apart ne $bpart ) { @@ -4651,7 +4871,21 @@ sub listdata { if ($astudent ne $bstudent) { $result = ($astudent cmp $bstudent); } elsif ($ares ne $bres ) { - $result = ($ares cmp $bres); + if ($caller eq 'newoverview') { + if (ref($hash_for_realm) eq 'HASH') { + if (($parmlev eq 'map') && ($pschp eq 'all')) { + $result = ($aid <=> $bid); + } elsif ($parmlev eq 'full') { + $result = ($aid <=> $bid); + } else { + $result = ($ares cmp $bres); + } + } else { + $result = ($ares cmp $bres); + } + } else { + $result = ($ares cmp $bres); + } } elsif ($apart ne $bpart ) { $result = ($apart cmp $bpart); } @@ -4680,6 +4914,7 @@ sub listdata { ($thiskey=~/^$env{'request.course.id'}\.(?:(.+)\.)*([\w\s\-]+)\.(\w+)$/); my $section=&mt('All Students'); $readonly = $readonlyall; + my $userscope; my $showval = $$resourcedata{$thiskey}; if ($middle=~/^\[(.*)\]/) { my $issection=$1; @@ -4693,6 +4928,7 @@ sub listdata { } } $section=&mt('User').": ".&Apache::loncommon::plainname($stuname,$studom); + $userscope = 1; } else { if (($env{'request.course.sec'} ne '') && ($caller eq 'overview')) { if (exists($grouphash{$issection})) { @@ -4729,6 +4965,12 @@ sub listdata { } if ($is_map) { my $leveltitle = &mt('Folder/Map'); + my $title = &Apache::lonnet::gettitle($mapurl); + if (ref($hash_for_realm) eq 'HASH') { + if ($hash_for_realm->{$mapurl} eq '1') { + $title = &mt('Main Content'); + } + } unless (($name eq 'hiddenresource') || ($name eq 'encrypturl')) { if ($caller eq 'newoverview') { my $altkey = $thiskey; @@ -4745,9 +4987,10 @@ sub listdata { $is_recursive = 1; } } - $realm=''.$leveltitle.': '.&Apache::lonnet::gettitle($mapurl).'
('.$mapurl.')
'; + $realm=''.$leveltitle.': '.$title.'
('.$mapurl.')
'; } elsif ($middle) { my ($map,$id,$url)=&Apache::lonnet::decode_symb($middle); + next if (($url =~ /\.(page|sequence)$/) && ($parmlev eq 'full') && ($caller eq 'newoverview')); $realm=''.&mt('Resource'). ': '.&Apache::lonnet::gettitle($middle). '
('.$url.' in '.$map.' id: '. @@ -4790,8 +5033,13 @@ sub listdata { ''.&mt($parmitem). ''); unless ($readonly) { + my $disabled; + if (($name eq 'availablestudent') && + (($showval eq '') || ($userscope))) { + $disabled = ' disabled="disabled"'; + } $r->print(''); + $thiskey.'"'.$disabled.' />'); } $r->print(''); $foundkeys++; @@ -4819,6 +5067,9 @@ sub listdata { $r->print(&date_interval_selector($thiskey,$name, $showval,$readonly)); } elsif ($thistype =~ m/^string/) { + if ($name eq 'availablestudent') { + $readonly = 1; + } $r->print(&string_selector($thistype,$thiskey, $showval,$name,$readonly)); } else { @@ -4861,13 +5112,17 @@ sub listdata { sub get_date_interval_from_form { my ($key) = @_; my $seconds = 0; + my $numnotnull = 0; foreach my $which (['days', 86400], ['hours', 3600], ['minutes', 60], ['seconds', 1]) { my ($name, $factor) = @{ $which }; if (defined($env{'form.'.$name.'_'.$key})) { - $seconds += $env{'form.'.$name.'_'.$key} * $factor; + unless ($env{'form.'.$name.'_'.$key} eq '') { + $numnotnull ++; + $seconds += $env{'form.'.$name.'_'.$key} * $factor; + } } } if (($key =~ /\.interval$/) && @@ -4886,6 +5141,7 @@ sub get_date_interval_from_form { $seconds .= '_'.$env{'form.done_'.$key.'_proctorkey'}; } } + return if (!$numnotnull); return $seconds; } @@ -4973,22 +5229,39 @@ sub string_ip_selector { sub string_deeplink_selector { my ($thiskey, $showval, $readonly) = @_; - my (@components,%values,@current,%titles,%options,%optiontext,%defaults, - %selectnull,%posslti,@possmenus); - @components = ('listing','scope','urls','menus'); + my (@tables,%values,@current,%titles,%options,%optiontext,%defaults, + %selectnull,%domlti,%crslti,@possmenus,%components); + @tables = ('upper','lower'); + %components = ( + upper => ['state','others','listing','scope'], + lower => ['protect','menus','target','exit'], + ); %titles = &Apache::lonlocal::texthash ( + state => 'Access status', + others => 'Hide other resources', listing => 'In Contents and/or Gradebook', scope => 'Access scope for link', - urls => 'Supported link types', + protect => 'Link protection', menus => 'Menu Items Displayed', + target => 'Embedded?', + exit => 'Exit Tool Button?', ); %options = ( + state => ['only','off','both'], + others => ['hide','unhide'], listing => ['full','absent','grades','details','datestatus'], scope => ['res','map','rec'], - urls => ['any','only','key','lti'], + protect => ['none','key','ltid','ltic'], menus => ['std','colls'], + target => ['_self','_top'], + exit => ['no','yes','url'], ); %optiontext = &Apache::lonlocal::texthash ( + only => 'deep only', + off => 'deeplink off', + both => 'regular + deep', + hide => 'Hidden', + unhide => 'Unhidden', full => 'Listed (linked) in both', absent => 'Not listed', grades => 'Listed in grades only', @@ -4997,42 +5270,62 @@ sub string_deeplink_selector { res => 'resource only', map => 'enclosing map/folder', rec => 'recursive map/folder', - any => 'regular + deep', - only => 'deep only', - key => 'deep with key', - lti => 'deep with LTI launch', + none => 'not in use', + key => 'key access', + ltic => 'LTI access (course)', + ltid => 'LTI access (domain)' , std => 'Standard (all menus)', colls => 'Numbered collection', + _self => 'Embedded', + _top => 'Not embedded', + no => 'Not in use', + yes => 'In use, no URL redirect', + url => 'In use, redirect to URL', ); %selectnull = &Apache::lonlocal::texthash ( - lti => 'Select Provider', + ltic => 'Select Launcher', + ltid => 'Select Launcher', colls => 'Select', ); if ($showval =~ /,/) { %values=(); @current = split(/,/,$showval); - ($values{'listing'}) = ($current[0] =~ /^(full|absent|grades|details|datestatus)$/); - ($values{'scope'}) = ($current[1] =~ /^(res|map|rec)$/); - ($values{'urls'}) = ($current[2] =~ /^(any|only|key:[a-zA-Z\d_.!\@#\$%^&*()+=-]+|lti:\d+)$/); - ($values{'menus'}) = ($current[3] =~ /^(\d+)$/); + ($values{'state'}) = ($current[0] =~ /^(only|off|both)$/); + ($values{'others'}) = ($current[1] =~ /^(hide|unhide)$/); + ($values{'listing'}) = ($current[2] =~ /^(full|absent|grades|details|datestatus)$/); + ($values{'scope'}) = ($current[3] =~ /^(res|map|rec)$/); + ($values{'protect'}) = ($current[4] =~ /^(key:[a-zA-Z\d_.!\@#\$%^&*()+=-]+|ltic:\d+|ltid:\d+)$/); + ($values{'menus'}) = ($current[5] =~ /^(\d+)$/); + ($values{'target'}) = ($current[6] =~ /^(_self|_top)$/); + ($values{'exit'}) = ($current[7] =~ /^((?:(?:yes|url)(?:|\:[^:;"',]+))|no)$/); } else { + $defaults{'state'} = 'off', + $defaults{'others'} = 'unhide', $defaults{'listing'} = 'full'; $defaults{'scope'} = 'res'; - $defaults{'urls'} = 'any'; + $defaults{'protect'} = 'none'; $defaults{'menus'} = '0'; + $defaults{'target'} = '_top'; + $defaults{'exit'} = 'yes'; } my $disabled; if ($readonly) { $disabled=' disabled="disabled"'; } - my %lti = + my %courselti = + &Apache::lonnet::get_course_lti($env{'course.'.$env{'request.course.id'}.'.num'}, + $env{'course.'.$env{'request.course.id'}.'.domain'}); + foreach my $item (keys(%courselti)) { + if (ref($courselti{$item}) eq 'HASH') { + $crslti{$item} = $courselti{$item}{'name'}; + } + } + my %lti = &Apache::lonnet::get_domain_lti($env{'course.'.$env{'request.course.id'}.'.domain'}, - 'provider'); + 'linkprot'); foreach my $item (keys(%lti)) { - if (ref($lti{$item}) eq 'HASH') { - unless ($lti{$item}{'requser'}) { - $posslti{$item} = $lti{$item}{'consumer'}; - } + if (($item =~ /^\d+$/) && (ref($lti{$item}) eq 'HASH')) { + $domlti{$item} = $lti{$item}{'name'}; } } if ($env{'course.'.$env{'request.course.id'}.'.menucollections'}) { @@ -5044,111 +5337,144 @@ sub string_deeplink_selector { } } - my $output = ''; - foreach my $item (@components) { - $output .= ''; - } - $output .= ''; - foreach my $item (@components) { - $output .= ''; } - $output .= '
'.$titles{$item}.'
'; - if (($item eq 'urls') || ($item eq 'menus')) { - my $selected = $values{$item}; - foreach my $option (@{$options{$item}}) { - if (($item eq 'urls') && ($option eq 'lti')) { - next unless (keys(%posslti)); - } elsif (($item eq 'menus') && ($option eq 'colls')) { - next unless (@possmenus); - } - my $checked; - if ($item eq 'menus') { - if (($selected =~ /^\d+$/) && (@possmenus) && - (grep(/^\Q$selected\E$/,@possmenus))) { - if ($option eq 'colls') { + my $output = ''; + foreach my $table ('upper','lower') { + next unless (ref($components{$table}) eq 'ARRAY'); + $output .= ''; + foreach my $item (@{$components{$table}}) { + $output .= ''; + } + $output .= ''; + foreach my $item (@{$components{$table}}) { + $output .= ''; + } + $output .= '
'.$titles{$item}.'
'; + if (($item eq 'protect') || ($item eq 'menus') || ($item eq 'exit')) { + my $selected = $values{$item}; + foreach my $option (@{$options{$item}}) { + if ($item eq 'protect') { + if ($option eq 'ltid') { + next unless (keys(%domlti)); + } elsif ($option eq 'ltic') { + next unless (keys(%crslti)); + } + } elsif (($item eq 'menus') && ($option eq 'colls')) { + next unless (@possmenus); + } + my $checked; + if ($item eq 'menus') { + if (($selected =~ /^\d+$/) && (@possmenus) && + (grep(/^\Q$selected\E$/,@possmenus))) { + if ($option eq 'colls') { + $checked = ' checked="checked"'; + } + } elsif (($option eq 'std') && ($selected == 0) && ($selected ne '')) { $checked = ' checked="checked"'; } - } elsif (($option eq 'std') && ($selected == 0) && ($selected ne '')) { + } elsif ($selected =~ /^\Q$option\E/) { $checked = ' checked="checked"'; } - } elsif ($selected =~ /^\Q$option\E/) { - $checked = ' checked="checked"'; - } - my $onclick; - unless ($readonly) { - my $esc_key = &js_escape($thiskey); - $onclick = ' onclick="toggleDeepLink(this.form,'."'$item','$esc_key'".');"'; - } - $output .= ''; - if (($item eq 'urls') && ($option eq 'key')) { - my $visibility="hidden"; - my $currkey; - if ($checked) { - $visibility = "text"; - $currkey = (split(/\:/,$values{$item}))[1]; - } - $output .= ' '. - ''; - } elsif (($option eq 'lti') || ($option eq 'colls')) { - my $display="none"; - my ($current,$blankcheck,@possibles); - if ($checked) { - $display = 'inline-block'; - if ($option eq 'lti') { - $current = (split(/\:/,$selected))[1]; + my $onclick; + unless ($readonly) { + my $esc_key = &js_escape($thiskey); + $onclick = ' onclick="toggleDeepLink(this.form,'."'$item','$esc_key'".');"'; + } + $output .= ''; + if (($item eq 'protect') && ($option eq 'key')) { + my $visibility="hidden"; + my $currkey; + if ($checked) { + $visibility = "text"; + $currkey = (split(/\:/,$values{$item}))[1]; + } + $output .= ' '. + ''; + } elsif (($option eq 'ltic') || ($option eq 'ltid') || ($option eq 'colls')) { + my $display="none"; + my ($current,$blankcheck,@possibles); + if ($checked) { + $display = 'inline-block'; + if (($option eq 'ltic') || ($option eq 'ltid')) { + $current = (split(/\:/,$selected))[1]; + } else { + $current = $selected; + } } else { - $current = $selected; + $blankcheck = ' selected="selected"'; } - } else { - $blankcheck = ' selected="selected"'; - } - if ($option eq 'lti') { - @possibles = keys(%posslti); - } else { - @possibles = @possmenus; - } - $output .= '
 '; + if (@possibles > 1) { + $output .= ''."\n"; + } + foreach my $poss (sort { $a <=> $b } @possibles) { + my $selected; + if (($poss == $current) || (scalar(@possibles) ==1)) { + $selected = ' selected="selected"'; + } + my $shown = $poss; + if ($option eq 'ltid') { + $shown = $domlti{$poss}; + } elsif ($option eq 'ltic') { + $shown = $crslti{$poss}; + } + $output .= ''; + } + $output .= '
'; + } + $output .= '
'; + } + if ($item eq 'exit') { + my $exitsty = 'none'; + my $displayval; + if ($values{$item} =~ /^(yes|url)/) { + $exitsty = 'inline-block'; + my $currval = (split(/\:/,$values{$item}))[1]; + if ($currval eq '') { + $displayval = 'Exit Tool'; + } else { + $displayval = $currval; } - $output .= ''; } - $output .= ''; - } - $output .= '
'; - } - } else { - my $selected = $values{$item}; - my $defsel; - if ($selected eq '') { - $defsel = ' selected="selected"'; - } - $output .= ''; } - $output .= '>'.$optiontext{$option}.''; - } - $output .= ''; + } else { + my $selected = $values{$item}; + my $defsel; + if ($selected eq '') { + $defsel = ' selected="selected"'; + } + $output .= ''; + } + $output .= '
'."\n"; + if ($table eq 'upper') { + $output .= '
'; } - $output .= '
'."\n"; return $output; } @@ -5187,7 +5513,7 @@ my %strings = => [['_allowfrom_','Hostname(s), or IP(s) from which access is allowed'], ['_denyfrom_','Hostname(s) or IP(s) from which access is disallowed']], 'string_deeplink' - => [['on','Set choices for link protection, resource listing, access scope, and shown menu items']], + => [['on','Set choices for link protection, resource listing, access scope, shown menu items, embedding, and exit link']], ); @@ -5198,7 +5524,7 @@ my %stringmatches = ( => [['_allowfrom_','[^\!]+'], ['_denyfrom_','\!']], 'string_deeplink' - => [['on','^(full|absent|grades|details|datestatus)\,(res|map|rec)\,(any|only|key\:\w+|lti\:\d+)\,(\d+|)$']], + => [['on','^(only|off|both)\,(hide|unhide)\,(full|absent|grades|details|datestatus)\,(res|map|rec)\,(none|key\:\w+|ltic\:\d+|ltid\:\d+)\,(\d+|)\,_(self|top),(yes|url|no)(|:[^:;\'",]+)$']], ); my %stringtypes = ( @@ -5518,6 +5844,11 @@ sub date_interval_selector { $showval %= $factor; my %select = ((map {$_ => $_} (0..$max)), 'select_form_order' => [0..$max]); + if ($currval eq '') { + unshift(@{$select{'select_form_order'}},''); + $select{''} = ''; + $amount = ''; + } $result .= &Apache::loncommon::select_form($amount,$name.'_'.$thiskey, \%select,'',$readonly); $result .= ' '.&mt($name); @@ -5525,29 +5856,29 @@ sub date_interval_selector { if ($name eq 'interval') { unless ($skipval{'done'}) { my $checkedon = ''; + my $checkedoff = ''; my $checkedproc = ''; my $currproctorkey = ''; my $currprocdisplay = 'hidden'; my $currdonetext = &mt('Done'); - my $checkedoff = ' checked="checked"'; if ($currval =~ /^(?:\d+)_done$/) { $checkedon = ' checked="checked"'; - $checkedoff = ''; } elsif ($currval =~ /^(?:\d+)_done\:([^\:]+)\:$/) { $currdonetext = $1; $checkedon = ' checked="checked"'; - $checkedoff = ''; } elsif ($currval =~ /^(?:\d+)_done_proctor_(.+)$/) { $currproctorkey = $1; $checkedproc = ' checked="checked"'; - $checkedoff = ''; $currprocdisplay = 'text'; } elsif ($currval =~ /^(?:\d+)_done\:([^\:]+)\:_proctor_(.+)$/) { $currdonetext = $1; $currproctorkey = $2; $checkedproc = ' checked="checked"'; - $checkedoff = ''; $currprocdisplay = 'text'; + } elsif ($currval ne '') { + $checkedoff = ' checked="checked"'; + } else { + $currdonetext = ''; } my $onclick = ' onclick="toggleSecret(this.form,'."'done_','$thiskey'".');"'; my $disabled; @@ -5564,7 +5895,8 @@ sub date_interval_selector { '&').'"'.$disabled.' />

'. ''.&mt('Button text').': '. - '&').'"'.$disabled.' />'; + '&').'"'.$disabled.' />
'; } } unless ($readonly) { @@ -5805,7 +6137,7 @@ ENDOVER &extractResourceInformation(\@ids, \%typep,\%keyp, \%allparms, \%allparts, \%allmaps, \%mapp, \%symbp,\%maptitles,\%uris, - \%keyorder,\%defkeytype); + \%keyorder,\%defkeytype,$pssymb); if (grep {$_ eq 'all'} (@psprt)) { @psprt = keys(%allparts); @@ -5817,10 +6149,8 @@ ENDOVER $r->print('
'); $r->print(&Apache::lonhtmlcommon::start_pick_box(undef,'parmlevel')); &levelmenu($r,\%alllevs,$parmlev); - if ($parmlev ne 'general') { - $r->print(&Apache::lonhtmlcommon::row_closure()); - &mapmenu($r,\%allmaps,$pschp,\%maptitles,\%symbp); - } + $r->print(&Apache::lonhtmlcommon::row_closure()); + &mapmenu($r,\%allmaps,$pschp,\%maptitles,\%symbp,$parmlev); $r->print(&Apache::lonhtmlcommon::row_closure(1)); $r->print(&Apache::lonhtmlcommon::end_pick_box()); $r->print('
'); @@ -5858,7 +6188,7 @@ ENDOVER $r->print('
'); my $sortorder=$env{'form.sortorder'}; unless ($sortorder) { $sortorder='realmstudent'; } - &sortmenu($r,$sortorder); + &sortmenu($r,$sortorder,'newoverview'); $r->print('
'); $r->print('

'); @@ -5883,7 +6213,15 @@ ENDOVER # List data - &listdata($r,$resourcedata,$listdata,$sortorder,'newoverview',undef,$readonly); + my $hash_for_realm; + if (($parmlev eq 'map') && (keys(%allmaps))) { + %{$hash_for_realm} = reverse(%allmaps); + } elsif (($parmlev eq 'full') && (keys(%symbp))) { + for (my $i=0; $i<@ids; $i++) { + $hash_for_realm->{$symbp{$ids[$i]}} = $i; + } + } + &listdata($r,$resourcedata,$listdata,$sortorder,'newoverview',undef,$readonly,$parmlev,$hash_for_realm,$pschp); } $r->print(&tableend()); unless ($readonly) { @@ -5990,7 +6328,7 @@ sub overview { my $sortorder=$env{'form.sortorder'}; unless ($sortorder) { $sortorder='realmstudent'; } - &sortmenu($r,$sortorder); + &sortmenu($r,$sortorder,'overview'); my $submitbutton = '';