[LON-CAPA-cvs] cvs: doc /loncapafiles loncapafiles.lpml loncom loncapa_apache.conf loncom/homework edit.pm loncom/interface loncommon.pm loncourseauthor.pm londocs.pm loncom/lonnet/perl lonnet.pm

raeburn raeburn at source.lon-capa.org
Sat Dec 31 09:09:02 EST 2022


raeburn		Sat Dec 31 14:09:02 2022 EDT

  Added files:                 
    /loncom/interface	loncourseauthor.pm 

  Modified files:              
    /loncom/interface	loncommon.pm londocs.pm 
    /loncom/homework	edit.pm 
    /loncom/lonnet/perl	lonnet.pm 
    /loncom	loncapa_apache.conf 
    /doc/loncapafiles	loncapafiles.lpml 
  Log:
  - Use ajax to dynamically update drop-down lists of directories and files
    available in course author space.
  
  
-------------- next part --------------
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.1399 loncom/interface/loncommon.pm:1.1400
--- loncom/interface/loncommon.pm:1.1399	Thu Dec  1 01:24:53 2022
+++ loncom/interface/loncommon.pm	Sat Dec 31 14:08:58 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1399 2022/12/01 01:24:53 raeburn Exp $
+# $Id: loncommon.pm,v 1.1400 2022/12/31 14:08:58 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1233,7 +1233,12 @@
         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
     }
     $result .= "</select>\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 .= "<select size=\"1\" name=\"$secondselectname\"";
     if ($onchangesecond) {
@@ -1816,8 +1821,11 @@
             save => 'Save page to make this permanent',
         );
         &js_escape(\%js_lt);
+        my $showfile_js = &show_crsfiles_js();
         $browse_or_search = <<"END";
 
+    $showfile_js
+
     function toggleChooser(form,element,titleid,only,search) {
         var disp = 'none';
         if (document.getElementById('chooser_'+element)) {
@@ -1832,22 +1840,54 @@
                 toggleResImport(form,element);
             }
             document.getElementById('chooser_'+element).style.display = disp;
+            var dirsel = '';
+            var filesel = '';
+            if (document.getElementById('chooser_'+element+'_crsres')) {
+                var currcrsres = document.getElementById('chooser_'+element+'_crsres').style.display;
+                if (currcrsres == 'none') {
+                    dirsel = 'coursepath_'+element;
+                    var filesel = 'coursefile_'+element;
+                    var include;
+                    if (document.getElementById('crsres_include_'+element)) {
+                        include = document.getElementById('crsres_include_'+element).value;
+                    }
+                    populateCrsSelects(form,dirsel,filesel,1,include,1,0,1,1);
+                }
+            }
+            if (document.getElementById('chooser_'+element+'_upload')) {
+                var currcrsupload = document.getElementById('chooser_'+element+'_upload').style.display;
+                if (currcrsupload == 'none') {
+                    dirsel = 'crsauthorpath_'+element;
+                    filesel = '';
+                    populateCrsSelects(form,dirsel,filesel,0,'',1,0,1,0);
+                }
+            }
         }
     }
 
