--- loncom/interface/loncommon.pm 2022/06/11 14:51:49 1.1384 +++ loncom/interface/loncommon.pm 2023/03/27 18:41:04 1.1402 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.1384 2022/06/11 14:51:49 raeburn Exp $ +# $Id: loncommon.pm,v 1.1402 2023/03/27 18:41:04 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -72,6 +72,7 @@ use Apache::lonuserstate(); use Apache::courseclassifier(); use LONCAPA qw(:DEFAULT :match); use LONCAPA::LWPReq; +use LONCAPA::map(); use HTTP::Request; use DateTime::TimeZone; use DateTime::Locale; @@ -957,8 +958,8 @@ ENDSCRT } sub select_timezone { - my ($name,$selected,$onchange,$includeempty,$disabled)=@_; - my $output=''."\n"; if ($includeempty) { $output .= '\n"; } $result .= "\n"; - my %select2 = %{$hashref->{$firstdefault}->{'select2'}}; + my %select2; + if (ref($hashref->{$firstdefault}) eq 'HASH') { + if (ref($hashref->{$firstdefault}->{'select2'}) eq 'HASH') { + %select2 = %{$hashref->{$firstdefault}->{'select2'}}; + } + } $result .= $middletext; $result .= "'. + ''; + if ($files{'/'}) { + $output .= ''."\n"; + } + foreach my $key (sort { lc($a) cmp lc($b) } (keys(%files))) { + next if ($key eq '/'); + $output .= ''."\n"; + } + $output .= '
'."\n". + $lt{'fnam'}.': '."\n". + ''; + return ($numdirs,$output); +} + +sub show_crsfiles_js { + my $excluderef = &Apache::lonnet::priv_exclude(); + my $se = &js_escape(&mt('Select')); + my $exclude; + if (ref($excluderef) eq 'HASH') { + $exclude = join(',', map { &js_escape($_); } sort(keys(%{$excluderef}))); + } + my $js = <<"END"; + + + function populateCrsSelects (form,dirsel,filesel,exc,include,setdir,setfile,recurse,nonemptydir,addtopdir) { + var relpath = ''; + if ((setfile) && (dirsel != null) && (dirsel != 'undefined') && (dirsel != '')) { + var currdir = form.elements[dirsel].options[form.elements[dirsel].selectedIndex].value; + if (currdir == '') { + if ((filesel != null) && (filesel != 'undefined') && (filesel != '')) { + selelem = form.elements[filesel]; + var j, numfiles = selelem.options.length -1; + if (numfiles >=0) { + for (j = numfiles; j >= 0; j--) { + selelem.remove(j); + } + } + if (selelem.options.length == 0) { + selelem.options[selelem.options.length] = new Option('',''); + selelem.selectedIndex = 0; } - $selimport_menus{$key}->{'select2'}->{$file} = $file; - push(@ordered,$file); - } - if (@ordered) { - push(@order,$key); - $nonemptydirs{$key} = 1; - $selimport_menus{$key}->{'text'} = $shown; - $selimport_menus{$key}->{'default'} = ''; - $selimport_menus{$key}->{'select2'}->{''} = ''; - $selimport_menus{$key}->{'order'} = \@ordered; } + return; + } else { + relpath = encodeURIComponent(form.elements[dirsel].options[form.elements[dirsel].selectedIndex].value); } } - $possdirs = scalar(keys(%nonemptydirs)); - if ($possdirs > 1) { - my @order = sort { lc($a) cmp lc($b) } (keys(%nonemptydirs)); - $output = $lt{'dire'}. - &linked_select_forms($form,'
'. - $lt{'fnam'},'', - $firstselectname,$secondselectname, - \%selimport_menus,\@order, - $onchangefirst,'',$suffix).'
'; - } elsif ($possdirs == 1) { - $singledir = (keys(%nonemptydirs))[0]; - if (ref($selimport_menus{$singledir}->{'order'}) eq 'ARRAY') { - @singledirfiles = @{$selimport_menus{$singledir}->{'order'}}; - } - delete($selimport_menus{$singledir}); - } - } elsif ($numdirs == 1) { - $singledir = (keys(%files))[0]; - foreach my $file (sort { lc($a) cmp lc($b) } (keys(%{$files{$singledir}}))) { - if ($only) { - my ($ext) = ($file =~ /\.([^.]+)$/); - unless ($possexts{lc($ext)}) { - next; + var http = new XMLHttpRequest(); + var url = "/adm/courseauthor"; + var crsrole = "$env{'request.role'}"; + var exclude = ''; + if (exc) { + exclude = '$exclude'; + } + var params = "role=course&files=1&rec="+recurse+"&nonempty="+nonemptydir+"&exc="+exclude+"&inc="+include+"&addtop="+addtopdir+"&path="+relpath; + http.open("POST", url, true); + http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + http.onreadystatechange = function() { + if (http.readyState == 4 && http.status == 200) { + var data = JSON.parse(http.responseText); + var selelem; + if ((setdir) && (dirsel != null) && (dirsel != 'undefined') && (dirsel != '')) { + if (Array.isArray(data.dirs)) { + selelem = form.elements[dirsel]; + var i, numdirs = selelem.options.length -1; + if (numdirs >=0) { + for (i = numdirs; i >= 0; i--) { + selelem.remove(i); + } + } + var len = data.dirs.length; + if (len) { + selelem.options[selelem.options.length] = new Option('$se',''); + var j; + for (j = 0; j < len; j++) { + selelem.options[selelem.options.length] = new Option(data.dirs[j],data.dirs[j]); + } + selelem.selectedIndex = 0; + } + if (!setfile) { + if ((filesel != null) && (filesel != 'undefined') && (filesel != '')) { + selelem = form.elements[filesel]; + var j, numfiles = selelem.options.length -1; + if (numfiles >=0) { + for (j = numfiles; j >= 0; j--) { + selelem.remove(j); + } + } + if (selelem.options.length == 0) { + selelem.options[selelem.options.length] = new Option('',''); + selelem.selectedIndex = 0; + } + } + } + } + } + if ((setfile) && (filesel != null) && (filesel != 'undefined') && (filesel != '')) { + selelem = form.elements[filesel]; + var i, numfiles = selelem.options.length -1; + if (numfiles >=0) { + for (i = numfiles; i >= 0; i--) { + selelem.remove(i); + } + } + var x; + for (x in data.files) { + if (Array.isArray(data.files[x])) { + if (data.files[x].length > 1) { + selelem.options[selelem.options.length] = new Option('$se',''); + } + var len = data.files[x].length; + if (len) { + var k; + for (k = 0; k < len; k++) { + selelem.options[selelem.options.length] = new Option(data.files[x][k],data.files[x][k]); + } + selelem.selectedIndex = 0; + } + } + } + if (selelem.options.length == 0) { + selelem.options[selelem.options.length] = new Option('',''); + selelem.selectedIndex = 0; + } } - } else { - next if ($file =~ /\.rights$/); } - push(@singledirfiles,$file); - } - if (@singledirfiles) { - $possdirs = 1; - } - } - if (($possdirs == 1) && (@singledirfiles)) { - my $showdir = $singledir; - if ($singledir eq '') { - $showdir = '/'; - } - $output = $lt{'dire'}. - '
'. - $lt{'fnam'}.'
'."\n"; + http.send(params); } - return ($possdirs,$output); +END } =pod @@ -4284,6 +4394,30 @@ sub syllabuswrapper { # ----------------------------------------------------------------------------- +sub aboutme_on { + my ($uname,$udom)=@_; + unless ($uname) { $uname=$env{'user.name'}; } + unless ($udom) { $udom=$env{'user.domain'}; } + return if ($udom eq 'public' && $uname eq 'public'); + my $hashkey=$uname.':'.$udom; + my ($aboutme,$cached)=&Apache::lonnet::is_cached_new('aboutme',$hashkey); + if ($cached) { + return $aboutme; + } + $aboutme = &Apache::lonnet::usertools_access($uname,$udom,'aboutme'); + &Apache::lonnet::do_cache_new('aboutme',$hashkey,$aboutme,3600); + return $aboutme; +} + +sub devalidate_aboutme_cache { + my ($uname,$udom)=@_; + if (!$udom) { $udom =$env{'user.domain'}; } + if (!$uname) { $uname=$env{'user.name'}; } + return if ($udom eq 'public' && $uname eq 'public'); + my $id=$uname.':'.$udom; + &Apache::lonnet::devalidate_cache_new('aboutme',$id); +} + sub track_student_link { my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_; my $link ="/adm/trackstudent?"; @@ -5998,7 +6132,7 @@ sub get_domainconf { foreach my $host (keys(%{$domconfig{'login'}{$key}})) { if (ref($domconfig{'login'}{$key}{$host}) eq 'HASH') { $designhash{$udom.'.login.'.$key.'_'.$host} = 1; - foreach my $item ('text','img','alt','url','title','notsso') { + foreach my $item ('text','img','alt','url','title','window','notsso') { $designhash{$udom.'.login.'.$key.'_'.$item.'_'.$host} = $domconfig{'login'}{$key}{$host}{$item}; } } @@ -6377,6 +6511,21 @@ Inputs: context, this will contain a reference to hash of items to be included in the page header and/or inline menu. +=item * $menucoll, optional argument, if specific menu collection is in + effect, either set as the default for the course, or set for + the deeplink paramater for $env{'request.deeplink.login'} + then $menucoll will be the number of that collection. + +=item * $menuref, optional argument, reference to a hash, containing the + menu options included for the menu in effect, based on the + configuration for the numbered menu collection in use. + +=item * $showncrumbsref, reference to a scalar. Calls to lonmenu::innerregister + within &bodytag() can result in calls to lonhtmlcommon::breadcrumbs(), + if so, $showncrumbsref is set there to 1, and will propagate back + via &bodytag() to &start_page(), to prevent lonhtmlcommon::breadcrumbs() + being called a second time. + =back Returns: A uniform header for LON-CAPA web pages. @@ -6389,7 +6538,7 @@ other decorations will be returned. sub bodytag { my ($title,$function,$addentries,$bodyonly,$domain,$forcereg, $no_nav_bar,$bgcolor,$args,$advtoolsref,$ltiscope,$ltiuri, - $ltimenu,$menucoll,$menuref)=@_; + $ltimenu,$menucoll,$menuref,$showncrumbsref)=@_; my $public; if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')) @@ -6570,12 +6719,12 @@ sub bodytag { $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); if ($env{'request.state'} eq 'construct') { $bodytag .= &Apache::lonmenu::innerregister($forcereg, - $args->{'bread_crumbs'},'','',$hostname,$ltiscope,$ltiuri); + $args->{'bread_crumbs'},'','',$hostname, + $ltiscope,$ltiuri,$showncrumbsref); } elsif ($forcereg) { $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef, - $args->{'group'}, - $args->{'hide_buttons'}, - $hostname,$ltiscope,$ltiuri); + $args->{'group'},$args->{'hide_buttons'}, + $hostname,$ltiscope,$ltiuri,$showncrumbsref); } else { $bodytag .= &Apache::lonmenu::prepare_functions($env{'request.noversionuri'}, @@ -6659,8 +6808,38 @@ sub endbodytag { } if ( exists( $env{'internal.head.redirect'} ) ) { if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) { + my ($endbodyjs,$idattr); + if ($env{'internal.head.to_opener'}) { + my $linkid = 'LC_continue_link'; + $idattr = ' id="'.$linkid.'"'; + my $redirect_for_js = &js_escape($env{'internal.head.redirect'}); + $endbodyjs=< +// + +ENDJS + } $endbodytag= - "
". + "$endbodyjs
". &mt('Continue').''. $endbodytag; } @@ -8830,7 +9009,13 @@ Inputs: $title - optional title for the 3- whether the side effect should occur (side effect of setting $env{'internal.head.redirect'} to the url - redirected too) + redirected to) + 4- whether the redirect target should be + the opener of the current (pop-up) + window (side effect of setting + $env{'internal.head.to_opener'} to + 1, if true. + 5- whether encrypt check should be skipped domain -> force to color decorate a page for a specific domain function -> force usage of a specific rolish color scheme @@ -8893,15 +9078,45 @@ sub headtag { } } if (ref($args->{'redirect'})) { - my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}}; - $url = &Apache::lonenc::check_encrypt($url); + my ($time,$url,$inhibit_continue,$to_opener,$skip_enc_check) = @{$args->{'redirect'}}; + if (!$skip_enc_check) { + $url = &Apache::lonenc::check_encrypt($url); + } if (!$inhibit_continue) { $env{'internal.head.redirect'} = $url; } - $result.=< +ADDMETA + if ($to_opener) { + $env{'internal.head.to_opener'} = 1; + my $dest = &js_escape($url); + my $timeout = int($time * 1000); + $result .=<<"ENDJS"; + +ENDJS + } else { + $result.=<<"ADDMETA"; ADDMETA + } } else { unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) { my $requrl = $env{'request.uri'}; @@ -9338,6 +9553,7 @@ sub start_page { } } + my $showncrumbs; if (! exists($args->{'skip_phases'}{'body'}) ) { if ($args->{'frameset'}) { my $attr_string = &make_attr_string($args->{'force_register'}, @@ -9350,7 +9566,8 @@ sub start_page { $args->{'only_body'}, $args->{'domain'}, $args->{'force_register'}, $args->{'no_nav_bar'}, $args->{'bgcolor'}, $args, - \@advtools,$ltiscope,$ltiuri,\%ltimenu,$menucoll,\%menu); + \@advtools,$ltiscope,$ltiuri,\%ltimenu,$menucoll, + \%menu,\$showncrumbs); } } @@ -9372,6 +9589,7 @@ sub start_page { #Breadcrumbs if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) { + unless ($showncrumbs) { &Apache::lonhtmlcommon::clear_breadcrumbs(); #if any br links exists, add them to the breadcrumbs if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') { @@ -9394,12 +9612,20 @@ sub start_page { } else { undef($menulink); } + my $linkprotout; + if ($env{'request.deeplink.login'}) { + my $linkprotout = &Apache::lonmenu::linkprot_exit(); + if ($linkprotout) { + &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$linkprotout); + } + } #if bread_crumbs_component exists show it as headline else show only the breadcrumbs if(exists($args->{'bread_crumbs_component'})){ $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},'',$menulink); } else { $result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink); } + } } return $result; } @@ -14195,7 +14421,9 @@ sub process_extracted_files { my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'. $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'. $title; - if (($outer !~ /\D/) && ($mapinner{$outer} !~ /\D/) && ($newidx !~ /\D/)) { + if (($outer !~ /\D/) && + (($mapinner{$outer} eq 'default') || ($mapinner{$outer} !~ /\D/)) && + ($newidx !~ /\D/)) { if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") { mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755); } @@ -16251,26 +16479,29 @@ sub assign_category_rows { sub commit_customrole { my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_; + my $result = &Apache::lonnet::assigncustomrole( + $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context); my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url. ($start?', '.&mt('starting').' '.localtime($start):''). - ($end?', ending '.localtime($end):'').': '. - &Apache::lonnet::assigncustomrole( - $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context). - '
'; - return $output; + ($end?', ending '.localtime($end):'').': '.$result.'
'; + if (wantarray) { + return ($output,$result); + } else { + return $output; + } } sub commit_standardrole { my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_; - my ($output,$logmsg,$linefeed); + my ($output,$logmsg,$linefeed,$result); if ($context eq 'auto') { $linefeed = "\n"; } else { $linefeed = "
\n"; } if ($three eq 'st') { - my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end, - $one,$two,$sec,$context,$credits); + $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end, + $one,$two,$sec,$context,$credits); if (($result =~ /^error/) || ($result eq 'not_in_class') || ($result eq 'unknown_course') || ($result eq 'refused')) { $output = $logmsg.' '.&mt('Error: ').$result."\n"; @@ -16290,14 +16521,18 @@ sub commit_standardrole { $output = &mt('Assigning').' '.$three.' in '.$url. ($start?', '.&mt('starting').' '.localtime($start):''). ($end?', '.&mt('ending').' '.localtime($end):'').': '; - my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context); + $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context); if ($context eq 'auto') { $output .= $result.$linefeed; } else { $output .= ''.$result.''.$linefeed; } } - return $output; + if (wantarray) { + return ($output,$result); + } else { + return $output; + } } sub commit_studentrole { @@ -16326,7 +16561,7 @@ sub commit_studentrole { } $oldsecurl = $uurl; $expire_role_result = - &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context); + &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','','',$context); if ($env{'request.course.sec'} ne '') { if ($expire_role_result eq 'refused') { my @roles = ('st'); @@ -16968,7 +17203,6 @@ sub construct_course { # unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank') || ($cloneid)) { - use LONCAPA::map; $outcome .= &mt('Setting first resource').': '; my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence'; @@ -17355,7 +17589,7 @@ sub init_user_environment { my %is_adv = ( is_adv => $env{'user.adv'} ); my %domdef = &Apache::lonnet::get_domain_defaults($domain); - foreach my $tool ('aboutme','blog','webdav','portfolio') { + foreach my $tool ('aboutme','blog','webdav','portfolio','timezone') { $userenv{'availabletools.'.$tool} = &Apache::lonnet::usertools_access($username,$domain,$tool,'reload', undef,\%userenv,\%domdef,\%is_adv); @@ -18228,30 +18462,54 @@ sub needs_coursereinit { if ($blocked) { return (); } - my $lastchange = &Apache::lonnet::get_coursechange($cdom,$cnum); - if ($lastchange > $env{'request.course.tied'}) { - my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired'); - if ($curr_reqd_hash{'internal.releaserequired'} ne '') { - my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'}; - if ($curr_reqd_hash{'internal.releaserequired'} ne $required) { - &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' => - $curr_reqd_hash{'internal.releaserequired'}}); - my ($switchserver,$switchwarning) = - &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'}, - $curr_reqd_hash{'internal.releaserequired'}); - if ($switchwarning ne '' || $switchserver ne '') { - return ('switch',$switchwarning,$switchserver); - } + my $update; + my $lastmainchange = &Apache::lonnet::get_coursechange($cdom,$cnum); + my $lastsuppchange = &Apache::lonnet::get_suppchange($cdom,$cnum); + if ($lastmainchange > $env{'request.course.tied'}) { + my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum); + if ($needswitch) { + return ('switch',$switchwarning,$switchserver); + } + $update = 'main'; + } + if ($lastsuppchange > $env{'request.course.suppupdated'}) { + if ($update) { + $update = 'both'; + } else { + my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum); + if ($needswitch) { + return ('switch',$switchwarning,$switchserver); + } else { + $update = 'supp'; } } - return ('update'); + return ($update); + } + } + return (); +} + +sub switch_for_update { + my ($loncaparev,$cdom,$cnum) = @_; + my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired'); + if ($curr_reqd_hash{'internal.releaserequired'} ne '') { + my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'}; + if ($curr_reqd_hash{'internal.releaserequired'} ne $required) { + &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' => + $curr_reqd_hash{'internal.releaserequired'}}); + my ($switchserver,$switchwarning) = + &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'}, + $curr_reqd_hash{'internal.releaserequired'}); + if ($switchwarning ne '' || $switchserver ne '') { + return ('switch',$switchwarning,$switchserver); + } } } return (); } sub update_content_constraints { - my ($cdom,$cnum,$chome,$cid,$keeporder) = @_; + my ($cdom,$cnum,$chome,$cid) = @_; my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired'); my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'}); my (%checkresponsetypes,%checkcrsrestypes); @@ -18299,25 +18557,7 @@ sub update_content_constraints { } undef($navmap); } - my (@resources,@order,@resparms,@zombies); - if ($keeporder) { - use LONCAPA::map; - @resources = @LONCAPA::map::resources; - @order = @LONCAPA::map::order; - @resparms = @LONCAPA::map::resparms; - @zombies = @LONCAPA::map::zombies; - } - my $suppmap = 'supplemental.sequence'; - my ($suppcount,$supptools,$errors) = (0,0,0); - ($suppcount,$supptools,$errors) = &recurse_supplemental($cnum,$cdom,$suppmap, - $suppcount,$supptools,$errors); - if ($keeporder) { - @LONCAPA::map::resources = @resources; - @LONCAPA::map::order = @order; - @LONCAPA::map::resparms = @resparms; - @LONCAPA::map::zombies = @zombies; - } - if ($supptools) { + if (&Apache::lonnet::count_supptools($cnum,$cdom,1)) { my ($major,$minor) = split(/\./,$checkcrsrestypes{'exttool'}); if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) { ($reqdmajor,$reqdminor) = ($major,$minor); @@ -18343,7 +18583,7 @@ sub allmaps_incourse { if ($lastchange > $env{'request.course.tied'}) { my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum"); unless ($ferr) { - &update_content_constraints($cdom,$cnum,$chome,$cid,1); + &update_content_constraints($cdom,$cnum,$chome,$cid); } } my $navmap = Apache::lonnavmaps::navmap->new(); @@ -18369,8 +18609,10 @@ sub parse_supplemental_title { my $name = &plainname($uname,$udom); $name = &HTML::Entities::encode($name,'"<>&\''); $renametitle = &HTML::Entities::encode($renametitle,'"<>&\''); - $title=''.&Apache::lonlocal::locallocaltime($time).' '. - $name.':
'.$foldertitle; + $title=''.&Apache::lonlocal::locallocaltime($time).' '.$name; + if ($foldertitle ne '') { + $title .= ':
'.$foldertitle; + } } if (wantarray) { return ($title,$foldertitle,$renametitle); @@ -18378,32 +18620,147 @@ sub parse_supplemental_title { return $title; } +sub get_supplemental { + my ($cnum,$cdom,$ignorecache,$possdel)=@_; + my $hashid=$cnum.':'.$cdom; + my ($supplemental,$cached,$set_httprefs); + unless ($ignorecache) { + ($supplemental,$cached) = &Apache::lonnet::is_cached_new('supplemental',$hashid); + } + unless (defined($cached)) { + my $chome=&Apache::lonnet::homeserver($cnum,$cdom); + unless ($chome eq 'no_host') { + my @order = @LONCAPA::map::order; + my @resources = @LONCAPA::map::resources; + my @resparms = @LONCAPA::map::resparms; + my @zombies = @LONCAPA::map::zombies; + my ($errors,%ids,%hidden); + $errors = + &recurse_supplemental($cnum,$cdom,'supplemental.sequence', + $errors,$possdel,\%ids,\%hidden); + @LONCAPA::map::order = @order; + @LONCAPA::map::resources = @resources; + @LONCAPA::map::resparms = @resparms; + @LONCAPA::map::zombies = @zombies; + $set_httprefs = 1; + if ($env{'request.course.id'} eq $cdom.'_'.$cnum) { + &Apache::lonnet::appenv({'request.course.suppupdated' => time}); + } + $supplemental = { + ids => \%ids, + hidden => \%hidden, + }; + &Apache::lonnet::do_cache_new('supplemental',$hashid,$supplemental,600); + } + } + return ($supplemental,$set_httprefs); +} + sub recurse_supplemental { - my ($cnum,$cdom,$suppmap,$numfiles,$numexttools,$errors) = @_; - if ($suppmap) { + my ($cnum,$cdom,$suppmap,$errors,$possdel,$suppids,$hiddensupp,$hidden) = @_; + if (($suppmap) && (ref($suppids) eq 'HASH') && (ref($hiddensupp) eq 'HASH')) { + my $mapnum; + if ($suppmap eq 'supplemental.sequence') { + $mapnum = 0; + } else { + ($mapnum) = ($suppmap =~ /^supplemental_(\d+)\.sequence$/); + } my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap); if ($fatal) { $errors ++; } else { - if ($#LONCAPA::map::resources > 0) { - foreach my $res (@LONCAPA::map::resources) { - my ($title,$src,$ext,$type,$status)=split(/\:/,$res); + my @order = @LONCAPA::map::order; + if (@order > 0) { + my @resources = @LONCAPA::map::resources; + my @resparms = @LONCAPA::map::resparms; + foreach my $idx (@order) { + my ($title,$src,$ext,$type,$status)=split(/\:/,$resources[$idx]); if (($src ne '') && ($status eq 'res')) { + my $id = $mapnum.':'.$idx; + push(@{$suppids->{$src}},$id); + if (($hidden) || (&get_supp_parameter($resparms[$idx],'parameter_hiddenresource') =~ /^yes/i)) { + $hiddensupp->{$id} = 1; + } if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) { - ($numfiles,$numexttools,$errors) = &recurse_supplemental($cnum,$cdom,$1, - $numfiles,$numexttools,$errors); + $errors = &recurse_supplemental($cnum,$cdom,$1,$errors,$possdel,$suppids, + $hiddensupp,$hiddensupp->{$id}); } else { - if ($src =~ m{^/adm/$cdom/$cnum/\d+/ext\.tool$}) { - $numexttools ++; + my $allowed; + if (($env{'request.role.adv'}) || (!$hiddensupp->{$id})) { + $allowed = 1; + } elsif ($possdel) { + foreach my $item (@{$suppids->{$src}}) { + next if ($item eq $id); + unless ($hiddensupp->{$item}) { + $allowed = 1; + last; + } + } + if ((!$allowed) && (exists($env{'httpref.'.$src}))) { + &Apache::lonnet::delenv('httpref.'.$src); + } + } + if ($allowed && (!exists($env{'httpref.'.$src}))) { + &Apache::lonnet::allowuploaded('/adm/coursedoc',$src); + } + } + } + } + } + } + } + return $errors; +} + +sub set_supp_httprefs { + my ($cnum,$cdom,$supplemental,$possdel) = @_; + if (ref($supplemental) eq 'HASH') { + if ((ref($supplemental->{'ids'}) eq 'HASH') && (ref($supplemental->{'hidden'}) eq 'HASH')) { + foreach my $src (keys(%{$supplemental->{'ids'}})) { + next if ($src =~ /\.sequence$/); + if (ref($supplemental->{'ids'}->{$src}) eq 'ARRAY') { + my $allowed; + if ($env{'request.role.adv'}) { + $allowed = 1; + } else { + foreach my $id (@{$supplemental->{'ids'}->{$src}}) { + unless ($supplemental->{'hidden'}->{$id}) { + $allowed = 1; + last; + } + } + } + if (exists($env{'httpref.'.$src})) { + if ($possdel) { + unless ($allowed) { + &Apache::lonnet::delenv('httpref.'.$src); } - $numfiles ++; } + } elsif ($allowed) { + &Apache::lonnet::allowuploaded('/adm/coursedoc',$src); } } } + if ($env{'request.course.id'} eq $cdom.'_'.$cnum) { + &Apache::lonnet::appenv({'request.course.suppupdated' => time}); + } } } - return ($numfiles,$numexttools,$errors); +} + +sub get_supp_parameter { + my ($resparm,$name)=@_; + return if ($resparm eq ''); + my $value=undef; + my $ptype=undef; + foreach (split('&&&',$resparm)) { + my ($thistype,$thisname,$thisvalue)=split('___',$_); + if ($thisname eq $name) { + $value=$thisvalue; + $ptype=$thistype; + } + } + return $value; } sub symb_to_docspath { @@ -18476,6 +18833,67 @@ sub symb_to_docspath { return $path; } +sub validate_folderpath { + my ($supplementalflag,$allowed,$coursenum,$coursedom) = @_; + if ($env{'form.folderpath'} ne '') { + my @items = split(/\&/,$env{'form.folderpath'}); + my ($badpath,$changed,$got_supp,$supppath,%supphidden,%suppids); + for (my $i=0; $i<@items; $i++) { + my $odd = $i%2; + if (($odd) && (!$supplementalflag) && ($items[$i] !~ /^[^:]*:(|\d+):(|1):(|1):(|1):(|1)$/)) { + $badpath = 1; + } elsif ($odd && $supplementalflag) { + my $idx = $i-1; + if ($items[$i] =~ /^([^:]*)::(|1):::$/) { + my $esc_name = $1; + if ((!$allowed) || ($items[$idx] eq 'supplemental')) { + $supppath .= '&'.$esc_name; + $changed = 1; + } else { + $supppath .= '&'.$items[$i]; + } + } elsif (($allowed) && ($items[$idx] ne 'supplemental')) { + $changed = 1; + my $is_hidden; + unless ($got_supp) { + my ($supplemental) = &get_supplemental($coursenum,$coursedom); + if (ref($supplemental) eq 'HASH') { + if (ref($supplemental->{'hidden'}) eq 'HASH') { + %supphidden = %{$supplemental->{'hidden'}}; + } + if (ref($supplemental->{'ids'}) eq 'HASH') { + %suppids = %{$supplemental->{'ids'}}; + } + } + $got_supp = 1; + } + if (ref($suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}) eq 'ARRAY') { + my $mapid = $suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}->[0]; + if ($supphidden{$mapid}) { + $is_hidden = 1; + } + } + $supppath .= '&'.$items[$i].'::'.$is_hidden.':::'; + } else { + $supppath .= '&'.$items[$i]; + } + } elsif ((!$odd) && ($items[$i] !~ /^(default|supplemental)(|_\d+)$/)) { + $badpath = 1; + } elsif ($supplementalflag) { + $supppath .= '&'.$items[$i]; + } + last if ($badpath); + } + if ($badpath) { + delete($env{'form.folderpath'}); + } elsif ($changed && $supplementalflag) { + $supppath =~ s/^\&//; + $env{'form.folderpath'} = $supppath; + } + } + return; +} + sub captcha_display { my ($context,$lonhost,$defdom) = @_; my ($output,$error); @@ -18598,7 +19016,7 @@ sub create_captcha { $output = ''."\n". ''. &mt('Type in the letters/numbers shown below').' '. - ''. + ''. '
'. 'captcha'; last;