--- loncom/interface/lonparmset.pm 2016/07/15 22:24:37 1.562 +++ 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.562 2016/07/15 22:24:37 damieng Exp $ +# $Id: lonparmset.pm,v 1.618 2023/04/03 15:39:10 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -36,7 +36,8 @@ lonparmset - Handler to set parameters f =head1 SYNOPSIS -lonparmset provides an interface to setting course parameters. +lonparmset provides an interface to setting content parameters in a +course. It contains all the code for the "Content and Problem Settings" UI, except for the helpers parameter.helper and resettimes.helper, and lonhelper.pm, @@ -137,7 +138,7 @@ javascript function 'pjump'. =item print_td() -=item print_usergroups() +=item check_other_groups() =item parm_control_group() @@ -328,6 +329,7 @@ use Apache::lonnavmaps; use Apache::longroup; use Apache::lonrss; use HTML::Entities; +use Text::Wrap(); use LONCAPA qw(:DEFAULT :match); @@ -367,14 +369,14 @@ sub endSettingsScreen { ################################################## -# TABLE MODE +# (mostly) TABLE MODE # (parmval is also used for the log of parameter changes) ################################################## -# Calls parmval_by_symb, getting the symb from $id (the big hash resource id) with &symbcache. +# Calls parmval_by_symb, getting the symb from $id with &symbcache. # # @param {string} $what - part info and parameter name separated by a dot, e.g. '0.weight' -# @param {string} $id - big hash resource id +# @param {string} $id - resource id or map pc # @param {string} $def - the resource's default value for this parameter # @param {string} $uname - user name # @param {string} $udom - user domain @@ -394,7 +396,7 @@ sub parmval { # (level 1 is the most specific and will have precedence) # # @param {string} $what - part info and parameter name separated by a dot, e.g. '0.weight' -# @param {string} $symb - resource symb +# @param {string} $symb - resource symb or map src # @param {string} $def - the resource's default value for this parameter # @param {string} $uname - user name # @param {string} $udom - user domain @@ -597,7 +599,7 @@ sub reset_caches { } } -# cache big hash id -> symb, using lonnavmaps to find association +# cache resource id or map pc -> resource symb or map src, using lonnavmaps to find association { my $symbsid; # course identifier, to initialize the cache only once for a course my %symbs; # hash id->symb @@ -607,7 +609,8 @@ sub reset_caches { undef(%symbs); } - # returns the symb corresponding to a big hash id (using lonnavmaps and a cache) + # returns the resource symb or map src corresponding to a resource id or map pc + # (using lonnavmaps and a cache) sub symbcache { my $id=shift; if ($symbsid ne $env{'request.course.id'}) { @@ -718,8 +721,8 @@ sub date_sanity_info { # Store a parameter value and type by ID, also triggering more parameter changes based on parameter default actions. # -# @param {string} $sresid - resource big hash id -# @param {string} $spnam - part info and parameter name separated by a dot, e.g. '0.weight' +# @param {string} $sresid - resource id or map pc +# @param {string} $spnam - part info and parameter name separated by a dot or underscore, e.g. '0.weight' # @param {integer} $snum - level # @param {string} $nval - new value # @param {string} $ntype - new type @@ -737,8 +740,8 @@ my %recstack; # hash parameter name -> 1 # Store a parameter value and type by symb, also triggering more parameter changes based on parameter default actions. # Uses storeparm_by_symb_inner to actually store the parameter, ignoring any returned error. # -# @param {string} $symb - resource symb -# @param {string} $spnam - part info and parameter name separated by a dot, e.g. '0.weight' +# @param {string} $symb - resource symb or map src +# @param {string} $spnam - part info and parameter name separated by a dot or underscore, e.g. '0.weight' # @param {integer} $snum - level # @param {string} $nval - new value # @param {string} $ntype - new type @@ -776,8 +779,7 @@ sub storeparm_by_symb { # are there restrictions? if (&rulescache($triggered.'_triggervalue')=~/\w/) { $active=0; - foreach my $possiblevalue (split(/\s*\, - \s*/,&rulescache($triggered.'_triggervalue'))) { + foreach my $possiblevalue (split(/\s*\,\s*/,&rulescache($triggered.'_triggervalue'))) { if (lc($possiblevalue) eq lc($nval)) { $active=1; } } } @@ -808,7 +810,7 @@ sub log_parmset { # Store a parameter value and type by symb, without using the parameter default actions. # Expire related sheets. # -# @param {string} $symb - resource symb +# @param {string} $symb - resource symb or map src # @param {string} $spnam - part info and parameter name separated by a dot, e.g. '0.weight' # @param {integer} $snum - level # @param {string} $nval - new value @@ -846,17 +848,36 @@ sub storeparm_by_symb_inner { my $courselevelm=$env{'request.course.id'}.'.'.$mapparm; my $storeunder=''; + my $possreplace=''; if (($snum==18) || ($snum==4)) { $storeunder=$courselevel; } - if (($snum==17) || ($snum==3)) { $storeunder=$courseleveli; } - if (($snum==16) || ($snum==2)) { $storeunder=$courselevelm; } + if (($snum==17) || ($snum==3)) { + $storeunder=$courseleveli; + $possreplace=$courselevelm; + } + if (($snum==16) || ($snum==2)) { + $storeunder=$courselevelm; + $possreplace=$courseleveli; + } if (($snum==13) || ($snum==1)) { $storeunder=$courselevelr; } if ($snum==12) { $storeunder=$seclevel; } - if ($snum==11) { $storeunder=$secleveli; } - if ($snum==10) { $storeunder=$seclevelm; } + if ($snum==11) { + $storeunder=$secleveli; + $possreplace=$seclevelm; + } + if ($snum==10) { + $storeunder=$seclevelm; + $possreplace=$secleveli; + } if ($snum==9) { $storeunder=$seclevelr; } if ($snum==8) { $storeunder=$grplevel; } - if ($snum==7) { $storeunder=$grpleveli; } - if ($snum==6) { $storeunder=$grplevelm; } + if ($snum==7) { + $storeunder=$grpleveli; + $possreplace=$grplevelm; + } + if ($snum==6) { + $storeunder=$grplevelm; + $possreplace=$grpleveli; + } if ($snum==5) { $storeunder=$grplevelr; } @@ -875,7 +896,7 @@ sub storeparm_by_symb_inner { &Apache::lonnet::expirespread('','','studentcalc'); if (($snum==13) || ($snum==9) || ($snum==5)) { &Apache::lonnet::expirespread('','','assesscalc',$symb); - } elsif (($snum==14) || ($snum==10) || ($snum==6)) { + } elsif (($snum==17) || ($snum==16) || ($snum==11) || ($snum==10) || ($snum==7) || ($snum==6)) { &Apache::lonnet::expirespread('','','assesscalc',$map); } else { &Apache::lonnet::expirespread('','','assesscalc'); @@ -889,6 +910,17 @@ sub storeparm_by_symb_inner { $reply=&Apache::lonnet::cput ('resourcedata',\%storecontent,$cdom,$cnum); &log_parmset(\%storecontent); + if ($possreplace) { + my $resdata = &Apache::lonnet::get_courseresdata($cnum,$cdom); + if (ref($resdata) eq 'HASH') { + if (exists($resdata->{$possreplace})) { + if (&Apache::lonnet::del + ('resourcedata',[$possreplace,$possreplace.'.type'],$cdom,$cnum) eq 'ok') { + &log_parmset({$possreplace => '', $possreplace.'.type' => $ntype},1); + } + } + } + } } &Apache::lonnet::devalidatecourseresdata($cnum,$cdom); } else { @@ -899,7 +931,7 @@ sub storeparm_by_symb_inner { if ($snum==1) { &Apache::lonnet::expirespread ($uname,$udom,'assesscalc',$symb); - } elsif ($snum==2) { + } elsif (($snum==2) || ($snum==3)) { &Apache::lonnet::expirespread ($uname,$udom,'assesscalc',$map); } else { @@ -914,6 +946,18 @@ sub storeparm_by_symb_inner { $reply=&Apache::lonnet::cput ('resourcedata',\%storecontent,$udom,$uname); &log_parmset(\%storecontent,0,$uname,$udom); + if ($possreplace) { + my $resdata = &Apache::lonnet::get_userresdata($uname,$udom); + if (ref($resdata) eq 'HASH') { + if (exists($resdata->{$possreplace})) { + if (&Apache::lonnet::del + ('resourcedata',[$possreplace,$possreplace.'.type'],$udom,$uname) eq 'ok') { + &log_parmset({$possreplace => '',$possreplace.'.type' => $ntype},1, + $uname,$udom); + } + } + } + } } &Apache::lonnet::devalidateuserresdata($uname,$udom); } @@ -933,10 +977,9 @@ sub storeparm_by_symb_inner { # # @param {string} $value - the parameter value # @param {string} $type - the parameter type -# @param {string} $name - the parameter name (unused) # @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') { @@ -1022,12 +1065,18 @@ sub valout { # @param {string} $marker - identifier for the parameter, "resource id&part_parameter name&level", will be passed as pres_marker when the user submits a change. # @param {string} $return - prefix for the name of the form and field names that will be used to submit the form ('parmform.pres') # @param {string} $call - javascript function to call to submit the form ('psub') +# @param {boolean} $recursive - true if link is for a map/folder where parameter is currently set to be recursive. +# @param {string} $extra - optional additional information to send as tenth arg in call to javascript pjump function. sub plink { - my ($type,$dis,$value,$marker,$return,$call)=@_; + my ($type,$dis,$value,$marker,$return,$call,$recursive,$extra)=@_; my $winvalue=$value; unless ($winvalue) { - if (&isdateparm($type)) { + if (&isdateparm($type) || (&is_specialstring($type))) { $winvalue=$env{'form.recent_'.$type}; + } elsif ($type eq 'string_yesno') { + if ($env{'form.recent_string'} =~ /^(yes|no)$/i) { + $winvalue=$env{'form.recent_string'}; + } } else { $winvalue=$env{'form.recent_'.(split(/\_/,$type))[0]}; } @@ -1035,17 +1084,19 @@ sub plink { my ($parmname)=((split(/\&/,$marker))[1]=~/\_([^\_]+)$/); my ($hour,$min,$sec,$val)=&preset_defaults($parmname); unless (defined($winvalue)) { $winvalue=$val; } - my $valout = &valout($value,$type,$parmname,1); + my $valout = &valout($value,$type,1); my $unencmarker = $marker; foreach my $item (\$type, \$dis, \$winvalue, \$marker, \$return, \$call, - \$hour, \$min, \$sec) { + \$hour, \$min, \$sec, \$extra) { $$item = &HTML::Entities::encode($$item,'"<>&'); $$item =~ s/\'/\\\'/g; } return '
'. ''. - $valout.'
'; + .$marker."','".$return."','".$call."','".$hour."','".$min."','".$sec."','".$extra."'".');">'. + $valout.''.($recursive?''. + &mt('recursive').'' : '').''; + } # Javascript for table mode. @@ -1061,12 +1112,14 @@ sub page_js { $pjump_def function psub() { + var specstring = /^string_!(yesno|any)/i; if (document.parmform.pres_marker.value!='') { document.parmform.action+='#'+document.parmform.pres_marker.value; var typedef=new Array(); typedef=document.parmform.pres_type.value.split('_'); if (document.parmform.pres_type.value!='') { - if (typedef[0]=='date') { + if ((typedef[0]=='date') || + (specstring.test(document.parmform.pres_type.value))) { eval('document.parmform.recent_'+ document.parmform.pres_type.value+ '.value=document.parmform.pres_value.value;'); @@ -1189,14 +1242,27 @@ function validateParms() { var tailLenient = /\.lenient$/; var patternRelWeight = /^\-?[\d.]+$/; var patternLenientStd = /^(yes|no|default)$/; + var ipRegExp = /^setip/; var ipallowRegExp = /^setipallow_/; var ipdenyRegExp = /^setipdeny_/; + var deeplinkRegExp = /^deeplink_/; + 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 possdeeplink = document.parmform.elements[i].options[idx].value + possdeeplink = possdeeplink.replace(/^\s+|\s+$/g,''); + if (document.parmform.elements['set_'+identifier].value) { + possdeeplink = ','+possdeeplink; + } + document.parmform.elements['set_'+identifier].value += possdeeplink; + } + } else if (dlLinkProtectRegExp.test(name)) { + if (document.parmform.elements[i].checked) { + var identifier = name.replace(dlLinkProtectRegExp,''); + var posslinkurl = document.parmform.elements[i].value; + posslinkurl = posslinkurl.replace(/^\s+|\s+$/g,''); + if (document.parmform.elements['set_'+identifier].value) { + posslinkurl = ','+posslinkurl; + } + document.parmform.elements['set_'+identifier].value += posslinkurl; + } + } else if (dlLtidRegExp.test(name)) { + var identifier = name.replace(dlLtidRegExp,''); + if (isRadioSet('deeplink_protect_'+identifier,'ltid')) { + var possltid = document.parmform.elements[i].value; + possltid = possltid.replace(/\D+/g,''); + if (possltid.length) { if (document.parmform.elements['set_'+identifier].value) { - possdeny = ','+possdeny; + possltid = ':'+possltid; } - document.parmform.elements['set_'+identifier].value += possdeny; + document.parmform.elements['set_'+identifier].value += possltid; + } else { + document.parmform.elements['set_'+identifier].value = ''; + alert("A link type of 'domain LTI launch' was selected but no domain LTI launcher was selected.\nPlease select one, or choose a different supported link type."); + return false; + } + } + } else if (dlLticRegExp.test(name)) { + var identifier = name.replace(dlLticRegExp,''); + if (isRadioSet('deeplink_protect_'+identifier,'ltic')) { + var possltic = document.parmform.elements[i].value; + possltic = possltic.replace(/\D+/g,''); + if (possltic.length) { + if (document.parmform.elements['set_'+identifier].value) { + possltic = ':'+possltic; + } + document.parmform.elements['set_'+identifier].value += possltic; + } else { + document.parmform.elements['set_'+identifier].value = ''; + alert("A link type of 'course LTI launch' was selected but no course LTI launcher was selected.\nPlease select one, or choose a different supported link type."); + return false; + } + } + } else if (dlKeyRegExp.test(name)) { + var identifier = name.replace(dlKeyRegExp,''); + if (isRadioSet('deeplink_protect_'+identifier,'key')) { + var posskey = document.parmform.elements[i].value; + posskey = posskey.replace(/^\s+|\s+$/g,''); + var origlength = posskey.length; + posskey = posskey.replace(/[^a-zA-Z\d_.!@#$%^&*()+=-]/g,''); + var newlength = posskey.length; + if (newlength > 0) { + var change = origlength - newlength; + if (change) { + alert(change+' disallowed character(s) removed from deeplink key'); + } + if (document.parmform.elements['set_'+identifier].value) { + posskey = ':'+posskey; + } + document.parmform.elements['set_'+identifier].value += posskey; + } else { + document.parmform.elements['set_'+identifier].value = ''; + if (newlength < origlength) { + alert("A link type of 'deep with key' was selected but the key value was blank, after removing disallowed characters.\nPlease enter a key using one or more of: a-zA-Z0-9_.!@#$%^&*()+=-"); + } else { + alert("A link type of 'deep with key' was selected but the key value was blank.\nPlease enter a key."); + } + return false; + } + } + } else if (dlMenusRegExp.test(name)) { + if (document.parmform.elements[i].checked) { + var identifier = name.replace(dlMenusRegExp,''); + var posslinkmenu = document.parmform.elements[i].value; + posslinkmenu = posslinkmenu.replace(/^\s+|\s+$/g,''); + if (posslinkmenu == 'std') { + posslinkmenu = '0'; + if (document.parmform.elements['set_'+identifier].value) { + posslinkmenu = ','+posslinkmenu; + } + document.parmform.elements['set_'+identifier].value += posslinkmenu; + } + } + } else if (dlCollsRegExp.test(name)) { + var identifier = name.replace(dlCollsRegExp,''); + if (isRadioSet('deeplink_menus_'+identifier,'colls')) { + var posslinkmenu = document.parmform.elements[i].value; + if (document.parmform.elements['set_'+identifier].value) { + posslinkmenu = ','+posslinkmenu; + } + document.parmform.elements['set_'+identifier].value += posslinkmenu; + } + } else if (dlTargetRegExp.test(name)) { + var identifier = name.replace(dlTargetRegExp,''); + var idx = document.parmform.elements[i].selectedIndex; + if (idx > 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; } } } @@ -1251,6 +1468,23 @@ function validateParms() { return true; } +function isRadioSet(name,expected) { + var menuitems = document.getElementsByName(name); + var radioLength = menuitems.length; + result = false; + if (radioLength > 1) { + for (var j=0; j parameter part (can be problem part.'_'.response id for response parameters) # @param {hash reference} $name - parameter key -> parameter name -# @param {hash reference} $symbp - resource id -> symb +# @param {hash reference} $symbp - map pc or resource/map id -> map src.'___(all)' or resource symb # @param {string} $rid - resource id # @param {hash reference} $default - parameter key -> resource parameter default value # @param {hash reference} $defaulttype - parameter key -> resource parameter default type @@ -1380,12 +1706,19 @@ ENDHEAD # @param {string} $cgroup - group name # @param {array reference} $usersgroups - list of groups the user belongs to, if any # @param {boolean} $noeditgrp - true if no edit is allowed for group level parameters +# @param {boolean} $readonly - true if no editing allowed. +# @param {array reference} - $recurseup - list of maps containing current one, ending at top-level. +# @param {hash reference} - $maptitles - - hash map id or src -> map title +# @param {hash reference} - $allmaps_inverted - hash map src -> map pc +# @param {scalar reference} - $reclinks - number of "parameter in effect" cells with link to map where recursive param was set sub print_row { my ($r,$which,$part,$name,$symbp,$rid,$default,$defaulttype,$display,$defbgone, - $defbgtwo,$defbgthree,$parmlev,$uname,$udom,$csec,$cgroup,$usersgroups,$noeditgrp)=@_; + $defbgtwo,$defbgthree,$parmlev,$uname,$udom,$csec,$cgroup,$usersgroups,$noeditgrp, + $readonly,$recurseup,$maptitles,$allmaps_inverted,$reclinks)=@_; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $courseopt=&Apache::lonnet::get_courseresdata($cnum,$cdom); + my $numlinks = 0; # get the values for the parameter in cascading order # empty levels will remain empty @@ -1422,85 +1755,209 @@ sub print_row { my $thismarker=$which; $thismarker=~s/^parameter\_//; my $mprefix=$rid.'&'.$thismarker.'&'; - my $effective_parm = &valout($outpar[$result],$typeoutpar[$result],$thismarker); - my ($othergrp,$grp_parm,$controlgrp); - + my ($parmname)=($thismarker=~/\_([^\_]+)$/); + my ($othergrp,$grp_parm,$controlgrp,$effective_parm,$effparm_rec,$effparm_level, + $eff_groupparm,$recurse_check,$recursinfo,$extra); + if ((ref($recurseup) eq 'ARRAY') && (@{$recurseup} > 0)) { + if ($result eq '') { + $recurse_check = 1; + } elsif (($uname ne '') && ($result > 3)) { + $recurse_check = 1; + } elsif (($cgroup ne '') && ($result > 7)) { + $recurse_check = 1; + } elsif (($csec ne '') && ($result > 11)) { + $recurse_check = 1; + } elsif ($result > 17) { + $recurse_check = 1; + } + if ($recurse_check) { + my $what = $$part{$which}.'.'.$$name{$which}; + my $prefix; + if (($uname ne '') && ($udom ne '')) { + my $useropt = &Apache::lonnet::get_userresdata($uname,$udom); + $prefix = $env{'request.course.id'}; + $recursinfo = &get_recursive($recurseup,$useropt,$what,$prefix); + if (ref($recursinfo) eq 'ARRAY') { + $effparm_rec = 1; + $effparm_level = &mt('user: [_1]',$uname); + } + } + if (($cgroup ne '') && (!$effparm_rec)) { + $prefix = $env{'request.course.id'}.'.['.$cgroup.']'; + $recursinfo = &get_recursive($recurseup,$courseopt,$what,$prefix); + if (ref($recursinfo) eq 'ARRAY') { + $effparm_rec = 1; + $effparm_level = &mt('group: [_1]',$cgroup); + } + } + if (($csec ne '') && (!$effparm_rec)) { + $prefix = $env{'request.course.id'}.'.['.$csec.']'; + $recursinfo = &get_recursive($recurseup,$courseopt,$what,$prefix); + if (ref($recursinfo) eq 'ARRAY') { + $effparm_rec = 1; + $effparm_level = &mt('section: [_1]',$csec); + } + } + if (!$effparm_rec) { + $prefix = $env{'request.course.id'}; + $recursinfo = &get_recursive($recurseup,$courseopt,$what,$prefix); + if (ref($recursinfo) eq 'ARRAY') { + $effparm_rec = 1; + } + } + } + } + if ((!$effparm_rec) && ($result == 17 || $result == 11 || $result == 7 || $result == 3)) { + $effparm_rec = 1; + } + if ((!$effparm_rec) && + (($$name{$which} eq 'encrypturl') || ($$name{$which} eq 'hiddenresource')) && + ($result == 16 || $result == 10 || $result == 6 || $result == 2)) { + $effparm_rec = 1; + } + if ($parmname eq 'deeplink') { + my ($domltistr,$crsltistr); + my %lti = + &Apache::lonnet::get_domain_lti($env{'course.'.$env{'request.course.id'}.'.domain'}, + 'linkprot'); + if (keys(%lti)) { + foreach my $item (sort { $a <=> $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; + } + } + 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; + } + } + if ($env{'course.'.$env{'request.course.id'}.'.menucollections'}) { + my @colls; + foreach my $item (split(/;/,$env{'course.'.$env{'request.course.id'}.'.menucollections'})) { + my ($num,$value) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + push(@colls,$num); + } + } + if (@colls) { + if ($extra) { + $extra .= '&'; + } + $extra .= 'menus_'.join(',',@colls); + } + } + } if ($parmlev eq 'general') { if ($uname) { - &print_td($r,4,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); + &print_td($r,4,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); } elsif ($cgroup) { - &print_td($r,8,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp); + &print_td($r,8,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp,$readonly,'',$extra); } elsif ($csec) { - &print_td($r,12,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); + &print_td($r,12,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); } else { - &print_td($r,18,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); + &print_td($r,18,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); } } elsif ($parmlev eq 'map') { if ($uname) { - &print_td($r,3,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,2,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); + &print_td($r,2,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,1,$extra); } elsif ($cgroup) { - &print_td($r,7,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp); - &print_td($r,6,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp); + &print_td($r,6,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp,$readonly,1,$extra); } elsif ($csec) { - &print_td($r,11,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,10,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); + &print_td($r,10,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,1,$extra); } else { - &print_td($r,17,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,16,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); + &print_td($r,16,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,1,$extra); } } else { if ($uname) { if (@{$usersgroups} > 1) { - my ($coursereply,$grp_parm,$controlgrp); - ($coursereply,$othergrp,$grp_parm,$controlgrp) = - &print_usergroups($r,$$part{$which}.'.'.$$name{$which}, + (my $coursereply,$othergrp,$grp_parm,$controlgrp,my $grp_is_rec) = + &check_other_groups($$part{$which}.'.'.$$name{$which}, $rid,$cgroup,$defbgone,$usersgroups,$result,$courseopt); - if ($coursereply && $result > 4) { + if (($coursereply) && ($result > 4)) { if (defined($controlgrp)) { if ($cgroup ne $controlgrp) { - $effective_parm = $grp_parm; - $result = 0; + $eff_groupparm = $grp_parm; + undef($result); + undef($effparm_rec); + if ($grp_is_rec) { + $effparm_rec = 1; + } } } } } } - &print_td($r,18,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,17,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,16,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,15,'#FFDDDD',$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,14,'#FFDDDD',$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,13,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); + &print_td($r,18,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); + &print_td($r,16,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,1,$extra); + &print_td($r,15,'#FFDDDD',$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); + &print_td($r,14,'#FFDDDD',$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); + &print_td($r,13,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); if ($csec) { - &print_td($r,12,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,11,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,10,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,9,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); + &print_td($r,12,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); + &print_td($r,10,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,1,$extra); + &print_td($r,9,$defbgtwo,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); } if ($cgroup) { - &print_td($r,8,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp); - &print_td($r,7,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp); - &print_td($r,6,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp); - &print_td($r,5,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp); + &print_td($r,8,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp,$readonly,'',$extra); + &print_td($r,6,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp,$readonly,1,$extra); + &print_td($r,5,$defbgthree,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,$noeditgrp.$readonly,'',$extra); } if ($uname) { if ($othergrp) { $r->print($othergrp); } - &print_td($r,4,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,3,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,2,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); - &print_td($r,1,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display); + &print_td($r,4,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); + &print_td($r,2,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,1,$extra); + &print_td($r,1,$defbgone,$result,\@outpar,$mprefix,$which,\@typeoutpar,$display,'',$readonly,'',$extra); } - } # end of $parmlev if/else - $r->print(''.$effective_parm.''); - + if (ref($recursinfo) eq 'ARRAY') { + my $rectitle = &mt('recursive'); + if ((ref($maptitles) eq 'HASH') && (exists($maptitles->{$recursinfo->[2]}))) { + if ((ref($allmaps_inverted) eq 'HASH') && (exists($allmaps_inverted->{$recursinfo->[2]}))) { + $rectitle = &mt('set in: [_1]','"'. + '{$recursinfo->[2]}."',". + "'$parmname','$$part{$which}'".');">'. + $maptitles->{$recursinfo->[2]}.'"'); + + $numlinks ++; + } + } + my ($parmname)=($thismarker=~/\_([^\_]+)$/); + $effective_parm = &valout($recursinfo->[0],$recursinfo->[1]); + $r->print(''.$effective_parm. + '
'.$rectitle.' '. + $effparm_level.''); + } else { + if ($result) { + $effective_parm = &valout($outpar[$result],$typeoutpar[$result]); + } + if ($eff_groupparm) { + $effective_parm = $eff_groupparm; + } + $r->print(''.$effective_parm. + ($effparm_rec?'
'.&mt('recursive'). + '':'').''); + } if ($parmlev eq 'full') { my $sessionval=&Apache::lonnet::EXT('resource.'.$$part{$which}. '.'.$$name{$which},$$symbp{$rid}); @@ -1509,11 +1966,14 @@ sub print_row { $sessionvaltype=$$defaulttype{$which}; } $r->print(''. - &valout($sessionval,$sessionvaltype,$$name{$which}).' '. + &valout($sessionval,$sessionvaltype).' '. ''); } $r->print(''); $r->print("\n"); + if (($numlinks) && (ref($reclinks))) { + $$reclinks = $numlinks; + } } # Prints a cell for table mode. @@ -1532,44 +1992,72 @@ sub print_row { # @param {array reference} $typeoutpar - array level -> parameter type (when defined) # @param {hash reference} $display - parameter key -> full title for the parameter # @param {boolean} $noeditgrp - true if no edit is allowed for group level parameters +# @param {boolean} $readonly -true if editing not allowed. +# @param {boolean} $ismaplevel - true if level is for a map. +# @param {string} $extra - extra information to pass to plink. sub print_td { - my ($r,$which,$defbg,$result,$outpar,$mprefix,$value,$typeoutpar,$display,$noeditgrp)=@_; - $r->print(''); my $nolink = 0; - if ($which == 14 || $which == 15) { - $nolink = 1; - } elsif (($env{'request.course.sec'} ne '') && ($which > 12)) { + if ($readonly) { $nolink = 1; - } elsif ($which == 5 || $which == 6 || $which == 7 || $which == 8) { - if ($noeditgrp) { + } else { + if ($which == 14 || $which == 15 || $mprefix =~ /mapalias\&$/) { $nolink = 1; - } - } elsif ($mprefix =~ /availablestudent\&$/) { - if ($which > 4) { + } elsif (($env{'request.course.sec'} ne '') && ($which > 12)) { $nolink = 1; - } - } elsif ($mprefix =~ /examcode\&$/) { - unless ($which == 2) { + } elsif ($which == 5 || $which == 6 || $which == 7 || $which == 8) { + if ($noeditgrp) { + $nolink = 1; + } + } elsif ($mprefix =~ /availablestudent\&$/) { $nolink = 1; + } elsif ($mprefix =~ /examcode\&$/) { + unless ($which == 2) { + $nolink = 1; + } } } if ($nolink) { - $r->print(&valout($$outpar[$which],$$typeoutpar[$which],$mprefix)); -# FIXME: probably a good thing that mprefix is not used in valout, because it does not look like a parameter name ! + my ($parmname)=((split(/\&/,$mprefix))[1]=~/\_([^\_]+)$/); + $r->print(&valout($currval,$currtype)); } else { - $r->print(&plink($$typeoutpar[$which], - $$display{$value},$$outpar[$which], - $mprefix."$which",'parmform.pres','psub')); + $r->print(&plink($currtype, + $$display{$value},$currval, + $mprefix.$currlevel,'parmform.pres','psub',$recursive, + $extra)); } $r->print(''."\n"); } -# FIXME: Despite the name, this does not print anything, the $r parameter is unused. # Returns HTML and other info for the cell added when a user is selected # and that user is in several groups. This is the cell with the title "Control by other group". # -# @param {Apache2::RequestRec} $r - the Apache request (unused) # @param {string} $what - parameter part.'.'.parameter name # @param {string} $rid - resource id # @param {string} $cgroup - group name @@ -1577,9 +2065,9 @@ sub print_td { # @param {array reference} $usersgroups - list of groups the user belongs to, if any # @param {integer} $result - level # @param {hash reference} $courseopt - course parameters hash (result of lonnet::get_courseresdata, dump of course's resourcedata.db) -# @returns {Array} - array (parameter value for the other group, HTML for the cell, HTML with the value, name of the other group) -sub print_usergroups { - my ($r,$what,$rid,$cgroup,$defbg,$usersgroups,$result,$courseopt) = @_; +# @returns {Array} - array (parameter value for the other group, HTML for the cell, HTML with the value, name of the other group, true if recursive) +sub check_other_groups { + my ($what,$rid,$cgroup,$defbg,$usersgroups,$result,$courseopt) = @_; my $courseid = $env{'request.course.id'}; my $output; my $symb = &symbcache($rid); @@ -1591,16 +2079,22 @@ sub print_usergroups { &parm_control_group($courseid,$usersgroups,$symbparm,$mapparm, $recurseparm,$what,$courseopt); my $bgcolor = $defbg; - my $grp_parm; + my ($grp_parm,$grp_is_rec); if (($coursereply) && ($cgroup ne $resultgroup)) { + my ($parmname) = ($what =~ /\.([^.]+)$/); if ($result > 3) { $bgcolor = '#AAFFAA'; - $grp_parm = &valout($coursereply,$resulttype,$what); } - $grp_parm = &valout($coursereply,$resulttype,$what); + $grp_parm = &valout($coursereply,$resulttype); $output = ''; if ($resultgroup && $resultlevel) { - $output .= ''.$resultgroup.' ('.$resultlevel.'): '.$grp_parm; + if ($resultlevel eq 'recursive') { + $resultlevel = 'map/folder'; + $grp_is_rec = 1; + } + $output .= ''.$resultgroup.' ('.$resultlevel.'): '.$grp_parm. + ($grp_is_rec?''.&mt('recursive').'':''); + } else { $output .= ' '; } @@ -1608,11 +2102,11 @@ sub print_usergroups { } else { $output .= ' '; } - return ($coursereply,$output,$grp_parm,$resultgroup); + return ($coursereply,$output,$grp_parm,$resultgroup,$grp_is_rec); } # Looks for a group with a defined parameter for given user and parameter. -# Used by print_usergroups. +# Used by check_other_groups. # # @param {string} $courseid - the course id # @param {array reference} $usersgroups - list of groups the user belongs to, if any @@ -1651,18 +2145,19 @@ sub parm_control_group { # Extracts lots of information about all of the the course's resources into a variety of hashes, using lonnavmaps and lonnet::metadata. # All the parameters are references and are filled by the sub. # -# @param {array reference} $ids - resource ids -# @param {hash reference} $typep - hash resource id (from big hash) -> resource type (file extension) -# @param {hash reference} $keyp - hash resource id -> comma-separated list of parameter keys from lonnet::metadata +# @param {array reference} $ids - resource and map ids +# @param {hash reference} $typep - hash resource/map id -> resource type (file extension) +# @param {hash reference} $keyp - hash resource/map id -> comma-separated list of parameter keys from lonnet::metadata # @param {hash reference} $allparms - hash parameter name -> parameter title # @param {hash reference} $allparts - hash parameter part -> part title (a parameter part can be problem part.'_'.response id for response parameters) -# @param {hash reference} $allmaps - hash map id (from big hash) -> map src -# @param {hash reference} $mapp - hash resource id -> enclosing map src -# @param {hash reference} $symbp - hash map id or resource id -> map src.'___(all)' for a map or resource symb for a resource -# @param {hash reference} $maptitles - hash map id or src -> map title (this should really be two separate hashes) -# @param {hash reference} $uris - hash resource id -> resource src +# @param {hash reference} $allmaps - hash map pc -> map src +# @param {hash reference} $mapp - hash map pc or resource/map id -> enclosing map src +# @param {hash reference} $symbp - hash map pc or resource/map id -> map src.'___(all)' for a map or resource symb for a resource +# @param {hash reference} $maptitles - hash map pc or src -> map title (this should really be two separate hashes) +# @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; @@ -1676,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); @@ -1689,23 +2195,27 @@ sub extractResourceInformation { my $srcf=$resource->src(); $srcf=~/\.(\w+)$/; $$typep{$id}=$1; + my $toolsymb; + if ($srcf =~ /ext\.tool$/) { + $toolsymb = $resource->symb(); + } $$keyp{$id}=''; $$uris{$id}=$srcf; - foreach my $key (split(/\,/,&Apache::lonnet::metadata($srcf,'allpossiblekeys'))) { + foreach my $key (split(/\,/,&Apache::lonnet::metadata($srcf,'allpossiblekeys',$toolsymb))) { next if ($key!~/^parameter_/); # Hidden parameters - next if (&Apache::lonnet::metadata($srcf,$key.'.hidden') eq 'parm'); + next if (&Apache::lonnet::metadata($srcf,$key.'.hidden',$toolsymb) eq 'parm'); # # allparms is a hash of parameter names # - my $name=&Apache::lonnet::metadata($srcf,$key.'.name'); + my $name=&Apache::lonnet::metadata($srcf,$key.'.name',$toolsymb); if (!exists($$allparms{$name}) || $$allparms{$name} =~ m/^\s*$/ ) { my ($display,$parmdis); $display = &standard_parameter_names($name); if ($display eq '') { - $display= &Apache::lonnet::metadata($srcf,$key.'.display'); + $display= &Apache::lonnet::metadata($srcf,$key.'.display',$toolsymb); $parmdis = $display; $parmdis =~ s/\s*\[Part.*$//g; } else { @@ -1714,14 +2224,14 @@ sub extractResourceInformation { $$allparms{$name}=$parmdis; if (ref($defkeytype)) { $$defkeytype{$name}= - &Apache::lonnet::metadata($srcf,$key.'.type'); + &Apache::lonnet::metadata($srcf,$key.'.type',$toolsymb); } } # # allparts is a hash of all parts # - my $part= &Apache::lonnet::metadata($srcf,$key.'.part'); + my $part= &Apache::lonnet::metadata($srcf,$key.'.part',$toolsymb); $$allparts{$part} = &mt('Part: [_1]',$part); # # Remember all keys going with this resource @@ -1762,6 +2272,29 @@ sub extractResourceInformation { } } +sub get_recursive { + my ($recurseup,$resdata,$what,$prefix) = @_; + if ((ref($resdata) eq 'HASH') && (ref($recurseup) eq 'ARRAY')) { + foreach my $item (@{$recurseup}) { + my $norecursechk=$prefix.'.'.$item.'___(all).'.$what; + if (defined($resdata->{$norecursechk})) { + if ($what =~ /\.(encrypturl|hiddenresource)$/) { + my $type = $resdata->{$norecursechk.'.type'}; + return [$resdata->{$norecursechk},$type,$item]; + } else { + last; + } + } + my $recursechk=$prefix.'.'.$item.'___(rec).'.$what; + if (defined($resdata->{$recursechk})) { + my $type = $resdata->{$recursechk.'.type'}; + return [$resdata->{$recursechk},$type,$item]; + } + } + } + return; +} + # Tells if a parameter type is a date. # @@ -1772,16 +2305,19 @@ sub isdateparm { return (($type=~/^date/) && (!($type eq 'date_interval'))); } +# Determine if parameter type is specialized string type (i.e., +# not just string or string_yesno. + +sub is_specialstring { + my $type=shift; + return (($type=~/^string_/) && ($type ne 'string_yesno')); +} + # Prints the HTML and Javascript to select parameters, with various shortcuts. -# FIXME: remove unused parameters # -# @param {Apache2::RequestRec} $r - the Apache request (unused) -# @param {hash reference} $allparms - hash parameter name -> parameter title -# @param {array reference} $pscat - list of selected parameter names (unused) -# @param {hash reference} $keyorder - hash parameter key -> appearance rank (unused) +# @param {Apache2::RequestRec} $r - the Apache request sub parmmenu { - my ($r,$allparms,$pscat,$keyorder)=@_; - my $tempkey; + my ($r)=@_; $r->print(< // print('
'); - &shortCuts($r,$allparms,$pscat,$keyorder); + &shortCuts($r); $r->print('
'); } @@ -1912,6 +2448,7 @@ sub lookUpTableParameter { 'buttonshide' => 'hiding', 'turnoffeditor' => 'hiding', 'encrypturl' => 'hiding', + 'deeplink' => 'hiding', 'randomorder' => 'high_level_randomization', 'randompick' => 'high_level_randomization', 'available' => 'slots', @@ -1927,8 +2464,8 @@ sub lookUpTableParameter { 'lenient' => 'grading', 'retrypartial' => 'tries', 'discussvote' => 'misc', - 'examcode' => 'high_level_randomization', - ); + 'examcode' => 'high_level_randomization', + ); } # Adds the given parameter name to an array of arrays listing all parameters for each category. @@ -2016,20 +2553,24 @@ 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 ''); $r->print('
' .'