-    function toggleCrsFile(form,element,numdirs) {
+    function toggleCrsFile(form,element) {
         if (document.getElementById('chooser_'+element+'_crsres')) {
             var curr = document.getElementById('chooser_'+element+'_crsres').style.display;
             if (curr == 'none') {
-                if (numdirs) {
+                if (document.getElementById('coursepath_'+element)) {
+                    var numdirs;
+                    if (document.getElementById('coursepath_'+element).length) {
+                        numdirs = document.getElementById('coursepath_'+element).length;
+                    }
                     form.elements['coursepath_'+element].selectedIndex = 0;
                     if (numdirs > 1) {
-                        window['select1'+element+'_changed']();
+                        var selelem = form.elements['coursefile_'+element];
+                        var i, len = selelem.options.length -1;
+                        if (len >=0) {
+                            for (i = len; i >= 0; i--) {
+                                selelem.remove(i);
+                            }
+                            selelem.options[0] = new Option('','');
+                        }
                     }
                 }
-            } 
+            }
             document.getElementById('chooser_'+element+'_crsres').style.display = 'block';
-            
         }
         if (document.getElementById('chooser_'+element+'_upload')) {
             document.getElementById('chooser_'+element+'_upload').style.display = 'none';
@@ -1858,20 +1898,20 @@
         return;
     }
 
-    function toggleCrsUpload(form,element,numcrsdirs) {
+    function toggleCrsUpload(form,element) {
         if (document.getElementById('chooser_'+element+'_crsres')) {
             document.getElementById('chooser_'+element+'_crsres').style.display = 'none';
         }
         if (document.getElementById('chooser_'+element+'_upload')) {
             var curr = document.getElementById('chooser_'+element+'_upload').style.display;
             if (curr == 'none') {
-                if (numcrsdirs) {
-                   form.elements['crsauthorpath_'+element].selectedIndex = 0;
-                   form.elements['newsubdir_'+element][0].checked = true;
-                   toggleNewsubdir(form,element);
+                form.elements['newsubdir_'+element][0].checked = true;
+                toggleNewsubdir(form,element);
+                document.getElementById('chooser_'+element+'_upload').style.display = 'block';
+                if (document.getElementById('uploadcrsres_'+element)) {
+                    document.getElementById('uploadcrsres_'+element).value = '';
                 }
             }
-            document.getElementById('chooser_'+element+'_upload').style.display = 'block';
         }
         return;
     }
@@ -1915,19 +1955,21 @@
         var filename = form.elements['coursefile_'+element];
         var path = directory.options[directory.selectedIndex].value;
         var file = filename.options[filename.selectedIndex].value;
-        form.elements[element].value = '$respath';
-        if (path == '/') {
-            form.elements[element].value += file;
-        } else {
-            form.elements[element].value += path+'/'+file;
-        }
-        unClean();
-        if (document.getElementById('previewimg_'+element)) {
-            document.getElementById('previewimg_'+element).src = form.elements[element].value;
-            var newsrc = document.getElementById('previewimg_'+element).src; 
-        }
-        if (document.getElementById('showimg_'+element)) {
-            document.getElementById('showimg_'+element).innerHTML = '($js_lt{save})';
+        if (file != '') {
+            form.elements[element].value = '$respath';
+            if (path == '/') {
+                form.elements[element].value += file;
+            } else {
+                form.elements[element].value += path+'/'+file;
+            }
+            unClean();
+            if (document.getElementById('previewimg_'+element)) {
+                document.getElementById('previewimg_'+element).src = form.elements[element].value;
+                var newsrc = document.getElementById('previewimg_'+element).src; 
+            }
+            if (document.getElementById('showimg_'+element)) {
+                document.getElementById('showimg_'+element).innerHTML = '($js_lt{save})';
+            }
         }
         toggleChooser(form,element);
         return;
@@ -2216,111 +2258,168 @@
 }
 
 sub import_crsauthor_form {
-    my ($form,$firstselectname,$secondselectname,$onchangefirst,$only,$suffix,$disabled) = @_;
+    my ($firstselectname,$secondselectname,$onchangefirst,$only,$suffix,$disabled) = @_;
     return (0) unless ($env{'request.course.id'});
     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
     my $crshome = $env{'course.'.$env{'request.course.id'}.'.home'};
     return (0) unless (($cnum ne '') && ($cdom ne ''));
-    my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
     my @ids=&Apache::lonnet::current_machine_ids();
-    my ($output,$is_home,$relpath,%subdirs,%files,%selimport_menus);
+    my ($output,$is_home,$toppath,%subdirs,%files,%selimport_menus,$include,$exclude);
     
     if (grep(/^\Q$crshome\E$/, at ids)) {
         $is_home = 1;
     }
-    $relpath = "/priv/$cdom/$cnum";
-    &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$relpath,'',\%subdirs,\%files);
+    $toppath = "/priv/$cdom/$cnum";
+    my $nonemptydir = 1;
+    my $js_only;
+    if ($only) {
+        map { $include->{$_} = 1; } split(/\s*,\s*/,$only);
+        $js_only = join(',',map { &js_escape($_); } sort(keys(%{$include})));
+    }
+    $exclude = &Apache::lonnet::priv_exclude();
+    &Apache::lonnet::recursedirs($is_home,1,$include,$exclude,1,$toppath,'',\%subdirs,\%files);
+    my $numdirs = scalar(keys(%files));
     my %lt = &Apache::lonlocal::texthash (
         fnam => 'Filename',
         dire => 'Directory',
+        se   => 'Select',
     );
-    my $numdirs = scalar(keys(%files));
-    my (%possexts,$singledir, at singledirfiles);
-    if ($only) {
-        map { $possexts{$_} = 1; } split(/\s*,\s*/,$only);
-    }
-    my (%nonemptydirs,$possdirs);
-    if ($numdirs > 1) {
-        my @order;
-        foreach my $key (sort { lc($a) cmp lc($b) } (keys(%files))) {
-            if (ref($files{$key}) eq 'HASH') {
-                my $shown = $key;
-                if ($key eq '') {
-                    $shown = '/';
-                }
-                my @ordered = ();
-                foreach my $file (sort { lc($a) cmp lc($b) } (keys(%{$files{$key}}))) {
-                    next if ($file =~ /\.rights$/);
-                    if ($only) {
-                        my ($ext) = ($file =~ /\.([^.]+)$/);
-                        unless ($possexts{lc($ext)}) {
-                            next;
-                        }
+    $output = $lt{'dire'}.
+              '<select id="'.$firstselectname.'" name="'.$firstselectname.'" '.
+              'onchange="populateCrsSelects(this.form,'."'$firstselectname','$secondselectname',1,'$js_only',0,1,0,0".');">'.
+              '<option value="" selected="selected">'.$lt{'se'}.'</option>';
+    foreach my $key (sort { lc($a) cmp lc($b) } (keys(%files))) {
+        $output .= '<option value="'.$key.'">'.$key.'</option>'."\n";
+    }
+    $output .= '</select><br />'."\n".
+               $lt{'fnam'}.'<select id="'.$secondselectname.'" name="'.$secondselectname.'">'."\n".
+               '<option value="" selected="selected"></option>'."\n".
+               '</select>'."\n";
+    $output .= '<input type="hidden" id="crsres_include_'.$suffix.'" value="'.$only.'" />';
+    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) {
+        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,'<br />'.
-                                           $lt{'fnam'},'',
-                                           $firstselectname,$secondselectname,
-                                           \%selimport_menus,\@order,
-                                           $onchangefirst,'',$suffix).'<br />';
-        } 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+"&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) {
+                            if (len > 1) {
+                                selelem.options[selelem.options.length] = new Option('$se','');
+                            }
+                        }
+                        if (len) {
+                            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;
         }
+        http.send(params);
     }
-    if (($possdirs == 1) && (@singledirfiles)) {
-        my $showdir = $singledir;
-        if ($singledir eq '') {
-            $showdir = '/';
-        }
-        $output = $lt{'dire'}.
-                  '<select name="'.$firstselectname.'">'.
-                  '<option value="'.$singledir.'">'.$showdir.'</option>'."\n".
-                  '</select><br />'.
-                  $lt{'fnam'}.'<select name="'.$secondselectname.'">'."\n".
-                  '<option value="" selected="selected">'.$lt{'se'}.'</option>'."\n";
-        foreach my $file (@singledirfiles) {
-            $output .= '<option value="'.$file.'">'.$file.'</option>'."\n";
-        }
-        $output .= '</select><br />'."\n";
-    }
-    return ($possdirs,$output);
+END
 }
 
 =pod
Index: loncom/interface/londocs.pm
diff -u loncom/interface/londocs.pm:1.689 loncom/interface/londocs.pm:1.690
--- loncom/interface/londocs.pm:1.689	Sat Dec 17 18:07:47 2022
+++ loncom/interface/londocs.pm	Sat Dec 31 14:08:59 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Documents
 #
-# $Id: londocs.pm,v 1.689 2022/12/17 18:07:47 raeburn Exp $
+# $Id: londocs.pm,v 1.690 2022/12/31 14:08:59 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -6294,12 +6294,12 @@
 SEDFFORM
         my $importcrsresform;
         my ($numdirs,$pickfile) = 
-            &Apache::loncommon::import_crsauthor_form('crsresimportform','coursepath','coursefile',
+            &Apache::loncommon::import_crsauthor_form('coursepath','coursefile',
                                                       "resize_scrollbox('contentscroll','1','0');",
                                                       undef,'res');
         if ($pickfile) {
             $importcrsresform=(<<CRSFORM);
-        <a class="LC_menubuttons_link" href="javascript:toggleImportCrsres('res','$numdirs');">
+        <a class="LC_menubuttons_link" href="javascript:toggleImportCrsres('res');">
         $lt{'imcr'}</a>$help{'Course_Resources'}
         <form action="/adm/coursedocs" method="post" name="crsresimportform" onsubmit="return validImportCrsRes();">
         <fieldset id="importcrsresform" style="display: none;">
@@ -6310,7 +6310,7 @@
         $lt{'title'}: <input type="textbox" name="crsrestitle" value="" $disabled />
         </p>
         <input type="hidden" name="importdetail" value="" />
-        <input type="submit" name="crsres" value="$lt{'impo'}" $disabled />
+        <input type="submit" name="crsres" value="$lt{'impo'}" $disabled /><br />
         </fieldset>
         </form>
 CRSFORM
@@ -6330,7 +6330,7 @@
         { '<img class="LC_noBorder LC_middle" src="/res/adm/pages/sequence.png" alt="'.$lt{impm}.'" onclick="javascript:toggleMap(\'map\');" />' => $importpubform },
         );
         if ($pickfile) {
-            push(@importpubforma,{ '<img class="LC_noBorder LC_middle" src="/res/adm/pages/res.png" alt="'.$lt{imcr}.'"  onclick="javascript:toggleImportCrsres(\'res\','."'$numdirs'".');"/>' => $importcrsresform});
+            push(@importpubforma,{ '<img class="LC_noBorder LC_middle" src="/res/adm/pages/res.png" alt="'.$lt{imcr}.'"  onclick="javascript:toggleImportCrsres(\'res\');" />' => $importcrsresform});
 	}
 	$importpubform = &create_form_ul(&create_list_elements(@importpubforma));
         my $extresourcesform =
@@ -6537,16 +6537,16 @@
         my $numcrsdirs = 0;
         my ($showstdprob,$showswitch,$switchlink);
         my $toppath = "/priv/$env{'user.domain'}/$env{'user.name'}"; 
+        my $exclude = &Apache::lonnet::priv_exclude();
         if ($env{'user.author'}) {
             $numauthor ++;
             $select_menus{'author'}->{'text'} = &Apache::lonnet::plaintext('au');
             if (grep(/^\Q$env{'user.home'}\E$/, at ids)) {
                 my $is_home = 1;
                 my %subdirs;
-                &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$toppath,'',\%subdirs);
+                &Apache::lonnet::recursedirs($is_home,1,'',$exclude,0,$toppath,'',\%subdirs);
                 $select_menus{'author'}->{'default'} = '/'; 
-                $select_menus{'author'}->{'select2'}->{'/'} = '/';
-                my @ordered = ('/');
+                my @ordered = ();
                 foreach my $relpath (sort { lc($a) cmp lc($b) } (keys(%subdirs))) {
                     $select_menus{'author'}->{'select2'}->{$relpath} = $relpath;
                     push(@ordered,$relpath);
@@ -6578,10 +6578,8 @@
                     my $is_home = 1;
                     my (%subdirs, at ordered);
                     my $toppath="/priv/$audom/$auname";
-                    &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$toppath,'',\%subdirs);
+                    &Apache::lonnet::recursedirs($is_home,1,'',$exclude,0,$toppath,'',\%subdirs);
                     $select_menus{$key}->{'default'} = '/';
-                    $select_menus{$key}->{'select2'}->{'/'} = '/';
-                    my @ordered = ('/');
                     foreach my $relpath (sort { lc($a) cmp lc($b) } (keys(%subdirs))) {
                         $select_menus{$key}->{'select2'}->{$relpath} = $relpath;
                         push(@ordered,$relpath);
@@ -6626,11 +6624,10 @@
                 my $is_home = 1;
                 my %subdirs;
                 my $toppath="/priv/$coursedom/$coursenum";
-                &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$toppath,'',\%subdirs);
+                &Apache::lonnet::recursedirs($is_home,1,'',$exclude,0,$toppath,'',\%subdirs);
                 $numcrsdirs = keys(%subdirs);
                 $select_menus{'course'}->{'default'} = '/';
-                $select_menus{'course'}->{'select2'}->{'/'} = '/';
-                my @ordered = ('/');
+                my @ordered = ();
                 foreach my $relpath (sort { lc($a) cmp lc($b) } (keys(%subdirs))) {
                     $select_menus{'course'}->{'select2'}->{$relpath} = $relpath;
                     push(@ordered,$relpath);
@@ -6658,7 +6655,7 @@
                 $pickdir .= '<input type="hidden" name="authorrole" value="course" />'; 
                 my $toppath="/priv/$coursedom/$coursenum'}";
                 my %subdirs;
-                &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$toppath,'',\%subdirs);
+                &Apache::lonnet::recursedirs($is_home,1,'',$exclude,0,$toppath,'',\%subdirs);
                 $numcrsdirs = keys(%subdirs); 
                 if ($numcrsdirs) {
                     $pickdir .= $lt{'dire'}.' <select name="authorpath">'."\n".
@@ -6679,7 +6676,7 @@
                 $select_menus{'course'}->{'default'} = 'switch';
                 $select_menus{'course'}->{'order'} = ['switch'];
                 push(@order,'course');
-                my $defrole;
+                my $defrole = 'course';
                 $pickdir = $lt{'loca'}.
                            &Apache::loncommon::linked_select_forms('courseresform','<br />'.$lt{'dire'},
                                                                    $defrole,'authorrole','authorpath',
@@ -7530,7 +7527,7 @@
     my $backtourl;
     my $toplevelmain = &escape(&default_folderpath($coursenum,$coursedom,$navmapref));
     my $toplevelsupp = &supplemental_base();
-
+    my $showfile_js = &Apache::loncommon::show_crsfiles_js();
     if ($env{'docs.exit.'.$env{'request.course.id'}} =~ /^direct_(.+)$/) {
         my $caller = $1;
         if ($caller =~ /^supplemental/) {
@@ -8056,17 +8053,14 @@
     }
 }
 
-function toggleImportCrsres(caller,dircount) {
+function toggleImportCrsres(caller) {
     var disp = 'none';
     if (document.getElementById('importcrsresform')) {
         if (caller == 'res') {
-            var numdirs = parseInt(dircount);
             var curr = document.getElementById('importcrsresform').style.display;
             if (curr == 'none') {
                 disp='block';
-                if (numdirs > 1) {
-                    select1res_changed();
-                }
+                populateCrsSelects(document.crsresimportform,'coursepath','coursefile',1,'',1,0,1,1);
             }
         }
         document.getElementById('importcrsresform').style.display=disp;
@@ -8075,6 +8069,8 @@
     return;
 }
 
+$showfile_js
+
 function switchForProb() {
     if (document.courseresform.authorpath.options[document.courseresform.authorpath.selectedIndex].value == 'switch') {
         var url = '/adm/switchserver?otherserver=';
Index: loncom/homework/edit.pm
diff -u loncom/homework/edit.pm:1.155 loncom/homework/edit.pm:1.156
--- loncom/homework/edit.pm:1.155	Wed Oct  4 12:55:09 2017
+++ loncom/homework/edit.pm	Sat Dec 31 14:08:59 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA 
 # edit mode helpers
 #
-# $Id: edit.pm,v 1.155 2017/10/04 12:55:09 raeburn Exp $
+# $Id: edit.pm,v 1.156 2022/12/31 14:08:59 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1225,32 +1225,27 @@
             $allonly = $crsonly;
         }
         my ($numdirs,$pickfile) =
-            &Apache::loncommon::import_crsauthor_form($form,'coursepath_'.$element,'coursefile_'.$element,undef,$allonly,$element);
-        if ($pickfile) {
-            $importcrsres=(<<CRSRES);
+            &Apache::loncommon::import_crsauthor_form('coursepath_'.$element,'coursefile_'.$element,undef,$allonly,$element);
+        $importcrsres=(<<CRSRES);
         <fieldset id="importcrsresform_$element" style="display:inline;">
         <legend>$lt{'uacf'}</legend>
         $pickfile
         <input type="button" name="crsres" value="$lt{'sefi'}" onclick="updateCrsFile(this.form,'$element');" />
         </fieldset>
 CRSRES
-        }
         my %subdirs;
         my $toppath="/priv/$cdom/$cnum";
-        my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
-        &Apache::lonnet::recursedirs(1,'priv',$londocroot,$toppath,'',\%subdirs);
+        my $exclude = &Apache::lonnet::priv_exclude();
+        &Apache::lonnet::recursedirs(1,1,'',$exclude,'',$toppath,'',\%subdirs);
         my $numcrsdirs = keys(%subdirs);
-        my $pickdir;
-        if ($numcrsdirs) {
-            $pickdir = $lt{'dire'}.'<select name="crsauthorpath_'.$element.'">'."\n".
+        my $pickdir = $lt{'dire'}.'<select name="crsauthorpath_'.$element.'">'."\n".
                                    '<option value="/">/</option>'."\n";
+        if ($numcrsdirs) {
             foreach my $key (sort { lc($a) cmp lc($b) } (keys(%subdirs))) {
                 $pickdir .= '<option value="'.$key.'">'.$key.'</option>'."\n";
             }
-            $pickdir .= '</select><br />';
-        } else {
-            $pickdir = '<input type="hidden" name="crsauthorpath_'.$element.'" value="/" />'."\n";
         }
+        $pickdir .= '</select><br />';
         my $uploadfile =(<<CRSUPL);
         <fieldset id="uploadcrsresform_$element" style="display:inline;">
         <legend>$lt{'uanf'}</legend>
@@ -1278,12 +1273,10 @@
         $output = '<a href="javascript:toggleChooser(document.'.$form.",'$element'".');">'.
                   &mt('Choose File').'</a>'.
                   '<div id="chooser_'.$element.'" style="display:none" class="LC_left_float">'.
-                  '<fieldset><legend>'.&mt('Choose File').'</legend>';
-        if ($numdirs) {
-            $output .= '<label><input type="radio" name="chooser_'.$element.'" value="crsres" onclick="toggleCrsFile(this.form,'."'$element','$numdirs'".')" />'.$lt{'uacf'}.'</label> ';
-        }
-        $output .= '<label><input type="radio" name="chooser_'.$element.'" value="upload" onclick="toggleCrsUpload(this.form,'."'$element','$numcrsdirs'".')" />'.$lt{'uanf'}.'</label> '.
-                   '<label><input type="radio" name="chooser_'.$element.'" value="import" onclick="toggleResImport(this.form,'."'$element'".');openbrowser('."'$form','$element'$bretitleelement)".'" />'.$lt{'impo'}.'</label>';
+                  '<fieldset><legend>'.&mt('Choose File').'</legend>'.
+                  '<label><input type="radio" name="chooser_'.$element.'" value="crsres" onclick="toggleCrsFile(this.form,'."'$element'".')" />'.$lt{'uacf'}.'</label> '.
+                  '<label><input type="radio" name="chooser_'.$element.'" value="upload" onclick="toggleCrsUpload(this.form,'."'$element'".')" />'.$lt{'uanf'}.'</label> '.
+                  '<label><input type="radio" name="chooser_'.$element.'" value="import" onclick="toggleResImport(this.form,'."'$element'".');openbrowser('."'$form','$element'$bretitleelement)".'" />'.$lt{'impo'}.'</label>';
         if ($usesearch) {
             $output .= ' <label><input type="radio" name="chooser_'.$element.'" value="search" onclick="opensearcher('."'$form','$element'$srchtitleelement".')" />'.$lt{'sear'}.'</label>';
         }
Index: loncom/lonnet/perl/lonnet.pm
diff -u loncom/lonnet/perl/lonnet.pm:1.1502 loncom/lonnet/perl/lonnet.pm:1.1503
--- loncom/lonnet/perl/lonnet.pm:1.1502	Wed Nov 23 02:55:37 2022
+++ loncom/lonnet/perl/lonnet.pm	Sat Dec 31 14:09:00 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1502 2022/11/23 02:55:37 raeburn Exp $
+# $Id: lonnet.pm,v 1.1503 2022/12/31 14:09:00 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -2924,7 +2924,7 @@
             if (&auto_instcode_format($caller,$dom,\%coursecodes,\%codes,
                                       \@codetitles,\%cat_titles,\%cat_order) eq 'ok') {
                 $instcats = {
-                                totcodes => $totcodes, 
+                                totcodes => $totcodes,
                                 codes => \%codes,
                                 codetitles => \@codetitles,
                                 cat_titles => \%cat_titles,
@@ -12026,14 +12026,19 @@
 # or corresponding Published Resource Space, and populate the hash ref:
 # $dirhashref with URLs of all directories, and if $filehashref hash
 # ref arg is provided, the URLs of any files, excluding versioned, .meta,
-# or .rights files in resource space, and .meta, .save, .log, and .bak
-# files in Authoring Space.
+# or .rights files in resource space, and .meta, .save, .log, .bak and
+# .rights files in Authoring Space.
 #
 # Inputs:
 #
 # $is_home - true if current server is home server for user's space
-# $context - either: priv, or res respectively for Authoring or Resource Space.
-# $docroot - Document root (i.e., /home/httpd/html
+# $recurse - if true will also traverse subdirectories recursively
+# $include - reference to hash containing allowed file extensions.  If provided,
+#             files which do not have a matching extension will be ignored.
+# $exclude - reference to hash containing excluded file extensions.  If provided,
+#             files which have a matching extension will be ignored.
+# $nonemptydir - if true, will only populate $fileshashref hash entry for a particular
+#             directory with first file found (with acceptable extension).
 # $toppath - Top level directory (i.e., /res/$dom/$uname or /priv/$dom/$uname
 # $relpath - Current path (relative to top level).
 # $dirhashref - reference to hash to populate with URLs of directories (Required)
@@ -12050,39 +12055,61 @@
 #
 
 sub recursedirs {
-    my ($is_home,$context,$docroot,$toppath,$relpath,$dirhashref,$filehashref) = @_;
+    my ($is_home,$recurse,$include,$exclude,$nonemptydir,$toppath,$relpath,$dirhashref,$filehashref) = @_;
     return unless (ref($dirhashref) eq 'HASH');
+    my $docroot = $perlvar{'lonDocRoot'};
     my $currpath = $docroot.$toppath;
-    if ($relpath) {
+    if ($relpath ne '') {
         $currpath .= "/$relpath";
     }
-    my $savefile;
+    my ($savefile,$checkinc,$checkexc);
     if (ref($filehashref)) {
         $savefile = 1;
     }
+    if (ref($include) eq 'HASH') {
+        $checkinc = 1;
+    }
+    if (ref($exclude) eq 'HASH') {
+        $checkexc = 1;
+    }
     if ($is_home) {
         if (opendir(my $dirh,$currpath)) {
+            my $filecount = 0;
             foreach my $item (sort { lc($a) cmp lc($b) } grep(!/^\.+$/,readdir($dirh))) {
                 next if ($item eq '');
                 if (-d "$currpath/$item") {
                     my $newpath;
-                    if ($relpath) {
+                    if ($relpath ne '') {
                         $newpath = "$relpath/$item";
                     } else {
                         $newpath = $item;
                     }
                     $dirhashref->{&Apache::lonlocal::js_escape($newpath)} = 1;
-                    &recursedirs($is_home,$context,$docroot,$toppath,$newpath,$dirhashref,$filehashref);
-                } elsif ($savefile) {
-                    if ($context eq 'priv') {
-                        unless ($item =~ /\.(meta|save|log|bak|DS_Store)$/) {
-                            $filehashref->{&Apache::lonlocal::js_escape($relpath)}{$item} = 1;
-                        }
-                    } else {
-                        unless (($item =~ /\.meta$/) || ($item =~ /\.\d+\.\w+$/) || ($item =~ /\.rights$/)) {
+                    if ($recurse) {
+                        &recursedirs($is_home,$recurse,$include,$exclude,$nonemptydir,$toppath,$newpath,$dirhashref,$filehashref);
+                    }
+                } elsif (($savefile) || ($relpath eq '')) {
+                    next if ($nonemptydir && $filecount);
+                    if ($checkinc || $checkexc) {
+                        my ($extension) = ($item =~ /\.(\w+)$/);
+                        if ($checkinc) {
+                            next unless ($extension && $include->{$extension});
+                        }
+                        if ($checkexc) {
+                            next if ($extension && $exclude->{$extension});
+                        }
+                    }
+                    if (($relpath eq '') && (!exists($dirhashref->{'/'})))  {
+                        $dirhashref->{'/'} = 1;
+                    }
+                    if ($savefile) {
+                        if ($relpath eq '') {
+                            $filehashref->{'/'}{$item} = 1;
+                        } else {
                             $filehashref->{&Apache::lonlocal::js_escape($relpath)}{$item} = 1;
                         }
                     }
+                    $filecount ++;
                 }
             }
             closedir($dirh);
@@ -12093,6 +12120,7 @@
         my @dir_lines;
         my $dirptr=16384;
         if (ref($dirlistref) eq 'ARRAY') {
+            my $filecount = 0;
             foreach my $dir_line (sort
                               {
                                   my ($afile)=split('&',$a,2);
@@ -12108,21 +12136,34 @@
                     if ($relpath) {
                         $newpath = "$relpath/$item";
                     } else {
-                        $relpath = '/';
                         $newpath = $item;
                     }
                     $dirhashref->{&Apache::lonlocal::js_escape($newpath)} = 1;
-                    &recursedirs($is_home,$context,$docroot,$toppath,$newpath,$dirhashref,$filehashref);
-                } elsif ($savefile) {
-                    if ($context eq 'priv') {
-                        unless ($item =~ /\.(meta|save|log|bak|DS_Store)$/) {
-                            $filehashref->{$relpath}{$item} = 1;
-                        }
-                    } else {
-                        unless (($item =~ /\.meta$/) || ($item =~ /\.\d+\.\w+$/)) {
-                            $filehashref->{$relpath}{$item} = 1;
+                    if ($recurse) {
+                        &recursedirs($is_home,$recurse,$include,$exclude,$nonemptydir,$toppath,$newpath,$dirhashref,$filehashref);
+                    }
+                } elsif (($savefile) || ($relpath eq '')) {
+                    next if ($nonemptydir && $filecount);
+                    if ($checkinc || $checkexc) {
+                        my $extension;
+                        if ($checkinc) {
+                            next unless ($extension && $include->{$extension});
+                        }
+                        if ($checkexc) {
+                            next if ($extension && $exclude->{$extension});
+                        }
+                    }
+                    if (($relpath eq '') && (!exists($dirhashref->{'/'}))) {
+                        $dirhashref->{'/'} = 1;
+                    }
+                    if ($savefile) {
+                        if ($relpath eq '') {
+                            $filehashref->{'/'}{$item} = 1;
+                        } else {
+                            $filehashref->{&Apache::lonlocal::js_escape($relpath)}{$item} = 1;
                         }
                     }
+                    $filecount ++; 
                 }
             }
         }
@@ -12130,6 +12171,17 @@
     return;
 }
 
+sub priv_exclude {
+    return {
+             meta => 1,
+             save => 1,
+             log => 1,
+             bak => 1,
+             rights => 1,
+             DS_Store => 1,
+           };
+}
+
 # -------------------------------------------------------- Value of a Condition
 
 # gets the value of a specific preevaluated condition
Index: loncom/loncapa_apache.conf
diff -u loncom/loncapa_apache.conf:1.278 loncom/loncapa_apache.conf:1.279
--- loncom/loncapa_apache.conf:1.278	Thu Jun 30 21:04:15 2022
+++ loncom/loncapa_apache.conf	Sat Dec 31 14:09:01 2022
@@ -2,7 +2,7 @@
 ## loncapa_apache.conf -- Apache HTTP LON-CAPA configuration file
 ##
 
-# $Id: loncapa_apache.conf,v 1.278 2022/06/30 21:04:15 raeburn Exp $
+# $Id: loncapa_apache.conf,v 1.279 2022/12/31 14:09:01 raeburn Exp $
 
 #
 # LON-CAPA Section (extensions to httpd.conf daemon configuration)
@@ -765,6 +765,17 @@
 ErrorDocument     500 /adm/errorhandler
 </Location>
 
+<Location /adm/courseauthor>
+AuthType LONCAPA
+Require valid-user
+PerlAuthzHandler       Apache::lonacc
+SetHandler perl-script
+PerlHandler Apache::loncourseauthor
+ErrorDocument     403 /adm/login
+ErrorDocument     406 /adm/roles
+ErrorDocument     500 /adm/errorhandler
+</Location>
+
 <Location /adm/login>
 SetHandler perl-script
 PerlHandler Apache::lonlogin
Index: doc/loncapafiles/loncapafiles.lpml
diff -u doc/loncapafiles/loncapafiles.lpml:1.1034 doc/loncapafiles/loncapafiles.lpml:1.1035
--- doc/loncapafiles/loncapafiles.lpml:1.1034	Tue Oct  4 20:39:58 2022
+++ doc/loncapafiles/loncapafiles.lpml	Sat Dec 31 14:09:02 2022
@@ -2,7 +2,7 @@
  "http://lpml.sourceforge.net/DTD/lpml.dtd">
 <!-- loncapafiles.lpml -->
 
-<!-- $Id: loncapafiles.lpml,v 1.1034 2022/10/04 20:39:58 raeburn Exp $ -->
+<!-- $Id: loncapafiles.lpml,v 1.1035 2022/12/31 14:09:02 raeburn Exp $ -->
 
 <!--
 
@@ -2516,6 +2516,16 @@
 <status>works/unverified</status>
 </file>
 <file>
+<source>loncom/interface/loncourseauthor.pm</source>
+<target dist='default'>home/httpd/lib/perl/Apache/loncourseauthor.pm</target>
+<categoryname>handler</categoryname>
+<description>
+Support ajax requests with json response for listing of directories and/or files
+stored in /home/httpd/html/priv/<domain>/<courseID> on course's home sever. 
+</description>
+<status>works/unverified</status>
+</file>
+<file>
 <source>loncom/interface/lonchat.pm</source>
 <target dist='default'>home/httpd/lib/perl/Apache/lonchat.pm</target>
 <categoryname>handler</categoryname>

Index: loncom/interface/loncourseauthor.pm
+++ loncom/interface/loncourseauthor.pm
# The LearningOnline Network
# Documents
#
# $Id: loncourseauthor.pm,v 1.1 2022/12/31 14:08:59 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#

package Apache::loncourseauthor;

use strict;
use Apache::Constants qw(:common :http);
use Apache::lonnet;
use Apache::loncommon;
use JSON::DWIW;
use LONCAPA qw(:DEFAULT :match);

sub handler {
    my $r = shift;
    &Apache::loncommon::content_type($r,'application/json');
    $r->send_http_header;
    my ($nonemptydir,%dirhash,%filehash);
    if ($env{'request.course.id'}) {
        if (&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) {
            my ($context,$recurse,$role,$is_home,$inc,$exc,$toppath,$relpath,
                $include,$exclude);
            if ($env{'form.role'}) {
                $role = $env{'form.role'};
                if ($env{'form.rec'}) {
                    $recurse = 1;
                }
                if ($env{'form.res'}) {
                    $context = 'res';
                } else {
                    $context = 'priv';
                }
                if ($env{'form.inc'}) {
                    $inc = $env{'form.inc'};
                    $inc =~ s/^\s+|\s+$//g;
                }
                if ($env{'form.exc'}) {
                    $exc = $env{'form.exc'};
                    $exc  =~ s/^\s+|\s+$//g;
                }
                if ($env{'form.nonempty'}) {
                    $nonemptydir = 1; 
                }
                my $now = time;
                my @ids=&Apache::lonnet::current_machine_ids();
                if ($role eq 'author') {
                    if (exists($env{"user.role.au./$env{'user.domain'}/"})) {
                        my ($start,$end) = split(/\./,$env{"user.role.au./$env{'user.domain'}/"});
                        unless (($start && $start > $now) || ($end && $end < $now)) {
                            $toppath = "/$context/$env{'user.domain'}/$env{'user.name'}";
                            if (grep(/^\Q$env{'user.home'}\E$/, at ids)) {
                                $is_home = 1;
                            }
                        }
                    }
                } elsif ($role eq 'course') {
                    my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                    my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                    if (($cdom ne '') && ($cnum ne '')) {
                        $toppath = "/$context/$cdom/$cnum";
                        my $rolehome = &Apache::lonnet::homeserver($cnum,$cdom);
                        if (grep(/^\Q$rolehome\E$/, at ids)) {
                            $is_home = 1;
                        } 
                    }
                } elsif ($role =~ m{^(ca|aa)\./($match_domain)/($match_username)$}) { 
                    my ($rolecode,$audom,$auname) = ($1,$2,$3);
                    if (exists($env{"user.role.$role"})) {
                        my ($start,$end) = split(/\./,$env{"user.role.$role"});
                        unless(($start && $start > $now) || ($end && $end < $now)) {
                            $toppath = "/$context/$audom/$auname";
                            my $rolehome = &Apache::lonnet::homeserver($auname,$audom);
                            if (grep(/^\Q$rolehome\E$/, at ids)) {
                                $is_home = 1;
                            }
                        }
                    }
                }
                if ($toppath ne '') {
                    if ($env{'form.path'}) {
                        $relpath = $env{'form.path'};
                    }
                    my @ids=&Apache::lonnet::current_machine_ids();
                    if (grep(/^\Q$env{'user.home'}\E$/, at ids)) {
                        $is_home = 1;
                    }
                    if ($inc) {
                        map { $include->{$_} = 1; } split(/\s*,\s*/,$inc);
                    }
                    if ($exc) {
                        map { $exclude->{$_} = 1; } split(/\s*,\s*/,$exc);
                    }
                    my $dirhashref = \%dirhash;
                    my $filehashref; 
                    if ($env{'form.files'}) {
                        $filehashref = \%filehash;
                    }
                    &Apache::lonnet::recursedirs($is_home,$recurse,$include,$exclude,$nonemptydir,
                                                 $toppath,$relpath,$dirhashref,$filehashref);
                }
            }
        }
    }
    my @dirs = ();
    if (%dirhash) {
        if ($dirhash{'/'}) {
            push(@dirs,'/');
            delete($dirhash{'/'});
        }
        if ($nonemptydir) {
            foreach my $dir (sort { lc($a) cmp lc($b) } (keys(%dirhash))) {
                next unless (ref($filehash{$dir}) eq 'HASH');
                push(@dirs,$dir);
            }
        } else {
            push(@dirs,(sort { lc($a) cmp lc($b) } (keys(%dirhash))));
        }
    }
    my %files;
    if (%filehash) {
        foreach my $dir (keys(%filehash)) {
            if (ref($filehash{$dir}) eq 'HASH') {
                foreach my $key (keys(%{$filehash{$dir}})) {
                    if ($key =~ /\./) {
                        my $ext = (split(/\./,$key))[-1];
                        delete($filehash{$dir}{$key}) if ($ext eq 'rights');
                    }
                }
                my @names = sort { lc($a) cmp lc($b) } (keys(%{$filehash{$dir}}));
                $files{$dir} = \@names;
            }
        }
    }
    $r->print(JSON::DWIW->to_json({dirs => \@dirs,
                                   files => \%files}));
    return OK;
}

1;


More information about the LON-CAPA-cvs mailing list