'.&mt($categories{$key}).'

'."\n"); foreach my $tempkey (&keysindisplayorderCategory($categoryList{$key},$keyorder)) { + next if ($tempkey eq ''); $r->print('' .'
'."\n"); } $r->print('
'); @@ -2038,14 +2579,10 @@ sub parmboxes { } # Prints HTML with shortcuts to select groups of parameters in one click, or deselect all. -# FIXME: remove unused parameters # # @param {Apache2::RequestRec} $r - the Apache request -# @param {hash reference} $allparms - hash parameter name -> parameter title (unused) -# @param {array reference} $pscat - list of selected parameter names (unused) -# @param {hash reference} $keyorder - hash parameter key -> appearance rank (unused) sub shortCuts { - my ($r,$allparms,$pscat,$keyorder)=@_; + my ($r)=@_; # Parameter Selection $r->print( @@ -2124,9 +2661,37 @@ sub partmenu { sub usermenu { my ($r,$uname,$id,$udom,$csec,$cgroup,$parmlev,$usersgroups,$pssymb)=@_; my $chooseopt=&Apache::loncommon::select_dom_form($udom,'udom').' '. - &Apache::loncommon::selectstudent_link('parmform','uname','udom'); - my $selscript=&Apache::loncommon::studentbrowser_javascript(); + &Apache::loncommon::selectstudent_link('parmform','uname','udom','condition'). + &Apache::lonhtmlcommon::scripttag(<'. + $stuonly.'  '. + ''; my $sections=''; my %sectionhash = &Apache::loncommon::get_sections(); @@ -2135,7 +2700,7 @@ sub usermenu { if (($pssymb) || &Apache::lonnet::allowed('mdg',$env{'request.course.id'})) { %grouphash = &Apache::longroup::coursegroups(); } elsif ($env{'request.course.groups'} ne '') { - map { $grouphash{$_} = 1; } split(/,/,$env{'request.course.groups'}); + map { $grouphash{$_} = 1; } split(/:/,$env{'request.course.groups'}); } my $g_s_header=''; @@ -2193,7 +2758,7 @@ function group_or_section(caller) { } if (%grouphash) { - $groups=&mt('Group:').' print(' checked="checked"'); + my ($r,$sortorder,$context)=@_; + my %text; + if ($context eq 'newoverview') { + %text = &Apache::lonlocal::texthash ( + realmstudent => 'Sort by location in course first, then student (group/section)', + studentrealm => 'Sort by student (group/section) first, then location in course', + ); + } else { + %text = &Apache::lonlocal::texthash ( + realmstudent => 'Sort by realm first, then student (group/section)', + studentrealm => 'Sort by student (group/section) first, then realm', + ); } - $r->print(' />'.&mt('Sort by realm first, then student (group/section)')); - $r->print('
'); } +# Returns a hash parameter key -> order (integer) giving the order for some parameters. +# +# @returns {hash} sub standardkeyorder { return ('parameter_0_opendate' => 1, 'parameter_0_duedate' => 2, @@ -2556,28 +3185,60 @@ sub standardkeyorder { # Table mode UI. +# If nothing is selected, prints HTML forms to select resources, parts, parameters, user, group and section. +# Otherwise, prints the parameter table, with a link to change the selection unless a single resource is selected. +# +# Parameters used from the request: +# action - handler action (see handler), usermenu is checking for value 'settable' +# cgroup - selected group +# command - 'set': direct access to table mode for a resource +# csec - selected section +# dis - set when the "Update Display" button was used, used only to discard command 'set' +# hideparmsel - can be 'hidden' to hide the parameter selection div initially and display the "Change Parameter Selection" link instead (which displays the div) +# id - student/employee ID +# parmlev - selected level (full|map|general) +# part - selected part (unused ?) +# pres_marker - &&&-separated parameter identifiers, "resource id&part_parameter name&level" +# pres_type - &&&-separated parameter types +# pres_value - &&&-separated parameter values +# prevvisit - '1' if the user has submitted the form before +# pscat (multiple values) - selected parameter names +# pschp - selected map pc, or 'all' +# psprt (multiple values) - list of selected parameter parts +# filter - part of or whole parameter name, to be filtered out when parameters are displayed (unused ?) +# recent_* (* = parameter type) - recent values entered by the user for parameter types +# symb - resource symb (when a single resource is selected) +# udom - selected user domain +# uname - selected user name +# url - used only with command 'set', the resource url +# +# @param {Apache2::RequestRec} $r - the Apache request +# @param $parm_permission - ref to hash of permissions +# if $parm_permission->{'edit'} is true, editing is allowed. sub assessparms { - my $r=shift; + my ($r,$parm_permission) = @_; # -------------------------------------------------------- Variable declaration - my @ids=(); - my %symbp=(); - my %mapp=(); - my %typep=(); - my %keyp=(); - my %uris=(); - my %maptitles=(); - my %allmaps=(); - my %alllevs=(); - - my $uname; - my $udom; - my $uhome; - my $csec; - my $cgroup; - my @usersgroups = (); + my @ids=(); # resource and map ids + my %symbp=(); # hash map pc or resource/map id -> map src.'___(all)' or resource symb + my %mapp=(); # hash map pc or resource/map id -> enclosing map src + my %typep=(); # hash resource/map id -> resource type (file extension) + my %keyp=(); # hash resource/map id -> comma-separated list of parameter keys + my %uris=(); # hash resource/map id -> resource src + my %maptitles=(); # hash map pc or src -> map title + my %allmaps=(); # hash map pc -> map src + my %allmaps_inverted=(); # hash map src -> map pc + my %alllevs=(); # hash English level title -> value + + my $uname; # selected user name + my $udom; # selected user domain + my $uhome; # server with the user's files, or 'no_host' + my $csec; # selected section name + my $cgroup; # selected group name + my @usersgroups = (); # list of the user groups + my $numreclinks = 0; my $coursename=$env{'course.'.$env{'request.course.id'}.'.description'}; @@ -2585,8 +3246,8 @@ sub assessparms { $alllevs{'Map/Folder Level'}='map'; $alllevs{'Course Level'}='general'; - my %allparms; - my %allparts; + my %allparms; # hash parameter name -> parameter title + my %allparts; # hash parameter part -> part title # ------------------------------------------------------------------------------ # @@ -2611,7 +3272,7 @@ sub assessparms { if ($cgroup ne '') { unless (&Apache::lonnet::allowed('mdg',$env{'request.course.id'})) { if (($env{'request.course.groups'} eq '') || - (!grep(/^\Q$cgroup\E$/,split(/,/,$env{'request.course.groups'})))) { + (!grep(/^\Q$cgroup\E$/,split(/:/,$env{'request.course.groups'})))) { $noeditgrp = 1; } } @@ -2691,27 +3352,124 @@ sub assessparms { $csec=&Apache::lonnet::getsection($udom,$uname, $env{'request.course.id'}); if ($csec eq '-1') { - $message= - '

'. - &mt('User [_1] at domain [_2] not in this course', - "'".$uname."'","'".$udom."'"). - '

'; - $uname=''; - $csec=$env{'form.csec'}; + my $crstype = $env{'course.'.$env{'request.course.id'}.'.type'}; + if ($env{'form.userroles'} eq 'any') { + if (($env{'user.name'} eq $uname) && ($env{'user.domain'} eq $udom)) { + $csec = $env{'request.course.sec'}; + $message = ''; + if ($crstype eq 'Community') { + $message .= &mt('User [_1] at domain [_2] has a non-member role in this community', + $uname,$udom); + } else { + $message .= &mt('User [_1] at domain [_2] has a non-student role in this course', + $uname,$udom); + } + $message .= ''; + } else { + my @possroles = ('in','ep','ta','cr'); + if ($crstype eq 'Community') { + unshift(@possroles,'co'); + } else { + unshift(@possroles,'cc'); + } + my %not_student_roles = + &Apache::lonnet::get_my_roles($uname,$udom,'userroles',['active'], + \@possroles,[$udom],1,1); + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my %sections_by_role; + foreach my $role (keys(%not_student_roles)) { + if ($role =~ /^\Q$cnum:$cdom:\E([^:]+):(|[^:]+)$/) { + my ($rolename,$sec) = ($1,$2); + if ($rolename =~ m{^cr/}) { + $rolename = 'cr'; + } + push(@{$sections_by_role{$rolename}},$sec); + } + } + my $numroles = scalar(keys(%sections_by_role)); + if ($numroles) { + foreach my $role (@possroles) { + if (ref($sections_by_role{$role}) eq 'ARRAY') { + my @secs = sort { $a <=> $b } @{$sections_by_role{$role}}; + $csec = $secs[0]; + last; + } + } + } + if ($csec eq '-1') { + $message = ''; + if ($crstype eq 'Community') { + $message .= &mt('User [_1] at domain [_2] does not have a role in this community', + $uname,$udom); + } else { + $message .= &mt('User [_1] at domain [_2] does not have a role in this course', + $uname,$udom); + } + $message .= ''; + $uname=''; + if ($env{'request.course.sec'} ne '') { + $csec=$env{'request.course.sec'}; + } else { + $csec=$env{'form.csec'}; + } + $cgroup=$env{'form.cgroup'}; + } else { + $message = ''; + if ($crstype eq 'Community') { + $message .= &mt('User [_1] at domain [_2] has a non-member role in this community', + $uname,$udom); + } else { + $message .= &mt('User [_1] at domain [_2] has a non-student role in this course', + $uname,$udom); + } + $message .= ''; + } + } + } else { + $message = ''; + if ($crstype eq 'Community') { + $message .= &mt('User [_1] at domain [_2] does not have a member role in this community', + $uname,$udom); + } else { + $message .= &mt('User [_1] at domain [_2] does not have a student role in this course', + $uname,$udom); + } + $message .= ''; + $uname=''; + if ($env{'request.course.sec'} ne '') { + $csec=$env{'request.course.sec'}; + } else { + $csec=$env{'form.csec'}; + } + $cgroup=$env{'form.cgroup'}; + } + } elsif ($env{'request.course.sec'} ne '') { + if ($csec ne $env{'request.course.sec'}) { + $message=''. + &mt("User '[_1]' at domain '[_2]' not in section '[_3]'", + $uname,$udom,$env{'request.course.sec'}). + ''; + $uname=''; + $csec=$env{'request.course.sec'}; + } $cgroup=$env{'form.cgroup'}; - } else { + } + if ($uname ne '') { my %name=&Apache::lonnet::userenvironment($udom,$uname, ('firstname','middlename','lastname','generation','id')); - $message="\n

\n".&mt("Full Name").": ". - $name{'firstname'}.' '.$name{'middlename'}.' ' - .$name{'lastname'}.' '.$name{'generation'}. - "
\n".&mt('Student/Employee ID').": ".$name{'id'}.'

'; - } - @usersgroups = &Apache::lonnet::get_users_groups( - $udom,$uname,$env{'request.course.id'}); - if (@usersgroups > 0) { - unless (grep(/^\Q$cgroup\E$/,@usersgroups)) { - $cgroup = $usersgroups[0]; + $message .= "\n

\n".&mt('Full Name').': ' + .$name{'firstname'}.' '.$name{'middlename'}.' ' + .$name{'lastname'}.' '.$name{'generation'} + ."
\n".&mt('Student/Employee ID').': '.$name{'id'}.'

'; + @usersgroups = &Apache::lonnet::get_users_groups( + $udom,$uname,$env{'request.course.id'}); + if (@usersgroups > 0) { + unless (grep(/^\Q$cgroup\E$/,@usersgroups)) { + $cgroup = $usersgroups[0]; + } + } else { + $cgroup = ''; } } } @@ -2723,13 +3481,15 @@ sub assessparms { # --------------------------------------------------------- Get all assessments &extractResourceInformation(\@ids, \%typep,\%keyp, \%allparms, \%allparts, \%allmaps, \%mapp, \%symbp,\%maptitles,\%uris, - \%keyorder); + \%keyorder,undef,$pssymb); + + %allmaps_inverted = reverse(%allmaps); $mapp{'0.0'} = ''; $symbp{'0.0'} = ''; # ---------------------------------------------------------- Anything to store? - if ($env{'form.pres_marker'}) { + if ($env{'form.pres_marker'} && $parm_permission->{'edit'}) { my @markers=split(/\&\&\&/,$env{'form.pres_marker'}); my @values=split(/\&\&\&/,$env{'form.pres_value'}); my @types=split(/\&\&\&/,$env{'form.pres_type'}); @@ -2738,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); @@ -2746,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'; @@ -2884,42 +3650,78 @@ 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 .= '

'; } } + #----------------------------------------------- if all selected, fill in array - if ($pscat[0] eq "all") {@pscat = (keys(%allparms));} - if (!@pscat) { @pscat=('duedate','opendate','answerdate','weight','maxtries','type','problemstatus') }; - if ($psprt[0] eq "all" || !@psprt) {@psprt = (keys(%allparts));} + if ($pscat[0] eq "all") { + @pscat = (keys(%allparms)); + } + if (!@pscat) { + @pscat=('duedate','opendate','answerdate','weight','maxtries','type','problemstatus') + }; + if ($psprt[0] eq "all" || !@psprt) { + @psprt = (keys(%allparts)); + } # ------------------------------------------------------------------ Start page my $crstype = &Apache::loncommon::course_type(); &startpage($r,$pssymb,$crstype); foreach my $item ('tolerance','date_default','date_start','date_end', - 'date_interval','int','float','string') { + 'date_interval','int','float','string','string_lenient', + 'string_examcode','string_deeplink','string_discussvote', + 'string_useslots','string_problemstatus','string_ip', + 'string_questiontype') { $r->print(''). - '" name="recent_'.$item.'" />'); + &HTML::Entities::encode($env{'form.recent_'.$item},'"&<>'). + '" name="recent_'.$item.'" />'); } # ----- 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(< // ENDPARMSELSCRIPT if (!$pssymb) { + # No single resource selected, print forms to select things (hidden after first selection) my $parmselhiddenstyle=' style="display:none"'; if($env{'form.hideparmsel'} eq 'hidden') { $r->print('
'); @@ -2939,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); @@ -2948,7 +3750,7 @@ ENDPARMSELSCRIPT # Step 2 $r->print(&Apache::lonhtmlcommon::topic_bar(2,&mt('Parameter Specification'),'parmstep2')); - &displaymenu($r,\%allparms,\@pscat,\@psprt,\%keyorder,'parmmenuscroll'); + &displaymenu($r,\%allparms,\@pscat,\%keyorder,'parmmenuscroll'); # Step 3 $r->print(&Apache::lonhtmlcommon::topic_bar(3,&mt('User Specification (optional)'),'parmstep3')); @@ -3015,15 +3817,20 @@ ENDPARMSELSCRIPT my @catmarker=map { tr|.|_|; 'parameter_'.$_; } @pscat; my $csuname=$env{'user.name'}; my $csudom=$env{'user.domain'}; + my $readonly = 1; + if ($parm_permission->{'edit'}) { + undef($readonly); + } + $r->print('
'); if ($parmlev eq 'full') { # # This produces the cascading table output of parameters # - my $coursespan=$csec?10:6; - my $userspan=4; + my $coursespan=$csec?8:5; + my $userspan=3; if ($cgroup ne '') { - $coursespan += 4; + $coursespan += 3; } $r->print(&Apache::loncommon::start_data_table()); @@ -3053,44 +3860,43 @@ ENDPARMSELSCRIPT 'femof' => 'from Enclosing Map or Folder', 'gen' => 'general', 'foremf' => 'for Enclosing Map or Folder', - 'formfr' => 'for Map or Folder (recursive)', 'fr' => 'for Resource' ); $r->print(<$lt{'pie'} $lt{'csv'}
($csuname:$csudom) -$lt{'ic'}$lt{'rl'} +$lt{'ic'}$lt{'rl'} $lt{'ic'} ENDTABLETWO if ($csec) { - $r->print(''. + $r->print(''. &mt("in Section")." $csec"); } if ($cgroup) { - $r->print(''. + $r->print(''. &mt("in Group")." $cgroup"); } $r->print(<$lt{'aut'}$lt{'type'} $lt{'emof'}$lt{'part'}$lt{'pn'} -$lt{'gen'}$lt{'formfr'}$lt{'foremf'} +$lt{'gen'}$lt{'foremf'} $lt{'def'}$lt{'femof'}$lt{'fr'} ENDTABLEHEADFOUR if ($csec) { - $r->print(''.$lt{'gen'}.''.$lt{'formfr'}.''.$lt{'foremf'}.''.$lt{'fr'}.''); + $r->print(''.$lt{'gen'}.''.$lt{'foremf'}.''.$lt{'fr'}.''); } if ($cgroup) { - $r->print(''.$lt{'gen'}.''.$lt{'formfr'}.''.&mt('foremf').''.$lt{'fr'}.''); + $r->print(''.$lt{'gen'}.''.$lt{'foremf'}.''.$lt{'fr'}.''); } if ($uname) { if (@usersgroups > 1) { $r->print(''.&mt('Control by other group?').''); } - $r->print(''.$lt{'gen'}.''.$lt{'formfr'}.''.$lt{'foremf'}.''.$lt{'fr'}.''); + $r->print(''.$lt{'gen'}.''.$lt{'foremf'}.''.$lt{'fr'}.''); } $r->print(''); @@ -3104,7 +3910,6 @@ ENDTABLEHEADFOUR foreach my $rid (@ids) { my ($inmapid)=($rid=~/\.(\d+)$/); - if ((!$pssymb && (($pschp eq 'all') || ($allmaps{$pschp} eq $mapp{$rid}))) || @@ -3134,19 +3939,23 @@ ENDTABLEHEADFOUR my %type= (); my %default=(); my $uri=&Apache::lonnet::declutter($uris{$rid}); + my $toolsymb; + if ($uri =~ /ext\.tool$/) { + $toolsymb = $symbp{$rid}; + } my $filter=$env{'form.filter'}; foreach my $tempkeyp (&keysplit($keyp{$rid})) { if (grep $_ eq $tempkeyp, @catmarker) { - my $parmname=&Apache::lonnet::metadata($uri,$tempkeyp.'.name'); + my $parmname=&Apache::lonnet::metadata($uri,$tempkeyp.'.name',$toolsymb); # We may only want certain parameters listed if ($filter) { unless ($filter=~/\Q$parmname\E/) { next; } } $name{$tempkeyp}=$parmname; - $part{$tempkeyp}=&Apache::lonnet::metadata($uri,$tempkeyp.'.part'); + $part{$tempkeyp}=&Apache::lonnet::metadata($uri,$tempkeyp.'.part',$toolsymb); - my $parmdis=&Apache::lonnet::metadata($uri,$tempkeyp.'.display'); + my $parmdis=&Apache::lonnet::metadata($uri,$tempkeyp.'.display',$toolsymb); if ($allparms{$name{$tempkeyp}} ne '') { my $identifier; if ($parmdis =~ /(\s*\[Part.*)$/) { @@ -3158,15 +3967,20 @@ ENDTABLEHEADFOUR } unless ($display{$tempkeyp}) { $display{$tempkeyp}=''; } $display{$tempkeyp}.=' ('.$name{$tempkeyp}.')'; - $default{$tempkeyp}=&Apache::lonnet::metadata($uri,$tempkeyp); - $type{$tempkeyp}=&Apache::lonnet::metadata($uri,$tempkeyp.'.type'); - $thistitle=&Apache::lonnet::metadata($uri,$tempkeyp.'.title'); + $default{$tempkeyp}=&Apache::lonnet::metadata($uri,$tempkeyp,$toolsymb); + $type{$tempkeyp}=&Apache::lonnet::metadata($uri,$tempkeyp.'.type',$toolsymb); + $thistitle=&Apache::lonnet::metadata($uri,$tempkeyp.'.title',$toolsymb); } } my $totalparms=scalar(keys(%name)); if ($totalparms>0) { my $firstrow=1; my $title=&Apache::lonnet::gettitle($symbp{$rid}); + my $navmap = Apache::lonnavmaps::navmap->new(); + my @recurseup; + if (ref($navmap) && $mapp{$rid}) { + @recurseup = $navmap->recurseup_maps($mapp{$rid}); + } $r->print(''. @@ -3198,7 +4012,9 @@ ENDTABLEHEADFOUR &print_row($r,$item,\%part,\%name,\%symbp,$rid,\%default, \%type,\%display,$defbgone,$defbgtwo, $defbgthree,$parmlev,$uname,$udom,$csec, - $cgroup,\@usersgroups,$noeditgrp); + $cgroup,\@usersgroups,$noeditgrp,$readonly, + \@recurseup,\%maptitles,\%allmaps_inverted, + \$numreclinks); } } } @@ -3222,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 @@ -3244,6 +4060,11 @@ ENDTABLEHEADFOUR if ($map eq $mapid) { my $uri=&Apache::lonnet::declutter($uris{$rid}); + my $toolsymb; + if ($uri =~ /ext\.tool$/) { + $toolsymb = $symbp{$rid}; + } + # $r->print("Keys: $keyp{$rid}
\n"); #-------------------------------------------------------------------- @@ -3259,8 +4080,8 @@ ENDTABLEHEADFOUR if ((grep $_ eq $fullkeyp, @catmarker) &&(!$name{$tempkeyp})) { $part{$tempkeyp}="0"; - $name{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp.'.name'); - my $parmdis=&Apache::lonnet::metadata($uri,$fullkeyp.'.display'); + $name{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp.'.name',$toolsymb); + my $parmdis=&Apache::lonnet::metadata($uri,$fullkeyp.'.display',$toolsymb); if ($allparms{$name{$tempkeyp}} ne '') { my $identifier; if ($parmdis =~ /(\s*\[Part.*)$/) { @@ -3273,8 +4094,8 @@ ENDTABLEHEADFOUR unless ($display{$tempkeyp}) { $display{$tempkeyp}=''; } $display{$tempkeyp}.=' ('.$name{$tempkeyp}.')'; $display{$tempkeyp} =~ s/_\w+_/_0_/; - $default{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp); - $type{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp.'.type'); + $default{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp,$toolsymb); + $type{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp.'.type',$toolsymb); } } # end loop through keys } @@ -3311,17 +4132,28 @@ ENDTABLEHEADFOUR $r->print('

'.&Apache::loncommon::start_data_table() .&Apache::loncommon::start_data_table_header_row() .''.&mt('Parameter Name').'' - .''.&mt('Recursive Value').'' - .''.&mt('Non-Recursive Value').'' + .''.&mt('Value').'' .''.&mt('Parameter in Effect').'' .&Apache::loncommon::end_data_table_header_row() ); + my $navmap = Apache::lonnavmaps::navmap->new(); + my @recurseup; + if (ref($navmap)) { + my $mapres = $navmap->getByMapPc($mapid); + if (ref($mapres)) { + @recurseup = $navmap->recurseup_maps($mapres->src()); + } + } + + foreach my $item (&keysinorder(\%name,\%keyorder)) { $r->print(&Apache::loncommon::start_data_table_row()); &print_row($r,$item,\%part,\%name,\%symbp,$mapid,\%default, \%type,\%display,$defbgone,$defbgtwo,$defbgthree, - $parmlev,$uname,$udom,$csec,$cgroup,'',$noeditgrp); + $parmlev,$uname,$udom,$csec,$cgroup,'',$noeditgrp, + $readonly,\@recurseup,\%maptitles,\%allmaps_inverted, + \$numreclinks); } $r->print(&Apache::loncommon::end_data_table().'

' .'
' @@ -3348,6 +4180,10 @@ ENDTABLEHEADFOUR my $rid = $id; my $uri=&Apache::lonnet::declutter($uris{$rid}); + my $toolsymb; + if ($uri =~ /ext\.tool$/) { + $toolsymb = $symbp{$rid}; + } #-------------------------------------------------------------------- # @catmarker contains list of all possible parameters including part #s @@ -3361,8 +4197,8 @@ ENDTABLEHEADFOUR $tempkeyp =~ s/_\w+_/_0_/; if ((grep $_ eq $fullkeyp, @catmarker) &&(!$name{$tempkeyp})) { $part{$tempkeyp}="0"; - $name{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp.'.name'); - my $parmdis=&Apache::lonnet::metadata($uri,$fullkeyp.'.display'); + $name{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp.'.name',$toolsymb); + my $parmdis=&Apache::lonnet::metadata($uri,$fullkeyp.'.display',$toolsymb); if ($allparms{$name{$tempkeyp}} ne '') { my $identifier; if ($parmdis =~ /(\s*\[Part.*)$/) { @@ -3375,8 +4211,8 @@ ENDTABLEHEADFOUR unless ($display{$tempkeyp}) { $display{$tempkeyp}=''; } $display{$tempkeyp}.=' ('.$name{$tempkeyp}.')'; $display{$tempkeyp} =~ s/_\w+_/_0_/; - $default{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp); - $type{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp.'.type'); + $default{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp,$toolsymb); + $type{$tempkeyp}=&Apache::lonnet::metadata($uri,$fullkeyp.'.type',$toolsymb); } } # end loop through keys } # end loop through ids @@ -3410,16 +4246,37 @@ ENDMAPONE foreach my $item (&keysinorder(\%name,\%keyorder)) { $r->print(&Apache::loncommon::start_data_table_row()); &print_row($r,$item,\%part,\%name,\%symbp,$mapid,\%default, - \%type,\%display,$defbgone,$defbgtwo,$defbgthree, - $parmlev,$uname,$udom,$csec,$cgroup,'',$noeditgrp); + \%type,\%display,$defbgone,$defbgtwo,$defbgthree, + $parmlev,$uname,$udom,$csec,$cgroup,'',$noeditgrp, + $readonly); } $r->print(&Apache::loncommon::end_data_table() .'

' .'' ); } # end of $parmlev eq general + $r->print('
'); } $r->print(''); + if ($numreclinks) { + $r->print(<<"END"); +
+ + + + +
+ +END + } &endSettingsScreen($r); $r->print(&Apache::loncommon::end_page()); } # end sub assessparms @@ -3429,10 +4286,14 @@ ENDMAPONE ################################################## # OVERVIEW MODE ################################################## -my $tableopen; +my $tableopen; # boolean, true if HTML table is already opened + +# Returns HTML with the HTML table start tag and header, unless the table is already opened. +# @param {boolean} $readonly - true if values cannot be edited (otherwise more columns are added) +# @returns {string} sub tablestart { - my ($readonly) = @_; + my ($readonly,$is_map) = @_; if ($tableopen) { return ''; } else { @@ -3441,13 +4302,19 @@ sub tablestart { if ($readonly) { $output .= ''.&mt('Current value').''; } else { - $output .= ''.&mt('Delete').''.&mt('Set to ...').''; + $output .= ''.&mt('Delete').''. + ''.&mt('Set to ...').''; + if ($is_map) { + $output .= ''.&mt('Recursive?').''; + } } $output .= ''; return $output; } } +# Returns HTML with the HTML table end tag, unless the table is not opened. +# @returns {string} sub tableend { if ($tableopen) { $tableopen=0; @@ -3457,6 +4324,13 @@ sub tableend { } } +# Reads course and user information. +# If the context is looking for a scalar, returns the course parameters hash (result of lonnet::get_courseresdata, dump of course's resourcedata.db) with added student data from lonnet::get_userresdata (which reads the user's resourcedata.db). +# The key for student data is modified with '[useropt:'.username.':'.userdomain.'].'. +# If the context is looking for a list, returns a list with the scalar data and the class list. +# @param {string} $crs - course number +# @param {string} $dom - course domain +# @returns {hash reference|Array} sub readdata { my ($crs,$dom)=@_; # Read coursedata @@ -3485,8 +4359,24 @@ sub readdata { } -# Setting - +# Stores parameter data, using form parameters directly. +# +# Uses the following form parameters. The variable part in the names is a resourcedata key (except for a modification for user data). +# set_* (except settext, setipallow, setipdeny, setdeeplink) - set a parameter value +# del_* - remove a parameter +# datepointer_* - set a date parameter (value is key_* refering to a set of other form parameters) +# dateinterval_* - set a date interval parameter (value refers to more form parameters) +# key_* - date values +# days_* - for date intervals +# hours_* - for date intervals +# minutes_* - for date intervals +# seconds_* - for date intervals +# done_* - for date intervals +# typeof_* - parameter type +# +# @param {Apache2::RequestRec} $r - the Apache request +# @param {string} $crs - course number +# @param {string} $dom - course domain sub storedata { my ($r,$crs,$dom)=@_; # Set userlevel immediately @@ -3495,6 +4385,8 @@ sub storedata { my %newdata=(); undef %newdata; my @deldata=(); + my @delrec=(); + my @delnonrec=(); undef @deldata; my ($got_chostname,$chostname,$cmajor,$cminor); my $now = time; @@ -3502,188 +4394,346 @@ sub storedata { if ($key =~ /^form\.([a-z]+)\_(.+)$/) { my $cmd=$1; my $thiskey=$2; - next if ($cmd eq 'settext' || $cmd eq 'setipallow' || $cmd eq 'setipdeny'); + my ($altkey,$recursive,$tkey,$tkeyrec,$tkeynonrec); + next if ($cmd eq 'rec' || $cmd eq 'settext' || $cmd eq 'setipallow' || $cmd eq 'setipdeny' || $cmd eq 'setdeeplink'); + if ((($cmd eq 'set') || ($cmd eq 'datepointer') || ($cmd eq 'dateinterval') || ($cmd eq 'del')) && + ($thiskey =~ /(?:sequence|page)\Q___(all)\E/)) { + unless ($thiskey =~ /(encrypturl|hiddenresource)$/) { + $altkey = $thiskey; + $altkey =~ s/\Q___(all)\E/___(rec)/; + if ($env{'form.rec_'.$thiskey}) { + $recursive = 1; + } + } + } my ($tuname,$tudom)=&extractuser($thiskey); - my $tkey=$thiskey; if ($tuname) { + $tkey=$thiskey; $tkey=~s/\.\[useropt\:$tuname\:$tudom\]\./\./; + if ($altkey) { + $tkeynonrec = $tkey; + $tkeyrec = $altkey; + $tkeyrec=~s/\.\[useropt\:$tuname\:$tudom\]\./\./; + } } if ($cmd eq 'set' || $cmd eq 'datepointer' || $cmd eq 'dateinterval') { - my ($data, $typeof, $text, $name, $valchk, $valmatch, $namematch); - if ($cmd eq 'set') { - $data=$env{$key}; - $valmatch = ''; - $valchk = $data; - $typeof=$env{'form.typeof_'.$thiskey}; - $text = &mt('Saved modified parameter for'); - if ($typeof eq 'string_questiontype') { - $name = 'type'; - } elsif ($typeof eq 'string_lenient') { - $name = 'lenient'; - my $stringmatch = &standard_string_matches($typeof); - if (ref($stringmatch) eq 'ARRAY') { - foreach my $item (@{$stringmatch}) { - if (ref($item) eq 'ARRAY') { - my ($regexpname,$pattern) = @{$item}; - if ($pattern ne '') { - if ($data =~ /$pattern/) { - $valmatch = $regexpname; - $valchk = ''; - last; + my ($data, $typeof, $text, $name, $valchk, $valmatch, $namematch); + if ($cmd eq 'set') { + $data=$env{$key}; + $valmatch = ''; + $valchk = $data; + $typeof=$env{'form.typeof_'.$thiskey}; + $text = &mt('Saved modified parameter for'); + if ($typeof eq 'string_questiontype') { + $name = 'type'; + } elsif (($typeof eq 'string_lenient') || ($typeof eq 'string_deeplink')) { + ($name) = ($typeof =~ /^string_(lenient|deeplink)$/); + my $stringmatch = &standard_string_matches($typeof); + if (ref($stringmatch) eq 'ARRAY') { + foreach my $item (@{$stringmatch}) { + if (ref($item) eq 'ARRAY') { + my ($regexpname,$pattern) = @{$item}; + if ($pattern ne '') { + if ($data =~ /$pattern/) { + $valmatch = $regexpname; + $valchk = ''; + last; + } } } } } + } elsif ($typeof eq 'string_discussvote') { + $name = 'discussvote'; + } elsif ($typeof eq 'string_examcode') { + $name = 'examcode'; + if (&Apache::lonnet::validCODE($data)) { + $valchk = 'valid'; + } + } elsif ($typeof eq 'string_yesno') { + if ($thiskey =~ /\.retrypartial$/) { + $name = 'retrypartial'; + } } - } elsif ($typeof eq 'string_discussvote') { - $name = 'discussvote'; - } elsif ($typeof eq 'string_examcode') { - $name = 'examcode'; - if (&Apache::lonnet::validCODE($data)) { - $valchk = 'valid'; - } - } elsif ($typeof eq 'string_yesno') { - if ($thiskey =~ /\.retrypartial$/) { - $name = 'retrypartial'; - } - } - } elsif ($cmd eq 'datepointer') { - $data=&Apache::lonhtmlcommon::get_date_from_form($env{$key}); - $typeof=$env{'form.typeof_'.$thiskey}; - $text = &mt('Saved modified date for'); - if ($typeof eq 'date_start') { - if ($thiskey =~ /\.printstartdate$/) { - $name = 'printstartdate'; - if (($data) && ($data > $now)) { - $valchk = 'future'; - } - } - } elsif ($typeof eq 'date_end') { - if ($thiskey =~ /\.printenddate$/) { - $name = 'printenddate'; - if (($data) && ($data < $now)) { - $valchk = 'past'; + } elsif ($cmd eq 'datepointer') { + $data=&Apache::lonhtmlcommon::get_date_from_form($env{$key}); + $typeof=$env{'form.typeof_'.$thiskey}; + $text = &mt('Saved modified date for'); + if ($typeof eq 'date_start') { + if ($thiskey =~ /\.printstartdate$/) { + $name = 'printstartdate'; + if (($data) && ($data > $now)) { + $valchk = 'future'; + } + } + } elsif ($typeof eq 'date_end') { + if ($thiskey =~ /\.printenddate$/) { + $name = 'printenddate'; + if (($data) && ($data < $now)) { + $valchk = 'past'; + } } } - } - } elsif ($cmd eq 'dateinterval') { - $data=&get_date_interval_from_form($thiskey); - if ($thiskey =~ /\.interval$/) { - $name = 'interval'; - my $intervaltype = &get_intervaltype($name); - my $intervalmatch = &standard_interval_matches($intervaltype); - if (ref($intervalmatch) eq 'ARRAY') { - foreach my $item (@{$intervalmatch}) { - if (ref($item) eq 'ARRAY') { - my ($regexpname,$pattern) = @{$item}; - if ($pattern ne '') { - if ($data =~ /$pattern/) { - $valmatch = $regexpname; - $valchk = ''; - last; + } elsif ($cmd eq 'dateinterval') { + $data=&get_date_interval_from_form($thiskey); + if ($thiskey =~ /\.interval$/) { + $name = 'interval'; + my $intervaltype = &get_intervaltype($name); + my $intervalmatch = &standard_interval_matches($intervaltype); + if (ref($intervalmatch) eq 'ARRAY') { + foreach my $item (@{$intervalmatch}) { + if (ref($item) eq 'ARRAY') { + my ($regexpname,$pattern) = @{$item}; + if ($pattern ne '') { + if ($data =~ /$pattern/) { + $valmatch = $regexpname; + $valchk = ''; + last; + } } } } } } + $typeof=$env{'form.typeof_'.$thiskey}; + $text = &mt('Saved modified date for'); } - $typeof=$env{'form.typeof_'.$thiskey}; - $text = &mt('Saved modified date for'); - } - if ($thiskey =~ m{\.(?:sequence|page)___\(rec\)}) { - $namematch = 'maplevelrecurse'; - } - if (($name ne '') || ($namematch ne '')) { - my ($needsrelease,$needsnewer); - if ($name ne '') { - $needsrelease = $Apache::lonnet::needsrelease{"parameter:$name:$valchk:$valmatch:"}; - if ($needsrelease) { - unless ($got_chostname) { - ($chostname,$cmajor,$cminor)=¶meter_release_vars(); - $got_chostname = 1; - } - $needsnewer = ¶meter_releasecheck($name,$valchk,$valmatch,undef, - $needsrelease, - $cmajor,$cminor); - } + if ($recursive) { + $namematch = 'maplevelrecurse'; } - if ($namematch ne '') { - if ($needsnewer) { - undef($namematch); - } else { - my $currneeded; + if (($name ne '') || ($namematch ne '')) { + my ($needsrelease,$needsnewer); + if ($name ne '') { + $needsrelease = $Apache::lonnet::needsrelease{"parameter:$name:$valchk:$valmatch:"}; if ($needsrelease) { - $currneeded = $needsrelease; - } - $needsrelease = - $Apache::lonnet::needsrelease{"parameter::::$namematch"}; - if (($needsrelease) && (($currneeded eq '') || ($needsrelease < $currneeded))) { unless ($got_chostname) { - ($chostname,$cmajor,$cminor) = ¶meter_release_vars(); + ($chostname,$cmajor,$cminor)=¶meter_release_vars(); $got_chostname = 1; } - $needsnewer = ¶meter_releasecheck(undef,$valchk,$valmatch,$namematch, - $needsrelease,$cmajor,$cminor); - } else { + $needsnewer = ¶meter_releasecheck($name,$valchk,$valmatch,undef, + $needsrelease, + $cmajor,$cminor); + } + } + if ($namematch ne '') { + if ($needsnewer) { undef($namematch); + } else { + my $currneeded; + if ($needsrelease) { + $currneeded = $needsrelease; + } + $needsrelease = + $Apache::lonnet::needsrelease{"parameter::::$namematch"}; + if (($needsrelease) && + (($currneeded eq '') || ($needsrelease < $currneeded))) { + unless ($got_chostname) { + ($chostname,$cmajor,$cminor) = ¶meter_release_vars(); + $got_chostname = 1; + } + $needsnewer = ¶meter_releasecheck(undef,$valchk,$valmatch, + $namematch, $needsrelease,$cmajor,$cminor); + } else { + undef($namematch); + } } } + if ($needsnewer) { + $r->print('
'.&oldversion_warning($name,$namematch,$data, + $chostname,$cmajor, + $cminor,$needsrelease)); + next; + } } - if ($needsnewer) { - $r->print('
'.&oldversion_warning($name,$namematch,$data, - $chostname,$cmajor, - $cminor,$needsrelease)); - next; - } - } - if (defined($data) and $$olddata{$thiskey} ne $data) { + my ($reconlychg,$haschange,$storekey); if ($tuname) { - if (&Apache::lonnet::put('resourcedata',{$tkey=>$data, - $tkey.'.type' => $typeof}, - $tudom,$tuname) eq 'ok') { - &log_parmset({$tkey=>$data,$tkey.'.type' => $typeof},0,$tuname,$tudom); - $r->print('
'.$text.' '. - &Apache::loncommon::plainname($tuname,$tudom)); + my $ustorekey; + if ($altkey) { + if ($recursive) { + if (exists($$olddata{$thiskey})) { + if ($$olddata{$thiskey} eq $data) { + $reconlychg = 1; + } + &Apache::lonnet::del('resourcedata',[$tkeynonrec,$tkeynonrec.'.type'],$tudom,$tuname); + } + if (exists($$olddata{$altkey})) { + if (defined($data) && $$olddata{$altkey} ne $data) { + $haschange = 1; + } + } elsif ((!$reconlychg) && ($data ne '')) { + $haschange = 1; + } + $ustorekey = $tkeyrec; + } else { + if (exists($$olddata{$altkey})) { + if ($$olddata{$altkey} eq $data) { + $reconlychg = 1; + } + &Apache::lonnet::del('resourcedata',[$tkeyrec,$tkeyrec.'.type'],$tudom,$tuname); + } + if (exists($$olddata{$thiskey})) { + if (defined($data) && $$olddata{$thiskey} ne $data) { + $haschange = 1; + } + } elsif ((!$reconlychg) && ($data ne '')) { + $haschange = 1; + } + $ustorekey = $tkeynonrec; + } } else { - $r->print('
'. - &mt('Error saving parameters').'
'); + if (exists($$olddata{$tkey})) { + if (defined($data) && $$olddata{$tkey} ne $data) { + $haschange = 1; + } + $ustorekey = $tkey; + } + } + if ($haschange || $reconlychg) { + unless ($env{'form.del_'.$thiskey}) { + if (&Apache::lonnet::put('resourcedata',{$ustorekey=>$data, + $ustorekey.'.type' => $typeof}, + $tudom,$tuname) eq 'ok') { + &log_parmset({$ustorekey=>$data,$ustorekey.'.type' => $typeof},0,$tuname,$tudom); + $r->print('
'.$text.' '. + &Apache::loncommon::plainname($tuname,$tudom)); + } else { + $r->print('
'. + &mt('Error saving parameters').'
'); + } + &Apache::lonnet::devalidateuserresdata($tuname,$tudom); + } } - &Apache::lonnet::devalidateuserresdata($tuname,$tudom); } else { - $newdata{$thiskey}=$data; - $newdata{$thiskey.'.type'}=$typeof; + if ($altkey) { + if ($recursive) { + if (exists($$olddata{$thiskey})) { + if ($$olddata{$thiskey} eq $data) { + $reconlychg = 1; + } + push(@delnonrec,($thiskey,$thiskey.'.type')); + } + if (exists($$olddata{$altkey})) { + if (defined($data) && $$olddata{$altkey} ne $data) { + $haschange = 1; + } + } elsif (($data ne '') && (!$reconlychg)) { + $haschange = 1; + } + $storekey = $altkey; + } else { + if (exists($$olddata{$altkey})) { + if ($$olddata{$altkey} eq $data) { + $reconlychg = 1; + } + push(@delrec,($altkey,$altkey.'.type')); + } + if (exists($$olddata{$thiskey})) { + if (defined($data) && $$olddata{$thiskey} ne $data) { + $haschange = 1; + } + } elsif (($data ne '') && (!$reconlychg)) { + $haschange = 1; + } + $storekey = $thiskey; + } + } else { + if (defined($data) && $$olddata{$thiskey} ne $data) { + $haschange = 1; + $storekey = $thiskey; + } + } + } + if ($reconlychg || $haschange) { + unless ($env{'form.del_'.$thiskey}) { + $newdata{$storekey}=$data; + $newdata{$storekey.'.type'}=$typeof; + } } - } } elsif ($cmd eq 'del') { if ($tuname) { - if (&Apache::lonnet::del('resourcedata',[$tkey],$tudom,$tuname) eq 'ok') { - &log_parmset({$tkey=>''},1,$tuname,$tudom); - $r->print('
'.&mt('Deleted parameter for').' '.&Apache::loncommon::plainname($tuname,$tudom)); + my $error; + if ($altkey) { + if (exists($$olddata{$altkey})) { + if (&Apache::lonnet::del('resourcedata',[$tkeyrec,$tkeyrec.'.type'],$tudom,$tuname) eq 'ok') { + &log_parmset({$tkeyrec=>''},1,$tuname,$tudom); + if ($recursive) { + $r->print('
'.&mt('Deleted parameter for').' '.&Apache::loncommon::plainname($tuname,$tudom)); + } + } elsif ($recursive) { + $error = 1; + } + } + if (exists($$olddata{$thiskey})) { + if (&Apache::lonnet::del('resourcedata',[$tkeynonrec,$tkeynonrec.'.type'],$tudom,$tuname) eq 'ok') { + &log_parmset({$tkeynonrec=>''},1,$tuname,$tudom); + unless ($recursive) { + $r->print('
'.&mt('Deleted parameter for').' '.&Apache::loncommon::plainname($tuname,$tudom)); + } + } elsif (!$recursive) { + $error = 1; + } + } } else { + if (exists($$olddata{$thiskey})) { + if (&Apache::lonnet::del('resourcedata',[$tkey,$tkey.'.type'],$tudom,$tuname) eq 'ok') { + &log_parmset({$tkey=>''},1,$tuname,$tudom); + $r->print('
'.&mt('Deleted parameter for').' '.&Apache::loncommon::plainname($tuname,$tudom)); + } else { + $error = 1; + } + } + } + if ($error) { $r->print('
'. &mt('Error deleting parameters').'
'); } &Apache::lonnet::devalidateuserresdata($tuname,$tudom); } else { - push (@deldata,$thiskey,$thiskey.'.type'); + if ($altkey) { + if (exists($$olddata{$altkey})) { + unless (grep(/^\Q$altkey\E$/,@delrec)) { + push(@deldata,($altkey,$altkey.'.type')); + } + } + if (exists($$olddata{$thiskey})) { + unless (grep(/^\Q$thiskey\E$/,@delnonrec)) { + push(@deldata,($thiskey,$thiskey.'.type')); + } + } + } elsif (exists($$olddata{$thiskey})) { + push(@deldata,($thiskey,$thiskey.'.type')); + } } } } } # Store all course level my $delentries=$#deldata+1; + my @alldels; + if (@delrec) { + push(@alldels,@delrec); + } + if (@delnonrec) { + push(@alldels,@delnonrec); + } + if (@deldata) { + push(@alldels,@deldata); + } my @newdatakeys=keys(%newdata); my $putentries=$#newdatakeys+1; - if ($delentries) { - if (&Apache::lonnet::del('resourcedata',\@deldata,$dom,$crs) eq 'ok') { - my %loghash=map { $_ => '' } @deldata; + my ($delresult,$devalidate); + if (@alldels) { + if (&Apache::lonnet::del('resourcedata',\@alldels,$dom,$crs) eq 'ok') { + my %loghash=map { $_ => '' } @alldels; &log_parmset(\%loghash,1); - $r->print('

'.&mt('Deleted [quant,_1,parameter]',$delentries/2).'

'); - } else { + if ($delentries) { + $r->print('

'.&mt('Deleted [quant,_1,parameter]',$delentries/2).'

'); + } + } elsif ($delentries) { $r->print('
'. &mt('Error deleting parameters').'
'); } - &Apache::lonnet::devalidatecourseresdata($crs,$dom); + $devalidate = 1; } if ($putentries) { if (&Apache::lonnet::put('resourcedata',\%newdata,$dom,$crs) eq 'ok') { @@ -3693,15 +4743,27 @@ sub storedata { $r->print('
'. &mt('Error saving parameters').'
'); } + $devalidate = 1; + } + if ($devalidate) { &Apache::lonnet::devalidatecourseresdata($crs,$dom); } } +# Returns the username and domain from a key created in readdata from a resourcedata key. +# +# @param {string} $key - the key +# @returns {Array} sub extractuser { my $key=shift; return ($key=~/^$env{'request.course.id'}.\[useropt\:($match_username)\:($match_domain)\]\./); } +# Parses a parameter key and returns the components. +# +# @param {string} $key - +# @param {hash reference} $listdata - +# @return {Array} - (student, resource, part, parameter) sub parse_listdata_key { my ($key,$listdata) = @_; # split into student/section affected, and @@ -3722,9 +4784,21 @@ sub parse_listdata_key { return ($student,$res,$part,$parm); } -# Displays forms for the given data in overview mode (newoverview or overview). +# Prints HTML with forms for the given parameter data in overview mode (newoverview or overview). +# +# @param {Apache2::RequestRec} $r - the Apache request +# @param {hash reference} $resourcedata - parameter data returned by readdata +# @param {hash reference} $listdata - data created in secgroup_lister, course id.[section id].part.name -> 1 or course id.[section id].part.name.type -> parameter type +# @param {string} $sortorder - realmstudent|studentrealm +# @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)=@_; + my ($r,$resourcedata,$listdata,$sortorder,$caller,$classlist,$readonly,$parmlev,$hash_for_realm,$pschp)=@_; # Start list output @@ -3735,6 +4809,7 @@ sub listdata { $tableopen=0; my $foundkeys=0; my %keyorder=&standardkeyorder(); + my $readonlyall = $readonly; my ($secidx,%grouphash); if (($env{'request.course.sec'} ne '') && ($caller eq 'overview')) { @@ -3742,13 +4817,27 @@ sub listdata { if (&Apache::lonnet::allowed('mdg',$env{'request.course.id'})) { %grouphash = &Apache::longroup::coursegroups(); } elsif ($env{'request.course.groups'} ne '') { - map { $grouphash{$_} = 1; } split(/,/,$env{'request.course.groups'}); + map { $grouphash{$_} = 1; } split(/:/,$env{'request.course.groups'}); } } - foreach my $thiskey (sort { + 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}; @@ -3758,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 ) { @@ -3768,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); } @@ -3786,17 +4903,19 @@ sub listdata { $result; - } keys(%{$listdata})) { # foreach my $thiskey - - my $readonly; + } keys(%{$listdata})) { # foreach my $key + my $thiskey = $key; if ($$listdata{$thiskey.'.type'}) { my $thistype=$$listdata{$thiskey.'.type'}; if ($$resourcedata{$thiskey.'.type'}) { $thistype=$$resourcedata{$thiskey.'.type'}; } my ($middle,$part,$name)= - ($thiskey=~/^$env{'request.course.id'}\.(?:(.+)\.)*([\w\s]+)\.(\w+)$/); + ($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; if ($issection=~/^useropt\:($match_username)\:($match_domain)/) { @@ -3809,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})) { @@ -3829,16 +4949,48 @@ sub listdata { $middle=~s/\.+$//; $middle=~s/^\.+//; my $realm=''.&mt('All Resources').''; - if ($middle=~/^(.+)\_\_\_\((all|rec)\)$/) { - my $mapurl = $1; - my $maplevel = $2; + my ($is_map,$is_recursive,$mapurl,$maplevel); + if ($caller eq 'overview') { + if ($middle=~/^(.+)\_\_\_\((all|rec)\)$/) { + $mapurl = $1; + $maplevel = $2; + $is_map = 1; + } + } elsif ($caller eq 'newoverview') { + if ($middle=~/^(.+)\_\_\_\((all)\)$/) { + $mapurl = $1; + $maplevel = $2; + $is_map = 1; + } + } + if ($is_map) { my $leveltitle = &mt('Folder/Map'); - if ($maplevel eq 'rec') { - $leveltitle = &mt('Recursive'); + my $title = &Apache::lonnet::gettitle($mapurl); + if (ref($hash_for_realm) eq 'HASH') { + if ($hash_for_realm->{$mapurl} eq '1') { + $title = &mt('Main Content'); + } } - $realm=''.$leveltitle.': '.&Apache::lonnet::gettitle($mapurl).'
('.$mapurl.')
'; + unless (($name eq 'hiddenresource') || ($name eq 'encrypturl')) { + if ($caller eq 'newoverview') { + my $altkey = $thiskey; + $altkey =~ s/\Q___(all)\E/___(rec)/; + if ((exists($$resourcedata{$altkey})) & (!exists($$resourcedata{$thiskey}))) { + $is_recursive = 1; + if ($$resourcedata{$altkey.'.type'}) { + $thistype=$$resourcedata{$altkey.'.type'}; + } + $showval = $$resourcedata{$altkey}; + } + } elsif (($caller eq 'overview') && ($maplevel eq 'rec')) { + $thiskey =~ s/\Q___(rec)\E/___(all)/; + $is_recursive = 1; + } + } + $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: '. @@ -3876,13 +5028,18 @@ sub listdata { # Ready to print # my $parmitem = &standard_parameter_names($name); - $r->print(&tablestart($readonly). + $r->print(&tablestart($readonly,$is_map). &Apache::loncommon::start_data_table_row(). ''.&mt($parmitem). ''); unless ($readonly) { + my $disabled; + if (($name eq 'availablestudent') && + (($showval eq '') || ($userscope))) { + $disabled = ' disabled="disabled"'; + } $r->print(''); + $thiskey.'"'.$disabled.' />'); } $r->print(''); $foundkeys++; @@ -3896,45 +5053,76 @@ sub listdata { $r->print( &Apache::lonhtmlcommon::date_setter('parmform', $jskey, - $$resourcedata{$thiskey}, + $showval, '',1,$state)); unless ($readonly) { $r->print( ''. - (($$resourcedata{$thiskey}!=0)?''. + (($showval!=0)?''. &mt('Shift all dates based on this date').'':''). - &date_sanity_info($$resourcedata{$thiskey}) + &date_sanity_info($showval) ); } } elsif ($thistype eq 'date_interval') { $r->print(&date_interval_selector($thiskey,$name, - $$resourcedata{$thiskey},$readonly)); + $showval,$readonly)); } elsif ($thistype =~ m/^string/) { + if ($name eq 'availablestudent') { + $readonly = 1; + } $r->print(&string_selector($thistype,$thiskey, - $$resourcedata{$thiskey},$name,$readonly)); + $showval,$name,$readonly)); } else { - $r->print(&default_selector($thiskey,$$resourcedata{$thiskey},$readonly)); + $r->print(&default_selector($thiskey,$showval,$readonly)); } unless ($readonly) { $r->print(''); } - $r->print(''.&Apache::loncommon::end_data_table_row()); + $r->print(''); + if ($is_map) { + if (($name eq 'encrypturl') || ($name eq 'hiddenresource')) { + $r->print('
'.&mt('Yes').'
'); + } else { + my ($disabled,$recon,$recoff); + if ($readonly) { + $disabled = ' disabled="disabled"'; + } + if ($is_recursive) { + $recon = ' checked="checked"'; + } else { + $recoff = ' checked="checked"'; + } + $r->print('
'. + '
'); + } + } + $r->print(&Apache::loncommon::end_data_table_row()); } } return $foundkeys; } +# Returns a string representing the interval, directly using form data matching the given key. +# The returned string may also include information related to proctored exams. +# Format: seconds['_done'[':'done button title':']['_proctor'['_'proctor key]]] +# +# @param {string} $key - suffix for form fields related to the interval +# @returns {string} 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$/) && @@ -3953,10 +5141,17 @@ sub get_date_interval_from_form { $seconds .= '_'.$env{'form.done_'.$key.'_proctorkey'}; } } + return if (!$numnotnull); return $seconds; } +# Returns HTML to enter a text value for a parameter. +# +# @param {string} $thiskey - parameter key +# @param {string} $showval - the current value +# @param {boolean} $readonly - true if the field should not be made editable +# @returns {string} sub default_selector { my ($thiskey, $showval, $readonly) = @_; my $disabled; @@ -3966,6 +5161,12 @@ sub default_selector { return ''; } +# Returns HTML to enter allow/deny rules related to IP addresses. +# +# @param {string} $thiskey - parameter key +# @param {string} $showval - the current value +# @param {boolean} $readonly - true if the fields should not be made editable +# @returns {string} sub string_ip_selector { my ($thiskey, $showval, $readonly) = @_; my %access = ( @@ -3994,7 +5195,7 @@ sub string_ip_selector { @{$access{'deny'}} = (''); } my ($disabled,$addmore); - if ($disabled) { + if ($readonly) { $disabled=' disabled="disabled"'; } else { $addmore = "\n".''; @@ -4026,6 +5227,257 @@ sub string_ip_selector { return $output; } +sub string_deeplink_selector { + my ($thiskey, $showval, $readonly) = @_; + 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', + 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'], + 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', + details => 'Listed (unlinked) in both', + datestatus => 'Listed (unlinked) inc. status in both', + res => 'resource only', + map => 'enclosing map/folder', + rec => 'recursive map/folder', + 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 ( + ltic => 'Select Launcher', + ltid => 'Select Launcher', + colls => 'Select', + ); + if ($showval =~ /,/) { + %values=(); + @current = split(/,/,$showval); + ($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{'protect'} = 'none'; + $defaults{'menus'} = '0'; + $defaults{'target'} = '_top'; + $defaults{'exit'} = 'yes'; + } + my $disabled; + if ($readonly) { + $disabled=' disabled="disabled"'; + } + 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'}, + 'linkprot'); + foreach my $item (keys(%lti)) { + if (($item =~ /^\d+$/) && (ref($lti{$item}) eq 'HASH')) { + $domlti{$item} = $lti{$item}{'name'}; + } + } + if ($env{'course.'.$env{'request.course.id'}.'.menucollections'}) { + foreach my $item (split(/;/,$env{'course.'.$env{'request.course.id'}.'.menucollections'})) { + my ($num,$value) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + push(@possmenus,$num); + } + } + } + + 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 ($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 '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 { + $blankcheck = ' selected="selected"'; + } + if ($option eq 'ltid') { + @possibles = keys(%domlti); + } elsif ($option eq 'ltic') { + @possibles = keys(%crslti); + } else { + @possibles = @possmenus; + } + $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 .= '

'.&mt('Button text').': '. + '
'; + } + } else { + my $selected = $values{$item}; + my $defsel; + if ($selected eq '') { + $defsel = ' selected="selected"'; + } + $output .= ''; + } + $output .= '
'."\n"; + if ($table eq 'upper') { + $output .= '
'; + } + } + return $output; +} + { # block using some constants related to parameter types (overview mode) @@ -4059,8 +5511,11 @@ my %strings = ['no','No']], 'string_ip' => [['_allowfrom_','Hostname(s), or IP(s) from which access is allowed'], - ['_denyfrom_',], 'Hostname(s) or IP(s) from which access is disallowed'], - ); + ['_denyfrom_','Hostname(s) or IP(s) from which access is disallowed']], + 'string_deeplink' + => [['on','Set choices for link protection, resource listing, access scope, shown menu items, embedding, and exit link']], + ); + my %stringmatches = ( 'string_lenient' @@ -4068,6 +5523,8 @@ my %stringmatches = ( 'string_ip' => [['_allowfrom_','[^\!]+'], ['_denyfrom_','\!']], + 'string_deeplink' + => [['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 = ( @@ -4077,8 +5534,14 @@ my %stringtypes = ( discussvote => 'string_discussvote', examcode => 'string_examcode', acc => 'string_ip', + deeplink => 'string_deeplink', ); +# Returns the possible values and titles for a given string type, or undef if there are none. +# Used by courseprefs. +# +# @param {string} $string_type - a parameter type for strings +# @returns {array reference} - 2D array, containing values and English titles sub standard_string_options { my ($string_type) = @_; if (ref($strings{$string_type}) eq 'ARRAY') { @@ -4087,6 +5550,10 @@ sub standard_string_options { return; } +# Returns regular expressions to match kinds of string types, or undef if there are none. +# +# @param {string} $string_type - a parameter type for strings +# @returns {array reference} - 2D array, containing regular expression names and regular expressions sub standard_string_matches { my ($string_type) = @_; if (ref($stringmatches{$string_type}) eq 'ARRAY') { @@ -4095,6 +5562,10 @@ sub standard_string_matches { return; } +# Returns a parameter type for a given parameter with a string type, or undef if not known. +# +# @param {string} $name - parameter name +# @returns {string} sub get_stringtype { my ($name) = @_; if (exists($stringtypes{$name})) { @@ -4103,6 +5574,14 @@ sub get_stringtype { return; } +# Returns HTML to edit a string parameter. +# +# @param {string} $thistype - parameter type +# @param {string} $thiskey - parameter key +# @param {string} $showval - parameter current value +# @param {string} $name - parameter name +# @param {boolean} $readonly - true if the values should not be made editable +# @returns {string} sub string_selector { my ($thistype, $thiskey, $showval, $name, $readonly) = @_; @@ -4115,6 +5594,7 @@ sub string_selector { ($thistype eq 'string_lenient') || ($thistype eq 'string_discussvote') || ($thistype eq 'string_ip') || + ($thistype eq 'string_deeplink') || ($name eq 'retrypartial')) { my ($got_chostname,$chostname,$cmajor,$cminor); foreach my $possibilities (@{ $strings{$thistype} }) { @@ -4153,6 +5633,8 @@ sub string_selector { if ($thistype eq 'string_ip') { return &string_ip_selector($thiskey,$showval,$readonly); + } elsif ($thistype eq 'string_deeplink') { + return &string_deeplink_selector($thiskey,$showval,$readonly); } my ($result,$disabled); @@ -4268,6 +5750,10 @@ my %intervaltypes = ( interval => 'date_interval', ); +# Returns regular expressions to match kinds of interval type, or undef if there are none. +# +# @param {string} $interval_type - a parameter type for intervals +# @returns {array reference} - 2D array, containing regular expression names and regular expressions sub standard_interval_matches { my ($interval_type) = @_; if (ref($intervalmatches{$interval_type}) eq 'ARRAY') { @@ -4276,6 +5762,10 @@ sub standard_interval_matches { return; } +# Returns a parameter type for a given parameter with an interval type, or undef if not known. +# +# @param {string} $name - parameter name +# @returns {string} sub get_intervaltype { my ($name) = @_; if (exists($intervaltypes{$name})) { @@ -4284,6 +5774,11 @@ sub get_intervaltype { return; } +# Returns the possible values and titles for a given interval type, or undef if there are none. +# Used by courseprefs. +# +# @param {string} $interval_type - a parameter type for intervals +# @returns {array reference} - 2D array, containing values and English titles sub standard_interval_options { my ($interval_type) = @_; if (ref($intervals{$interval_type}) eq 'ARRAY') { @@ -4292,6 +5787,13 @@ sub standard_interval_options { return; } +# Returns HTML to edit a date interval parameter. +# +# @param {string} $thiskey - parameter key +# @param {string} $name - parameter name +# @param {string} $showval - parameter current value +# @param {boolean} $readonly - true if the values should not be made editable +# @returns {string} sub date_interval_selector { my ($thiskey, $name, $showval, $readonly) = @_; my ($result,%skipval); @@ -4342,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); @@ -4349,42 +5856,47 @@ 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; + if ($readonly) { + $disabled = ' disabled="disabled"'; + } $result .= '
'.&mt('Include "done" button'). - '
'. + 'name="done_'.$thiskey.'_proctorkey" value="'.&HTML::Entities::encode($currproctorkey,'"<>&').'"'.$disabled.' />

'. ''.&mt('Button text').': '. - '&').'" />'; + '&').'"'.$disabled.' />
'; } } unless ($readonly) { @@ -4393,6 +5905,16 @@ sub date_interval_selector { return $result; } +# Returns HTML with a warning if a parameter requires a more recent version of LON-CAPA. +# +# @param {string} $name - parameter name +# @param {string} $namematch - parameter level name (recognized: resourcelevel|maplevel|maplevelrecurse|courselevel) +# @param {string} $value - parameter value +# @param {string} $chostname - course server name +# @param {integer} $cmajor - major version number +# @param {integer} $cminor - minor version number +# @param {string} $needsrelease - release version needed (major.minor) +# @returns {string} sub oldversion_warning { my ($name,$namematch,$value,$chostname,$cmajor,$cminor,$needsrelease) = @_; my $standard_name = &standard_parameter_names($name); @@ -4468,14 +5990,28 @@ sub oldversion_warning { } # end of block using some constants related to parameter types -# -# Shift all start and end dates by $shift -# +# Shifts all start and end dates in the current course by $shift. +# +# @param {integer} $shift - time to shift, in seconds +# @returns {string} - error name or 'ok' sub dateshift { - my ($shift)=@_; + my ($shift,$numchanges)=@_; my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $sec = $env{'request.course.sec'}; + my $secgrpregex; + if ($sec ne '') { + my @groups; + if ($env{'request.course.groups'} ne '') { + @groups = split(/:/,$env{'request.course.groups'}); + } + if (@groups) { + $secgrpregex = '(?:'.join('|',($sec,@groups)).')'; + } else { + $secgrpregex = $sec; + } + } my %data=&Apache::lonnet::dump('resourcedata',$dom,$crs); # ugly retro fix for broken version of types foreach my $key (keys(%data)) { @@ -4490,7 +6026,11 @@ sub dateshift { # go through all parameters and look for dates foreach my $key (keys(%data)) { if ($data{$key.'.type'}=~/^date_(start|end)$/) { + if ($sec ne '') { + next unless ($key =~ /^$env{'request.course.id'}\.\[$secgrpregex\]\./); + } my $newdate=$data{$key}+$shift; + $$numchanges ++; $storecontent{$key}=$newdate; } } @@ -4503,12 +6043,19 @@ sub dateshift { return $reply; } +# Overview mode UI to edit course parameters. +# +# @param {Apache2::RequestRec} $r - the Apache request sub newoverview { - my ($r) = @_; + my ($r,$parm_permission) = @_; my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; my $crstype = $env{'course.'.$env{'request.course.id'}.'.type'}; + my $readonly = 1; + if ($parm_permission->{'edit'}) { + undef($readonly); + } &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=setoverview', text=>"Overview Mode"}); @@ -4525,6 +6072,7 @@ sub newoverview { &validateparms_js()."\n". &ipacc_boxes_js()."\n". &done_proctor_js()."\n". + &deeplink_js()."\n". '// ]]> '; @@ -4589,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); @@ -4601,17 +6149,15 @@ 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('
'); $r->print('
'); $r->print('
'); - &displaymenu($r,\%allparms,\@pscat,\%keyorder); # FIXME: wrong parameters, could make keysindisplayorderCategory crash because $keyorder is undefined + &displaymenu($r,\%allparms,\@pscat,\%keyorder); $r->print(&Apache::lonhtmlcommon::start_pick_box()); $r->print(&Apache::lonhtmlcommon::row_title(&mt('Select Parts to View'))); my $sectionselector = §ionmenu(\@selected_sections); @@ -4642,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('

'); @@ -4667,15 +6213,39 @@ ENDOVER # List data - &listdata($r,$resourcedata,$listdata,$sortorder,'newoverview'); + 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(). - ((($env{'form.store'}) || ($env{'form.dis'}))?'

':''). - ''); + $r->print(&tableend()); + unless ($readonly) { + $r->print( ((($env{'form.store'}) || ($env{'form.dis'}))?'

':'') ); + } + $r->print(''); &endSettingsScreen($r); $r->print(&Apache::loncommon::end_page()); } +# Fills $listdata with parameter information. +# Keys use the format course id.[section id].part.name and course id.[section id].part.name.type. +# The non-type value is always 1. +# +# @param {string} $cat - parameter name +# @param {string} $pschp - selected map pc, or 'all' +# @param {string} $parmlev - selected level value (full|map|general), or '' +# @param {hash reference} $listdata - the parameter data that will be modified +# @param {array reference} $psprt - selected parts +# @param {array reference} $selections - selected sections +# @param {hash reference} $defkeytype - hash parameter name -> parameter type +# @param {hash reference} $allmaps - hash map pc -> map src +# @param {array reference} $ids - resource and map ids +# @param {hash reference} $symbp - hash map pc or resource/map id -> map src.'___(all)' or resource symb sub secgroup_lister { my ($cat,$pschp,$parmlev,$listdata,$psprt,$selections,$defkeytype,$allmaps,$ids,$symbp) = @_; foreach my $item (@{$selections}) { @@ -4696,9 +6266,6 @@ sub secgroup_lister { my $newparmkey=$rootparmkey.'.'.$$allmaps{$mapid}.'___(all).'.$part.'.'.$cat; $$listdata{$newparmkey}=1; $$listdata{$newparmkey.'.type'}=$$defkeytype{$cat}; - $newparmkey=$rootparmkey.'.'.$$allmaps{$mapid}.'___(rec).'.$part.'.'.$cat; - $$listdata{$newparmkey}=1; - $$listdata{$newparmkey.'.type'}=$$defkeytype{$cat}; } } else { # resource-level parameter @@ -4714,18 +6281,26 @@ sub secgroup_lister { } } -# Display all existing parameter settings. +# UI to edit parameter settings starting with a list of all existing parameters. +# (called by setoverview action) +# +# @param {Apache2::RequestRec} $r - the Apache request sub overview { - my ($r) = @_; + my ($r,$parm_permission) = @_; my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; my $crstype = $env{'course.'.$env{'request.course.id'}.'.type'}; + my $readonly = 1; + if ($parm_permission->{'edit'}) { + undef($readonly); + } my $js = ''."\n"; &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=setoverview', @@ -4742,7 +6317,9 @@ sub overview { # Store modified - &storedata($r,$crs,$dom); + unless ($readonly) { + &storedata($r,$crs,$dom); + } # Read modified data @@ -4751,17 +6328,31 @@ sub overview { my $sortorder=$env{'form.sortorder'}; unless ($sortorder) { $sortorder='realmstudent'; } - &sortmenu($r,$sortorder); + &sortmenu($r,$sortorder,'overview'); + + my $submitbutton = ''; + + if ($readonly) { + $r->print('

'.$submitbutton.'

'); + } # List data - my $foundkeys=&listdata($r,$resourcedata,$resourcedata,$sortorder,'overview',$classlist); - $r->print(&tableend().'

'. - ($foundkeys?'':''.&mt('There are no parameters.').'').'

'. - &Apache::loncommon::end_page()); + my $foundkeys=&listdata($r,$resourcedata,$resourcedata,$sortorder,'overview',$classlist,$readonly); + $r->print(&tableend().'

'); + if ($foundkeys) { + unless ($readonly) { + $r->print('

'.$submitbutton.'

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

'.&mt('There are no parameters.').'

'); + } + $r->print(''.&Apache::loncommon::end_page()); } # Unused sub. +# +# @param {Apache2::RequestRec} $r - the Apache request sub clean_parameters { my ($r) = @_; my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; @@ -4821,10 +6412,8 @@ ENDOVER $r->print(&mt('All users')); } elsif ($data{'scope_type'} eq 'user') { $r->print(&mt('User: [_1]',join(':',@{$data{'scope'}}))); - } elsif ($data{'scope_type'} eq 'section') { - $r->print(&mt('Section: [_1]',$data{'scope'})); - } elsif ($data{'scope_type'} eq 'group') { - $r->print(&mt('Group: [_1]',$data{'scope'})); + } elsif ($data{'scope_type'} eq 'secgroup') { + $r->print(&mt('Group/Section: [_1]',$data{'scope'})); } $r->print('
'); if ($data{'realm_type'} eq 'all') { @@ -4850,15 +6439,30 @@ ENDOVER $r->print(&Apache::loncommon::end_page()); } -# Overview mode, UI to shift all dates. +# UI to shift all dates (called by dateshift1 action). +# Used by overview mode. +# +# @param {Apache2::RequestRec} $r - the Apache request sub date_shift_one { my ($r) = @_; my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; my $crstype = $env{'course.'.$env{'request.course.id'}.'.type'}; - + my $sec = $env{'request.course.sec'}; &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=dateshift1&timebase='.$env{'form.timebase'}, text=>"Shifting Dates"}); + my $submit_text = &mt('Shift all dates accordingly'); + if ($sec ne '') { + my @groups; + if ($env{'request.course.groups'} ne '') { + @groups = split(/:/,$env{'request.course.groups'}); + } + if (@groups) { + $submit_text = &mt("Shift dates set just for your section/group(s), accordingly"); + } else { + $submit_text = &mt("Shift dates set just for your section, accordingly"); + } + } my $start_page=&Apache::loncommon::start_page('Shift Dates'); my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Shift'); $r->print($start_page.$breadcrumbs); @@ -4874,16 +6478,19 @@ sub date_shift_one { ''. ''. ''. - ''); + ''); &endSettingsScreen($r); $r->print(&Apache::loncommon::end_page()); } -# Overview mode, UI to shift all dates (second form). +# UI to shift all dates (second form). +# +# @param {Apache2::RequestRec} $r - the Apache request sub date_shift_two { my ($r) = @_; my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $sec = $env{'request.course.sec'}; my $crstype = $env{'course.'.$env{'request.course.id'}.'.type'}; &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=dateshift1&timebase='.$env{'form.timebase'}, text=>"Shifting Dates"}); @@ -4892,14 +6499,47 @@ sub date_shift_two { $r->print($start_page.$breadcrumbs); &startSettingsScreen($r,'parmset',$crstype); my $timeshifted=&Apache::lonhtmlcommon::get_date_from_form('timeshifted'); - $r->print('

'.&mt('Shift Dates').'

'. - '

'.&mt('Shifting all dates such that [_1] becomes [_2]', - &Apache::lonlocal::locallocaltime($env{'form.timebase'}), - &Apache::lonlocal::locallocaltime($timeshifted)).'

'); + $r->print('

'.&mt('Shift Dates').'

'); + if ($sec ne '') { + my @groups; + if ($env{'request.course.groups'} ne '') { + @groups = split(/:/,$env{'request.course.groups'}); + } + if (@groups) { + $r->print('

'. + &mt("Shift dates set just for your section/group(s), such that [_1] becomes [_2]", + &Apache::lonlocal::locallocaltime($env{'form.timebase'}), + &Apache::lonlocal::locallocaltime($timeshifted)). + '

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

'. + &mt("Shift dates set just for your section, such that [_1] becomes [_2]", + &Apache::lonlocal::locallocaltime($env{'form.timebase'}), + &Apache::lonlocal::locallocaltime($timeshifted)). + '

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

'.&mt('Shifting all dates such that [_1] becomes [_2]', + &Apache::lonlocal::locallocaltime($env{'form.timebase'}), + &Apache::lonlocal::locallocaltime($timeshifted)). + '

'); + } my $delta=$timeshifted-$env{'form.timebase'}; - &dateshift($delta); + my $numchanges = 0; + my $result = &dateshift($delta,\$numchanges); + if ($result eq 'ok') { + $r->print( + &Apache::lonhtmlcommon::confirm_success(&mt('Completed shifting of [quant,_1,date setting]', + $numchanges))); + } elsif ($result eq 'con_delayed') { + $r->print( + &Apache::lonhtmlcommon::confirm_success(&mt('Queued shifting of [quant,_1,date setting]', + $numchanges))); + } else { + $r->print( + &Apache::lonhtmlcommon::confirm_success(&mt('An error occurred attempting to shift dates'),1)); + } $r->print( - &Apache::lonhtmlcommon::confirm_success(&mt('Done')). '

'. &Apache::lonhtmlcommon::actionbox( [''.&mt('Content and Problem Settings').''])); @@ -4907,11 +6547,18 @@ sub date_shift_two { $r->print(&Apache::loncommon::end_page()); } +# Returns the different components of a resourcedata key. +# Keys: scope_type, scope, realm_type, realm, realm_title, +# realm_exists, parameter_part, parameter_name. +# Was used by clean_parameters (which is unused). +# +# @param {string} $key - the parameter key +# @returns {hash} sub parse_key { my ($key) = @_; my %data; my ($middle,$part,$name)= - ($key=~/^$env{'request.course.id'}\.(?:(.+)\.)*([\w\s]+)\.(\w+)$/); + ($key=~/^$env{'request.course.id'}\.(?:(.+)\.)*([\w\s\-]+)\.(\w+)$/); $data{'scope_type'} = 'all'; if ($middle=~/^\[(.*)\]/) { $data{'scope'} = $1; @@ -4919,8 +6566,7 @@ sub parse_key { $data{'scope_type'} = 'user'; $data{'scope'} = [$1,$2]; } else { - #FIXME check for group scope - $data{'scope_type'} = 'section'; + $data{'scope_type'} = 'secgroup'; } $middle=~s/^\[(.*)\]//; } @@ -4947,6 +6593,7 @@ sub parse_key { } +# Calls loncommon::start_page with the "Settings" title. sub header { return &Apache::loncommon::start_page('Settings'); } @@ -4957,6 +6604,10 @@ sub header { # MAIN MENU ################################################## +# Content and problem settings main menu. +# +# @param {Apache2::RequestRec} $r - the Apache request +# @param {boolean} $parm_permission - true if the user has permission to edit the current course or section sub print_main_menu { my ($r,$parm_permission)=@_; # @@ -4976,35 +6627,73 @@ ENDMAINFORMHEAD my $vgr = &Apache::lonnet::allowed('vgr',$env{'request.course.id'}); my $mgr = &Apache::lonnet::allowed('mgr',$env{'request.course.id'}); my $dcm = &Apache::lonnet::allowed('dcm',$env{'request.course.id'}); + my $vcb = &Apache::lonnet::allowed('vcb',$env{'request.course.id'}); + my $vpa = &Apache::lonnet::allowed('vpa',$env{'request.course.id'}); if ((!$dcm) && ($env{'request.course.sec'} ne '')) { $dcm = &Apache::lonnet::allowed('dcm',$env{'request.course.id'}. '/'.$env{'request.course.sec'}); } - + if ((!$vcb) && ($env{'request.course.sec'} ne '')) { + $vcb = &Apache::lonnet::allowed('vcb',$env{'request.course.id'}. + '/'.$env{'request.course.sec'}); + } + my (%linktext,%linktitle,%url); + if ($parm_permission->{'edit'}) { + %linktext = ( + newoverview => 'Edit Resource Parameters - Overview Mode', + settable => 'Edit Resource Parameters - Table Mode', + setoverview => 'Modify Resource Parameters - Overview Mode', + ); + %linktitle = ( + newoverview => 'Set/Modify resource parameters in overview mode.', + settable => 'Set/Modify resource parameters in table mode.', + setoverview => 'Set/Modify existing resource parameters in overview mode.', + ); + } else { + %linktext = ( + newoverview => 'View Resource Parameters - Overview Mode', + settable => 'View Resource Parameters - Table Mode', + setoverview => 'View Resource Parameters - Overview Mode', + ); + %linktitle = ( + newoverview => 'Display resource parameters in overview mode.', + settable => 'Display resource parameters in table mode.', + setoverview => 'Display existing resource parameters in overview mode.', + ); + } + if ($mgr) { + $linktext{'resettimes'} = 'Reset Student Access Times'; + $linktitle{'resettimes'} = "Reset access times for folders/maps, resources or the $lc_crstype."; + $url{'resettimes'} = '/adm/helper/resettimes.helper'; + } elsif ($vgr) { + $linktext{'resettimes'} = 'Display Student Access Times', + $linktitle{'resettimes'} = "Display access times for folders/maps, resources or the $lc_crstype.", + $url{'resettimes'} = '/adm/accesstimes'; + } my @menu = ( { categorytitle=>"Content Settings for this $crstype", items => [ { linktext => 'Portfolio Metadata', url => '/adm/parmset?action=setrestrictmeta', - permission => $parm_permission, + permission => $parm_permission->{'setrestrictmeta'}, linktitle => "Restrict metadata for this $lc_crstype." , icon =>'contact-new.png' , }, - { linktext => 'Reset Student Access Times', - url => '/adm/helper/resettimes.helper', - permission => $mgr, - linktitle => "Reset access times for folders/maps, resources or the $lc_crstype." , - icon => 'start-here.png' , + { linktext => $linktext{'resettimes'}, + url => $url{'resettimes'}, + permission => ($vgr || $mgr), + linktitle => $linktitle{'resettimes'}, + icon => 'start-here.png', }, { linktext => 'Blocking Communication/Resource Access', url => '/adm/setblock', - permission => $dcm, + permission => ($vcb || $dcm), linktitle => 'Configure blocking of communication/collaboration and access to resources during an exam', icon => 'comblock.png', }, { linktext => 'Set Parameter Setting Default Actions', url => '/adm/parmset?action=setdefaults', - permission => $parm_permission, + permission => $parm_permission->{'setdefaults'}, linktitle =>'Set default actions for parameters.' , icon => 'folder-new.png' , }]}, @@ -5012,37 +6701,37 @@ ENDMAINFORMHEAD items => [ { linktext => 'Edit Resource Parameters - Helper Mode', url => '/adm/helper/parameter.helper', - permission => $parm_permission, + permission => $parm_permission->{'helper'}, linktitle =>'Set/Modify resource parameters in helper mode.' , icon => 'dialog-information.png' , #help => 'Parameter_Helper', }, - { linktext => 'Edit Resource Parameters - Overview Mode', + { linktext => $linktext{'newoverview'}, url => '/adm/parmset?action=newoverview', - permission => $parm_permission, - linktitle =>'Set/Modify resource parameters in overview mode.' , - icon => 'edit-find.png' , + permission => $parm_permission->{'newoverview'}, + linktitle => $linktitle{'newoverview'}, + icon => 'edit-find.png', #help => 'Parameter_Overview', }, - { linktext => 'Edit Resource Parameters - Table Mode', + { linktext => $linktext{'settable'}, url => '/adm/parmset?action=settable', - permission => $parm_permission, - linktitle =>'Set/Modify resource parameters in table mode.' , - icon => 'edit-copy.png' , + permission => $parm_permission->{'settable'}, + linktitle => $linktitle{'settable'}, + icon => 'edit-copy.png', #help => 'Table_Mode', }]}, { categorytitle => 'Existing Parameter Settings for Resources', items => [ - { linktext => 'Modify Resource Parameters - Overview Mode', + { linktext => $linktext{'setoverview'}, url => '/adm/parmset?action=setoverview', - permission => $parm_permission, - linktitle =>'Set/Modify existing resource parameters in overview mode.' , - icon => 'preferences-desktop-wallpaper.png' , + permission => $parm_permission->{'setoverview'}, + linktitle => $linktitle{'setoverview'}, + icon => 'preferences-desktop-wallpaper.png', #help => 'Parameter_Overview', }, { linktext => 'Change Log', url => '/adm/parmset?action=parameterchangelog', - permission => $parm_permission, + permission => $parm_permission->{'parameterchangelog'}, linktitle =>"View parameter and $lc_crstype blog posting/user notification change log." , icon => 'document-properties.png', }]} @@ -5060,6 +6749,13 @@ ENDMAINFORMHEAD # PORTFOLIO METADATA ################################################## +# Prints HTML to edit an item of portfolio metadata. The HTML contains several td elements (no tr). +# It looks like field titles are not localized. +# +# @param {Apache2::RequestRec} $r - the Apache request +# @param {string} $field_name - metadata field name +# @param {string} $field_text - metadata field title, in English unless manually added +# @param {boolean} $added_flag - true if the field was manually added sub output_row { my ($r, $field_name, $field_text, $added_flag) = @_; my $output; @@ -5117,6 +6813,9 @@ sub output_row { # UI to order portfolio metadata fields. +# Currently useless because addmetafield does not work. +# +# @param {Apache2::RequestRec} $r - the Apache request sub order_meta_fields { my ($r)=@_; my $idx = 1; @@ -5207,15 +6906,21 @@ sub order_meta_fields { } +# Returns HTML with a Continue button redirecting to the initial portfolio metadata screen. +# @returns {string} sub continue { my $output; $output .= '
'; $output .= ''; - $output .= ''; + $output .= ''; return ($output); } +# UI to add a metadata field. +# Currenly does not work because of an HTML error (the field is not visible). +# +# @param {Apache2::RequestRec} $r - the Apache request sub addmetafield { my ($r)=@_; &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=addmetadata', @@ -5235,7 +6940,10 @@ sub addmetafield { my $put_result = &Apache::lonnet::put('environment', {'metadata.'.$meta_field.'.options'=>$options},$dom,$crs); - $r->print('Undeleted Metadata Field '.$env{'course.'.$env{'request.course.id'}.'.metadata.'.$meta_field.'.added'}." with result ".$put_result.'
'); + $r->print(&mt('Undeleted Metadata Field [_1] with result [_2]', + ''.$env{'course.'.$env{'request.course.id'}.'.metadata.'.$meta_field.'.added'}. + '',$put_result). + '
'); } $r->print(&continue()); } elsif (exists($env{'form.fieldname'})) { @@ -5247,30 +6955,40 @@ sub addmetafield { {'metadata.'.$meta_field.'.values'=>"", 'metadata.'.$meta_field.'.added'=>"$display_field", 'metadata.'.$meta_field.'.options'=>""},$dom,$crs); - $r->print('Added new Metadata Field '.$env{'form.fieldname'}." with result ".$put_result.'
'); + $r->print(&mt('Added new Metadata Field [_1] with result [_2]', + ''.$env{'form.fieldname'}.'',$put_result). + '
'); $r->print(&continue()); } else { my $fields = &get_deleted_meta_fieldnames($env{'request.course.id'}); if ($fields) { - $r->print('You may undelete previously deleted fields.
Check those you wish to undelete and click Undelete.
'); + $r->print(&mt('You may undelete previously deleted fields.'). + '
'. + &mt('Check those you wish to undelete and click Undelete.'). + '
'); $r->print(''); foreach my $key(keys(%$fields)) { - $r->print(''.$$fields{$key}.'
print('
print(''); + $r->print(''); $r->print('
'); } - $r->print('
Or you may enter a new metadata field name.
print('
'. + &mt('[_1]Or[_2] you may enter a new metadata field name.', + '',''). + ''); $r->print('
'); - $r->print(''); + $r->print(''); + $r->print('
'); } - $r->print(''); &endSettingsScreen($r); } # Display or save portfolio metadata. +# +# @param {Apache2::RequestRec} $r - the Apache request sub setrestrictmeta { my ($r)=@_; my $next_meta; @@ -5287,6 +7005,11 @@ sub setrestrictmeta { &startSettingsScreen($r,'parmset',$crstype); my $key_base = $env{'course.'.$env{'request.course.id'}.'.'}; my $save_field = ''; + my %lt = &Apache::lonlocal::texthash( + addm => 'Add Metadata Field', + ordm => 'Order Metadata Fields', + save => 'Save', + ); if ($env{'form.restrictmeta'}) { foreach my $field (sort(keys(%env))) { if ($field=~m/^form.(.+)_(.+)$/) { @@ -5325,28 +7048,25 @@ sub setrestrictmeta { my %metadata_fields = &Apache::lonmeta::fieldnames('portfolio'); # Now get possible added metadata fields my $added_metadata_fields = &get_added_meta_fieldnames($env{'request.course.id'}); - my $row_alt = 1; $output .= &Apache::loncommon::start_data_table(); foreach my $field (sort(keys(%metadata_fields))) { if ($field ne 'courserestricted') { - $row_alt = $row_alt ? 0 : 1; - $output.= &output_row($r, $field, $metadata_fields{$field}); + $output.= &output_row($r,$field,$metadata_fields{$field}); } } my $buttons = (< +
- +

- + ENDButtons my $added_flag = 1; foreach my $field (sort(keys(%$added_metadata_fields))) { - $row_alt = $row_alt ? 0 : 1; - $output.= &output_row($r, $field, $$added_metadata_fields{$field},$added_flag, $row_alt); + $output.= &output_row($r,$field,$$added_metadata_fields{$field},$added_flag); } $output .= &Apache::loncommon::end_data_table(); $r->print(< field title (not localized) sub get_added_meta_fieldnames { my ($cid) = @_; my %fields; @@ -5375,6 +7099,10 @@ sub get_added_meta_fieldnames { } +# Returns metadata fields that have been manually added and deleted. +# +# @param {string} $cid - course id +# @returns {hash reference} - hash field name -> field title (not localized) sub get_deleted_meta_fieldnames { my ($cid) = @_; my %fields; @@ -5396,6 +7124,8 @@ sub get_deleted_meta_fieldnames { ################################################## # UI to change parameter setting default actions +# +# @param {Apache2::RequestRec} $r - the Apache request sub defaultsetter { my ($r) = @_; @@ -5489,8 +7219,8 @@ sub defaultsetter { push @datedisplay,&mt('Automatically set earlier than ').$tempkey; } } -$r->print(&mt('Manual setting rules apply to all interfaces.').'
'. - &mt('Automatic setting rules apply to table mode interfaces only.')); + $r->print(&mt('Manual setting rules apply to all interfaces.').'
'. + &mt('Automatic setting rules apply to table mode interfaces only.')); $r->print("\n".&Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(). "".&mt('Rule for parameter').''. @@ -5534,7 +7264,7 @@ $r->print(&mt('Manual setting rules appl $lt{'hours'}
$lt{'min'}
$lt{'sec'} - ENDINPUTDATE +ENDINPUTDATE } elsif ($defkeytype{$tempkey} eq 'string_yesno') { my $yeschecked=''; my $nochecked=''; @@ -5544,7 +7274,7 @@ $r->print(&mt('Manual setting rules appl $r->print(< $lt{'yes'}
- ENDYESNO +ENDYESNO } else { $r->print(''); } @@ -5562,15 +7292,33 @@ $r->print(&mt('Manual setting rules appl # PARAMETER CHANGES LOG ################################################## +# Returns some info for a parameter log entry. +# Returned entries: +# $realm - HTML title for the parameter level and resource +# $section - parameter section +# $name - parameter name +# $part - parameter part +# $what - $part.'.'.$name +# $middle - resource symb ? +# $uname - user name (same as given) +# $udom - user domain (same as given) +# $issection - section or group name +# $realmdescription - title for the parameter level and resource (without using HTML) +# +# @param {string} $key - parameter log key +# @param {string} $uname - user name +# @param {string} $udom - user domain +# @param {boolean} $typeflag - .type log entry +# @returns {Array} sub components { - my ($key,$uname,$udom,$exeuser,$exedomain,$typeflag)=@_; + my ($key,$uname,$udom,$typeflag)=@_; if ($typeflag) { $key=~s/\.type$//; } my ($middle,$part,$name)= - ($key=~/^$env{'request.course.id'}\.(?:(.+)\.)*([\w\s]+)\.(\w+)$/); + ($key=~/^$env{'request.course.id'}\.(?:(.+)\.)*([\w\s\-]+)\.(\w+)$/); my $issection; my $section=&mt('All Students'); @@ -5610,11 +7358,12 @@ sub components { $what,$middle,$uname,$udom,$issection,$realmdescription); } -my %standard_parms; -my %standard_parms_types; +my %standard_parms; # hash parameter name -> parameter title (not localized) +my %standard_parms_types; # hash parameter name -> parameter type +# Reads parameter info from packages.tab into %standard_parms. sub load_parameter_names { - open(my $config,"<$Apache::lonnet::perlvar{'lonTabDir'}/packages.tab"); + open(my $config,"<","$Apache::lonnet::perlvar{'lonTabDir'}/packages.tab"); while (my $configline=<$config>) { if ($configline !~ /\S/ || $configline=~/^\#/) { next; } chomp($configline); @@ -5629,8 +7378,13 @@ sub load_parameter_names { close($config); $standard_parms{'int_pos'} = 'Positive Integer'; $standard_parms{'int_zero_pos'} = 'Positive Integer or Zero'; + $standard_parms{'scoreformat'} = 'Format for display of score'; } +# Returns a parameter title for standard parameters, the name for others. +# +# @param {string} $name - parameter name +# @returns {string} sub standard_parameter_names { my ($name)=@_; if (!%standard_parms) { @@ -5643,6 +7397,10 @@ sub standard_parameter_names { } } +# Returns a parameter type for standard parameters, undef for others. +# +# @param {string} $name - parameter name +# @returns {string} sub standard_parameter_types { my ($name)=@_; if (!%standard_parms_types) { @@ -5654,6 +7412,10 @@ sub standard_parameter_types { return; } +# Returns a parameter level title (not localized) from the parameter level name. +# +# @param {string} $name - parameter level name (recognized: resourcelevel|maplevel|maplevelrecurse|courselevel) +# @returns {string} sub standard_parameter_levels { my ($name)=@_; my %levels = ( @@ -5669,11 +7431,13 @@ sub standard_parameter_levels { } # Display log for parameter changes, blog postings, user notification changes. +# +# @param {Apache2::RequestRec} $r - the Apache request sub parm_change_log { - my ($r)=@_; + my ($r,$parm_permission)=@_; my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - my $crstype = $env{'course.'.$env{'request.course.id'}.'.type'} + my $crstype = $env{'course.'.$env{'request.course.id'}.'.type'}; &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=settable', text=>"Parameter Change Log"}); my $js = '