File:  [LON-CAPA] / loncom / interface / lonindexer.pm
Revision 1.182: download - view: text, annotated - select for diffs
Fri Sep 19 08:50:42 2008 UTC (15 years, 8 months ago) by bisitz
Branches: MAIN
CVS tags: HEAD
"Browse Resources" table:
  - Now uses LON-CAPA standard table
  - Removed hardcoded styles

Note:
Due to the usage of standard sub routines some styles are lost now, e.g. special background color for absolute links and vertical alignment in some cases (which actually needed to be improved anyway). I kept these code lines as comments starting with "#SB".
Another idea is to use a different background color for domain and folder rows.

# The LearningOnline Network with CAPA
# Directory Indexer
#
# $Id: lonindexer.pm,v 1.182 2008/09/19 08:50:42 bisitz 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/
#
###

###############################################################################
##                                                                           ##
## ORGANIZATION OF THIS PERL MODULE                                          ##
##                                                                           ##
## 1. Description of functions                                               ##
## 2. Modules used by this module                                            ##
## 3. Choices for different output views (detailed, summary, xml, etc)       ##
## 4. BEGIN block (to be run once after compilation)                         ##
## 5. Handling routine called via Apache and mod_perl                        ##
## 6. Other subroutines                                                      ##
##                                                                           ##
###############################################################################

package Apache::lonindexer;

# ------------------------------------------------- modules used by this module
use strict;
use Apache::lonnet;
use Apache::loncommon();
use Apache::lonhtmlcommon();
use Apache::lonsequence();
use Apache::Constants qw(:common);
use Apache::lonmeta;
use Apache::File;
use Apache::lonlocal;
use Apache::lonsource();
use Apache::groupsort();
use GDBM_File;
use LONCAPA qw(:match);

# ---------------------------------------- variables used throughout the module
my %hash; # global user-specific gdbm file
my %dirs; # keys are directories, values are the open/close status
my %language; # has the reference information present in language.tab
my %dynhash; # hash of hashes for dynamic metadata
my %dynread; # hash of directories already read for dynamic metadata
my %fieldnames; # Metadata fieldnames
# ----- Values which are set by the handler subroutine and are accessible to
# -----     other methods.
my $extrafield; # default extra table cell
my $fnum; # file counter
my $dnum; # directory counter

# ----- Used to include or exclude files with certain extensions.
my @Only = ();
my @Omit = ();




# ----------------------------- Handling routine called via Apache and mod_perl
sub handler {
    my $r = shift;
    my $c = $r->connection();
    &Apache::loncommon::content_type($r,'text/html');
    &Apache::loncommon::no_cache($r);
    $r->send_http_header;
    return OK if $r->header_only;
    $fnum=0;
    $dnum=0;

    # Deal with stupid global variables (is there a way around making
    # these global to this package?  It is just so wrong....)
    undef (@Only);
    undef (@Omit);
    %fieldnames=&Apache::lonmeta::fieldnames();

# ------------------------------------- read in machine configuration variables
    my $iconpath= $r->dir_config('lonIconsURL') . "/";
    my $domain  = $r->dir_config('lonDefDomain');
    my $role    = $r->dir_config('lonRole');
    my $loadlim = $r->dir_config('lonLoadLim');
    my $servadm = $r->dir_config('lonAdmEMail');
    my $sysadm  = $r->dir_config('lonSysEMail');
    my $lonhost = $r->dir_config('lonHostID');
    my $tabdir  = $r->dir_config('lonTabDir');

#SB my $fileclr='#ffffe6';
    my $line;
    my (@attrchk,@openpath,$typeselect);
    my $uri=$r->uri;

# -------------------------------------- see if called from an interactive mode
    # Get the parameters from the query string
    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
	     ['catalogmode','launch','acts','mode','form','element',
              'only','omit','titleelement']);
    #-------------------------------------------------------------------
    my $closebutton='';
    my $groupimportbutton='';
    my $colspan=''; 
    
    $extrafield='';
    my $diropendb = 
	"/home/httpd/perl/tmp/$env{'user.domain'}_$env{'user.name'}_sel_res.db";
    %hash = ();
    {
	my %dbfile;
	if (tie(%dbfile,'GDBM_File',$diropendb,&GDBM_WRITER(),0640)) {
	    if ($env{'form.launch'} eq '1') {
		&start_fresh_session(\%dbfile);
	    }
	    while(my($key,$value)=each(%dbfile)) {
		$hash{$key}=$value;
	    }
	    untie(%dbfile);
	}
    }
# - Evaluate actions from previous page (both cumulatively and chronologically)
        if ($env{'form.catalogmode'} eq 'import' || $hash{'form.catalogmode'} eq 'import') {
	    &Apache::groupsort::update_actions_hash(\%hash);
	}
    
    {
  #Hijack lonindexer to verify a title and be close down.
   if ($env{'form.launch'} eq '2') {
       &Apache::loncommon::content_type($r,'text/html');
       my $extra='';
       if (defined($env{'form.titleelement'}) && 
	   $env{'form.titleelement'} ne '') {
	   my $verify_title = &Apache::lonnet::gettitle($env{'form.acts'});
#	   &Apache::lonnet::logthis("Hrrm $env{'form.acts'} -- $verify_title");
	   $verify_title=~s/'/\\'/g;
	   $extra='window.opener.document.forms["'.$env{'form.form'}.'"].elements["'.$env{'form.titleelement'}.'"].value=\''.$verify_title.'\';';
       }
       my $js = <<ENDSUBM;
	       <script type="text/javascript">
		function load() {
			window.opener.document.forms["$env{'form.form'}"]
			    .elements["$env{'form.element'}"]
			    .value='$env{'form.acts'}';
			$extra
			window.close();
		}
   	       </script>
ENDSUBM
       $r->print(&Apache::loncommon::start_page(undef,$js,
						{'only_body'   =>1,
						 'add_entries' =>
						     {'onload' => "load();"},}
						).
		 &Apache::loncommon::end_page());
       return OK;
   }
    
# -------------------- refresh environment with user database values (in %hash)
	&setvalues(\%hash,'form.catalogmode',\%env,'form.catalogmode'   );

# --------------------- define extra fields and buttons in case of special mode
	if ($env{'form.catalogmode'} eq 'interactive') {
#SB	    $extrafield='<td bgcolor="'.$fileclr.'" valign="bottom">'.
            $extrafield='<td valign="bottom">'.
		'<img alt="" src="'.$iconpath.'whitespace1.gif"'.
		' class="LC_icon" /></td>';
	    $colspan=" colspan='2' ";
            my $cl=&mt('Close');
            $closebutton=<<END;
<input type="button" name="close" value='$cl' onClick="self.close()" />
END
        }
	elsif ($env{'form.catalogmode'} eq 'import') {
#SB	    $extrafield='<td bgcolor="'.$fileclr.'" valign="bottom">'.
            $extrafield='<td valign="bottom">'.
		'<img alt="" src="'.$iconpath.'whitespace1.gif"'.
		' class="LC_icon" /></td>';
	    $colspan=" colspan='2' ";
	    my $cl=&mt('Close');
            my $gi=&mt('Import');
            $closebutton=<<END;
<input type="button" name="close" value='$cl' onClick="self.close()" />
END
            $groupimportbutton=<<END;
<input type="button" name="groupimport" value='$gi'
onClick="javascript:select_group()" />
END
        }
	# Additions made by Matthew to make the browser a little easier to deal
	# with in the future.
	#
	# $mode (at this time) indicates if we are in edit mode.
	# $form is the name of the form that the URL is placed when the
	#       selection is made.
	# $element is the name of the element in $formname which receives
	#       the URL.
	#&Apache::lonxml::debug('Checking mode, form, element');
	&setvalues(\%hash,'form.mode'        ,\%env,'form.mode'   );
	&setvalues(\%hash,'form.form'        ,\%env,'form.form'   );
	&setvalues(\%hash,'form.element'     ,\%env,'form.element');
	&setvalues(\%hash,'form.titleelement',\%env,'form.titleelement');
	&setvalues(\%hash,'form.only'        ,\%env,'form.only'   );
	&setvalues(\%hash,'form.omit'        ,\%env,'form.omit'   );

        # Deal with 'omit' and 'only' 
        if (exists $env{'form.omit'}) {
            @Omit = split(',',$env{'form.omit'});
        }
        if (exists $env{'form.only'}) {
            @Only = split(',',$env{'form.only'});
        }
        
	my $mode = $env{'form.mode'};
	my ($form,$element,$titleelement);
	if ($mode eq 'edit' || $mode eq 'parmset') {
	    $form         = $env{'form.form'};
	    $element      = $env{'form.element'};
	    $titleelement = $env{'form.titleelement'};
	}
	#&Apache::lonxml::debug("mode=$mode form=$form element=$element titleelement=$titleelement");
# ------ set catalogmodefunctions to have extra needed javascript functionality
	my $catalogmodefunctions='';
	if ($env{'form.catalogmode'} eq 'interactive' or
	    $env{'form.catalogmode'} eq 'import') {
	    # The if statement below sets us up to use the old version
	    # by default (ie. if $mode is undefined).  This is the easy
	    # way out.  Hopefully in the future I'll find a way to get 
	    # the calls dealt with in a more comprehensive manner.

#
# There is now also mode "simple", which is for the simple version of the rat
#
#
	    if (!defined($mode) || ($mode ne 'edit' && $mode ne 'parmset')) {
                my $location = "/adm/groupsort?&inhibitmenu=yes&catalogmode=import&";
                $location .= "mode=".$mode."&";
                $location .= "acts=";
		$catalogmodefunctions=<<"END";
function select_data(url) {
    changeURL(url);
    self.close();
}
function select_group() {
    window.location="$location"+document.forms.fileattr.acts.value;
}
function changeURL(val) {
    if (opener.inf) {
        if (opener.inf.document.forms.resinfo.elements.u) {
	    opener.inf.document.forms.resinfo.elements.u.value=val;
        }
    }
}
END
            } elsif ($mode eq 'edit') { # we are in 'edit' mode
                my $location = "/adm/groupsort?catalogmode=interactive&";
                $location .= "form=$form&element=$element&mode=edit&acts=";
		$catalogmodefunctions=<<END;
// mode = $mode
function select_data(url) {
   var location = "/res/?launch=2&form=$form&element=$element&titleelement=$titleelement&acts=" + url;
   window.location=location;
   if (window.opener.document.forms["$form"].elements["$element"].value != url) {
       window.opener.unClean();
   }
}
function select_group() {
    window.location="$location"+document.forms.fileattr.acts.value;
}

function changeURL(val) {
    if (window.opener.document) {
	window.opener.document.forms["$form"].elements["$element"].value=val;
    } else {
	    alert("The file you selected is: "+val);
    }
}
END
                if (!$titleelement) {
		    $catalogmodefunctions.='function changeTitle(val) {}';
		} else {
		    $catalogmodefunctions.=<<END;
function changeTitle(val) {
    if (window.opener.document) {
	    window.opener.document.forms["$form"].elements["$titleelement"].value=val;
    } else {
	    alert("The title of the file you selected is: "+val);
    }
}
END
                }
            } elsif ($mode eq 'parmset') {
                my $location = "/adm/groupsort?catalogmode=interactive&";
                $location .= "form=$form&element=$element&mode=parmset&acts=";
		$catalogmodefunctions=<<END;
// mode = $mode
function select_data(url) {
    changeURL(url);
    self.close();
}

function select_group() {
    window.location="$location"+document.forms.fileattr.acts.value;
}

function changeURL(val) {
    if (window.opener.document) {
        var elementname  = "$element"+"_value";
        var checkboxname = "$element"+"_setparmval";
	window.opener.document.forms["$form"].elements[elementname].value=val;
        window.opener.document.forms["$form"].elements[checkboxname].checked=true;
    } else {
	    alert("The file you selected is: "+val);
    }
}

END
            }
        }
        $catalogmodefunctions.=<<END;
var acts='';
function rep_dirpath(suffix,val) {
    eval("document.forms.dirpath"+suffix+".acts.value=val");
}
END
	if ($env{'form.catalogmode'} eq 'import') {
            $catalogmodefunctions.=<<END;
function queue(val) {
    if (eval("document.forms."+val+".filelink.checked")) {
	var l=val.length;
	var v=val.substring(4,l);
	document.forms.fileattr.acts.value+='1a'+v+'b';
    }
    else {
	var l=val.length;
	var v=val.substring(4,l);
	document.forms.fileattr.acts.value+='0a'+v+'b';
    }
}
END
	}

        my $inhibit_menu = "+'&".&Apache::loncommon::inhibit_menu_check()."'";
# ---------------------------------------------------------------- Print Header
        
	my $js = <<"ENDHEADER";
<script type="text/javascript">
$catalogmodefunctions;
function update_only(field) {
    alert(field.name);
}
function checkAll() {
    var numForms = document.forms.length;
    for (i=0;i<numForms;i++) {
        var numElements = document.forms[i].elements.length;
        for (j=0;j<numElements;j++){
            var fieldName = document.forms[i].elements[j].name;
            if (fieldName == 'filelink') {
                document.forms[i].elements[j].checked = true;
                queue(document.forms[i].name);
            }
        }
    }
}
function uncheckAll() {
    var numForms = document.forms.length;
    for (i=0;i<numForms;i++) {
        var numElements = document.forms[i].elements.length;
        for (j=0;j<numElements;j++){
            var fieldName = document.forms[i].elements[j].name;
            if (fieldName == 'filelink') {
                document.forms[i].elements[j].checked = false;
                queue(document.forms[i].name);
            }
        }
    }
}
function openWindow(url, wdwName, w, h, toolbar,scrollbar,locationbar) {
    var xpos = (screen.width-w)/2;
    xpos = (xpos < 0) ? '0' : xpos;
    var ypos = (screen.height-h)/2-30;
    ypos = (ypos < 0) ? '0' : ypos;
    var options = "width=" + w + ",height=" + h + ",screenx="+xpos+",screeny="+ypos+",";
    options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
    options += "menubar=no,toolbar="+toolbar+",location="+locationbar+",directories=no";
    var newWin = window.open(url, wdwName, options);
    newWin.focus();
}
function gothere(val) {
    window.location=val+'?acts='+document.forms.fileattr.acts.value$inhibit_menu;
}
</script>
ENDHEADER

        my ($headerdom)=($uri=~m{^/res/($match_domain)/});
	$r->print(&Apache::loncommon::start_page('Browse Resources',$js,
						 {'domain' => $headerdom,}));
# ---------------------------------- get state of file types to be showing
	if ($env{'form.only'}) {
	    $typeselect = $env{'form.only'};
	} else {
	    $typeselect = '';
	}

# ---------------------------------- get state of file attributes to be showing
	if ($env{'form.attrs'}) {
	    for (my $i=0; $i<=11; $i++) {
		delete $hash{'display_attrs_'.$i};
		if ($env{'form.attr'.$i} == 1) {
		    $attrchk[$i] = 'checked';
		    $hash{'display_attrs_'.$i} = 1;
		}
	    }
	} else {
	    for (my $i=0; $i<=11; $i++) {
		$attrchk[$i] = 'checked' if $hash{'display_attrs_'.$i} == 1;
	    }
	}

# ------------------------------- output state of file attributes to be showing
#                                 All versions has to the last item
#                                 since it does not take an extra col
	my %lt=&Apache::lonlocal::texthash(
					   'ti' => 'Title',
					   'si' => 'Size',
					   'la' => 'Last access',
					   'lm' => 'Last modified',
					   'st' => 'Statistics',
					   'au' => 'Author',
					   'kw' => 'Keywords',
					   'ln' => 'Language',
					   'sa' => 'Source Available',
					   'sr' => 'Show resource',
					   'li' => 'Linked/Related Resources',
					   'av' => 'All versions',
					   'ud' => 'Update Display',
					   'pr' => 'Problems',
					   'gr' => 'Graphics',
					   'at' => 'All types'
					   );
	$r->print(<<END);
<form method="post" name="fileattr" action="$uri"
 enctype="application/x-www-form-urlencoded">
<label><input type="checkbox" name="attr9" value="1" $attrchk[9] onClick="this.form.submit();" /> $lt{'av'}</label>
<table border="0">
<tr>
<td><label><input type="checkbox" name="attr0" value="1" $attrchk[0] onClick="this.form.submit();" /> $lt{'ti'}</label></td>
<td><label><input type="checkbox" name="attr4" value="1" $attrchk[4] onClick="this.form.submit();" /> $lt{'au'}</label></td>
<td><label><input type="checkbox" name="attr5" value="1" $attrchk[5] onClick="this.form.submit();" /> $lt{'kw'}</label></td>
<td><label><input type="checkbox" name="attr6" value="1" $attrchk[6] onClick="this.form.submit();" /> $lt{'ln'}</label></td>
</tr>
<tr>
<td><label><input type="checkbox" name="attr1" value="1" $attrchk[1] onClick="this.form.submit();" /> $lt{'si'}</label></td>
<td><label><input type="checkbox" name="attr2" value="1" $attrchk[2] onClick="this.form.submit();" /> $lt{'la'}</label></td>
<td><label><input type="checkbox" name="attr3" value="1" $attrchk[3] onClick="this.form.submit();" /> $lt{'lm'}</label></td>
<td><label><input type="checkbox" name="attr10" value="1" $attrchk[10] onClick="this.form.submit();" /> $lt{'sa'}</label></td>
</tr>
<tr>
<td><label><input type="checkbox" name="attr8" value="1" $attrchk[8] onClick="this.form.submit();" /> $lt{'st'}</label></td>
<td><label><input type="checkbox" name="attr11" value="1" $attrchk[11] onClick="this.form.submit();" /> $lt{'li'}</label></td>
<td><label><input type="checkbox" name="attr7" value="1" $attrchk[7] onClick="this.form.submit();" /> $lt{'sr'}</label></td>
<td>&nbsp;</td>
</tr>
</table>
<input type="hidden" name="attrs" value="1" />
END
        my @file_categories = &Apache::loncommon::filecategories();
        my %select_file_categories;
        my @select_form_order = ('');
        $select_file_categories{''} = &mt('All file types');
        foreach my $cat (@file_categories) {
	    my $types = join(",",&Apache::loncommon::filecategorytypes($cat));
	    $select_file_categories{$types} = $cat;
	    push(@select_form_order,$types);
	}
        $select_file_categories{'select_form_order'} = \@select_form_order;
        my $type_element=
	    &Apache::loncommon::select_form($typeselect,'only',
					    %select_file_categories);
        $type_element = '<label>'.&mt('File Type Displayed: [_1]',
				      $type_element).'</label>';
        $r->print(<<END);
$type_element
<br />
<input type="submit" name="updatedisplay" value="$lt{'ud'}" />
<input type="hidden" name="acts" value="" />
$closebutton $groupimportbutton
END
        $r->print(&Apache::loncommon::inhibit_menu_check('input'));
   
# -------------- Filter out sequence containment in crumbs and "recent folders"
	my $storeuri=$uri;
	$storeuri='/'.(split(/\.(page|sequence)\/\//,$uri))[-1];
	$storeuri=~s/\/+/\//g;
# ---------------------------------------------------------------- Bread crumbs
        $r->print(&Apache::lonhtmlcommon::crumbs($storeuri,'','',
				(($env{'form.catalogmode'} eq 'import')?
				 'document.forms.fileattr':'')).
		  &Apache::lonhtmlcommon::select_recent('residx','resrecent',
'window.status=this.form.resrecent.options[this.form.resrecent.selectedIndex].value;this.form.action=this.form.resrecent.options[this.form.resrecent.selectedIndex].value;this.form.submit();'));
# -------------------------------------------------------- Resource Home Button
	my $reshome=$env{'course.'.$env{'request.course.id'}.'.reshome'};
	if ($reshome) {
	    $r->print("<font size='+2'><a href='");
	    if ($env{'form.catalogmode'} eq 'import') {
		$r->print('javascript:document.forms.fileattr.action="'.&Apache::loncommon::inhibit_menu_check($reshome).'";document.forms.fileattr.submit();');
	    } else {
		$r->print($reshome);
	    }
	    $r->print("'>".&mt('Home').'</a></font>');
	}
	$r->print('</form>');
# ------------------------------------------------------ Remember where we were
	&Apache::loncommon::storeresurl($storeuri);
	&Apache::lonhtmlcommon::store_recent('residx',$storeuri,$storeuri);
# -------------------------------------------------- Check All and Uncheck all
	if ($env{'form.catalogmode'} eq 'import') {
	    $r->print('<br /><input type="button" value="'.&mt("Check All").'" id="checkallbutton" onClick="javascript:checkAll()" />');
	    $r->print('<input type="button" value="'.&mt("Uncheck All").'" id="uncheckallbutton" onClick="javascript:uncheckAll()" />');
	}
# ----------------- output starting row to the indexed file/directory hierarchy
        #$r->print(&initdebug());
        #$r->print(&writedebug("Omit:@Omit")) if (@Omit);
        #$r->print(&writedebug("Only:@Only")) if (@Only);
        $r->print('<br /><br />');
        $r->print(&Apache::loncommon::start_data_table()
                 .&Apache::loncommon::start_data_table_header_row());
	$r->print("<th $colspan>".&mt('Name')."</th>\n");
	$r->print("<th>".&mt('Title')."</th>\n") 
	    if ($hash{'display_attrs_0'} == 1);
	$r->print('<th align="right">'.&mt("Size")." (".&mt("bytes").") ".
		  "</th>\n") if ($hash{'display_attrs_1'} == 1);
	$r->print("<th>".&mt("Last accessed")."</th>\n") 
	    if ($hash{'display_attrs_2'} == 1);
	$r->print("<th>".&mt("Last modified")."</th>\n")
	    if ($hash{'display_attrs_3'} == 1);
	$r->print("<th>".&mt("Author(s)")."</th>\n")
	    if ($hash{'display_attrs_4'} == 1);
	$r->print("<th>".&mt("Keywords")."</th>\n")
	    if ($hash{'display_attrs_5'} == 1);
	$r->print("<th>".&mt("Language")."</th>\n")
	    if ($hash{'display_attrs_6'} == 1);
	$r->print("<th>".&mt("Usage Statistics")." <br />(".
		  &mt("Courses/Network Hits").") ".&mt('updated periodically')."</th>\n")
	    if ($hash{'display_attrs_8'} == 1);
	$r->print("<th>".&mt("Source Available")."</th>\n")
	    if ($hash{'display_attrs_10'} == 1);
	$r->print("<th>".&mt("Linked/Related Resources")."</th>\n")
	    if ($hash{'display_attrs_11'} == 1);
	$r->print("<th>".&mt("Resource")."</th>\n")
	    if ($hash{'display_attrs_7'} == 1);
        $r->print(&Apache::loncommon::end_data_table_header_row());

# ----------------- read in what directories have previously been set to "open"
	foreach (keys %hash) {
	    if ($_ =~ /^diropen_status_/) {
		my $key = $_;
		$key =~ s/^diropen_status_//;
		$dirs{$key} = $hash{$_};
	    }
	}

	if ($env{'form.openuri'}) {  # take care of review and refresh options
	    my $uri=$env{'form.openuri'};
	    if (exists($hash{'diropen_status_'.$uri})) {
		my $cursta = $hash{'diropen_status_'.$uri};
		$dirs{$uri} = 'open';
		$hash{'diropen_status_'.$uri} = 'open';
		if ($cursta eq 'open') {
		    $dirs{$uri} = 'closed';
		    $hash{'diropen_status_'.$uri} = 'closed';
		}
	    } else {
		$hash{'diropen_status_'.$uri} = 'open';
		$dirs{$uri} = 'open';
	    }
	}
	
	my $toplevel;
	my $indent = 0;
	$uri = $uri.'/' if $uri !~ /.*\/$/;

 	if ($env{'form.dirPointer'} ne 'on') {
 	    $hash{'top.level'} = $uri;
 	    $toplevel = $uri;
 	} else {
 	    $toplevel = $hash{'top.level'};
 	}

# -------------------------------- if not at top level, provide an uplink arrow
	if ($toplevel ne '/res/'){
	    my (@uri_com) = split(/\//,$uri);
	    pop @uri_com;
	    my $upone = join('/',@uri_com);
	    my @list = qw (0);
	    &display_line ($r,'opened',$upone.'&viewOneUp',0,$upone,@list);
	    $indent = 1;
	}

# -------- recursively go through all the directories and output as appropriate
	&scanDir ($r,$toplevel,$indent,\%hash);

# -------------------------------------------------------------- end the tables
        $r->print(&Apache::loncommon::end_data_table());

# ---------------------------- embed hidden information useful for group import
	$r->print("<form name='fnum' action=''>");
	$r->print("<input type='hidden' name='fnum' value='$fnum' /></form>");

# --------------------------------------------------- end the output and return
	$r->print(&Apache::loncommon::end_page()."\n");
    }
    if(! $c->aborted()) {
# write back into the temporary file
	my %dbfile;
        if (tie(%dbfile,'GDBM_File',$diropendb,&GDBM_NEWDB(),0640)) {
            while (my($key,$value) = each(%hash)) {
                $dbfile{$key}=$value;
            }
            untie(%dbfile);
        }
    }

    return OK;
}

# ----------------------------------------------- recursive scan of a directory
sub scanDir {
    my ($r,$startdir,$indent,$hashref)=@_;
    my $c = $r->connection();
    my ($compuri,$curdir);
    my $dirptr=16384;
    my $obs;
    $indent++;
    my %dupdirs = %dirs;
    my @list=&get_list($r,$startdir);
    foreach my $line (@list) {
        return if ($c->aborted());
	#This is a kludge, sorry aboot this
	my ($strip,$dom,undef,$testdir,undef,undef,undef,undef,undef,undef,undef,undef,undef,undef,$obs,undef)=split(/\&/,$line,16); 
	next if($strip =~ /.*\.meta$/ | $obs eq '1');
	my (@fileparts) = split(/\./,$strip);
	if ($hash{'display_attrs_9'} != 1) {
            # if not all versions to be shown
	    if (scalar(@fileparts) >= 3) {
		my $fext = pop @fileparts;
		my $ov = pop @fileparts;
		my $fname = join ('.',@fileparts,$fext);
		next if (grep /\Q$fname\E/,@list and $ov =~ /^\d+$/);
	    }
	}

	if ($dom eq 'domain') {
	    # dom list has full path /res/<domain name>/ already
	    $curdir='';
	    $compuri = (split(/\&/,$line))[0];
	} else {
	    # user, dir & file have name only, i.e., w/o path
	    $compuri = join('',$startdir,$strip,'/');
	    $curdir = $startdir;
	}
	my $diropen = 'closed';
	if (($dirptr&$testdir) or ($dom =~ /^(domain|user)$/) or ($compuri=~/\.(sequence|page)\/$/)) {
	    while (my ($key,$val)= each %dupdirs) {
		if ($key eq $compuri and $val eq "open") {
		    $diropen = "opened";
		    delete($dupdirs{$key});
		    delete($dirs{$key});
		}
	    }
	}
	&display_line($r,$diropen,$line,$indent,$curdir,$hashref,@list);
	&scanDir ($r,$compuri,$indent) if $diropen eq 'opened';
    }
    $indent--;
}

# --------------- get complete matched list based on the uri (returns an array)
sub get_list {
    my ($r,$uri)=@_;
    my @list=();
    (my $luri = $uri) =~ s/\//_/g;
    if ($env{'form.updatedisplay'}) {
	foreach (keys %hash) {
	    delete $hash{$_} if ($_ =~ /^dirlist_files_/);
	    delete $hash{$_} if ($_ =~ /^dirlist_timestamp_files_/);
	}
    }

    if (defined($hash{'dirlist_files_'.$luri}) &&
	$hash{'dirlist_timestamp_files_'.$luri}+600 > (time)) {
	@list = split(/\n/,$hash{'dirlist_files_'.$luri});
    } elsif ($uri=~/\.(page|sequence)\/$/) {
# is a page or a sequence
	$uri=~s/\/$//;
	$uri='/'.(split(/\.(page|sequence)\/\//,$uri))[-1];
	$uri=~s/\/+/\//g;
	foreach (&Apache::lonsequence::attemptread(&Apache::lonnet::filelocation('',$uri))) {
	    my @ratpart=split(/\:/,$_);
	    push(@list,&LONCAPA::map::qtescape($ratpart[1]));
	} 
	$hash{'dirlist_files_'.$luri} = join("\n",@list);
    } else {
# is really a directory
	@list = &Apache::lonnet::dirlist($uri);
	$hash{'dirlist_files_'.$luri} = join("\n",@list);
	$hash{'dirlist_timestamp_files_'.$luri} = time;
    }
    return @list=&match_ext($r,@list);    
}

sub dynmetaread {
    my $uri=shift;
    if (($hash{'display_attrs_8'}==1) || ($hash{'display_attrs_11'}==1)) {
# We don't want the filename
	$uri=~s/\/[^\/]+$//;
# Did we already see this?
	my $builddir=$uri;
	while ($builddir) {
	    if ($dynread{$builddir}) {
		return 0;
	    }
	    $builddir=~s/\/[^\/]+$//;
	}
# Actually get the data
	%dynhash=
	    (%dynhash,&Apache::lonmeta::get_dynamic_metadata_from_sql($uri.'/'));
# Remember that we got it
	$dynread{$uri}=1;
    } 
}

sub initdebug {
    my $start_page=
	&Apache::loncommon::start_page('Debug',undef,
				       {'only_body' => 1,});
    $start_page =~ s/\n/ /g;
    return <<ENDJS;
<script type="text/javascript">
var debugging = true;
if (debugging) {
    var debuggingWindow = window.open('','Debug','width=400,height=300',true);
} 

function output(text) {
    if (debugging) {
        debuggingWindow.document.writeln(text);
    }
}
output('$start_page<pre>');   
</script>
ENDJS
}

sub writedebug {
    my ($text) = @_;
    return "<script type=\"text/javascript\">output('$text');</script>";
}

# -------------------- filters out files based on extensions (returns an array)
sub match_ext {
    my ($r,@packlist)=@_;
    my @trimlist;
    my $nextline;
    my @fileext;
    my $dirptr=16384;

    foreach my $line (@packlist) {
	chomp $line;
	$line =~ s/^\/home\/httpd\/html//;
	my @unpackline = split (/\&/,$line);
	next if ($unpackline[0] eq '.');
	next if ($unpackline[0] eq '..');
	my @filecom = split (/\./,$unpackline[0]);
	my $fext = pop(@filecom);
	my $fnptr = ($unpackline[3]&$dirptr) || ($fext=~/\.(page|sequence)$/);
 	if ($fnptr == 0 and $unpackline[3] ne "") {
	    my $embstyle = &Apache::loncommon::fileembstyle($fext);
            push @trimlist,$line if (defined($embstyle) && 
				     ($embstyle ne 'hdn' or $fext eq 'meta'));
	} else {
	    push @trimlist,$line;
	}
    }
    @trimlist = sort {uc($a) cmp uc($b)} (@trimlist);
    return @trimlist;
}

# ------------------------------- displays one line in appropriate table format
sub display_line {
    my ($r,$diropen,$line,$indent,$startdir,$hashref,@list)=@_;
    my (@pathfn, $fndir);
# there could be relative paths (files actually belonging into this directory)
# or absolute paths (for example, from sequences)
    my $absolute;
    my $pathprefix;
    if ($line=~m|^/res/| && $startdir ne '') {
	$absolute=1;
	$pathprefix='';
    } else {
	$absolute=0;
	$pathprefix=$startdir;
    }
    my $dirptr=16384;
#SB my $fileclr="#ffffe6";
    my $iconpath= $r->dir_config('lonIconsURL') . '/';

    my @filecom = split (/\&/,$line);
    my @pathcom = split (/\//,$filecom[0]);
    my $listname = $pathcom[scalar(@pathcom)-1];
    my $fnptr = $filecom[3]&$dirptr;
    my $msg = &mt('View').' '.$filecom[0].' '.&mt('resources');
    $msg = &mt('Close').' '.$filecom[0].' '.&mt('directory') if $diropen eq 'opened';

    my $tabtag='</td>';
    my $i=0;
    while ($i<=11) {
	$tabtag=join('',$tabtag,"<td>&nbsp;</td>")
	    if ($i != 9 &&
		$hash{'display_attrs_'.$i} == 1);
	$i++;
    }
    my $valign = ($hash{'display_attrs_7'} == 1 ? 'top' : 'bottom');

# display uplink arrow
    if ($filecom[1] eq 'viewOneUp') {
	my $updir=$startdir;
# -------------- Filter out sequence containment in crumbs and "recent folders"
	$updir='/'.(split(/\.(page|sequence)\/\//,$startdir))[-1];
	$updir=~s/\/+/\//g;

#SB	$r->print("<tr valign='$valign' bgcolor=\"$fileclr\">$extrafield");
        $r->print(&Apache::loncommon::start_data_table_row()); # valign="$valign" ?!?
        $r->print($extrafield);
	$r->print("<td>\n");
	$r->print ('<form method="post" name="dirpathUP" action="'.$updir.
		   '/" '.
		   'onSubmit="return rep_dirpath(\'UP\','.
		   'document.forms.fileattr.acts.value)" '.
		   'enctype="application/x-www-form-urlencoded"'.
                   '>'."\n");
	$r->print(&Apache::loncommon::inhibit_menu_check('input'));
	$r->print ('<input type=hidden name=openuri value="'.
		   $startdir.'" />'."\n");
	$r->print ('<input type="hidden" name="acts" value="" />'."\n");
	$r->print ('<input src="'.$iconpath.'arrow_up.gif"');
	$r->print (' name="'.$msg.'" type="image" class="LC_indexer_icon" />'.
		   "\n");
	$r->print(&mt("Up")."</form>$tabtag");
        $r->print(&Apache::loncommon::end_data_table_row());
	return OK;
    }
# Do we have permission to look at this?

    if($filecom[15] ne '1') { return OK if (!&Apache::lonnet::allowed('bre',$pathprefix.$filecom[0])); }

# make absolute links appear on different background
#SB    if ($absolute) { $fileclr='#ccdd99'; }

# display domain
    if ($filecom[1] eq 'domain') {
 	$r->print ('<input type="hidden" name="dirPointer" value="on" />'."\n")
 	    if ($env{'form.dirPointer'} eq "on");
#SB	$r->print("<tr valign='$valign' bgcolor=\"$fileclr\">$extrafield");
	$r->print(&Apache::loncommon::start_data_table_row()); # valign="$valign" ?!?"
        $r->print($extrafield);
	$r->print("<td>");
	&begin_form ($r,$filecom[0]);
	my $anchor = $filecom[0];
	$anchor =~ s/\W//g;
	$r->print ('<a name="'.$anchor.'"></a>');
	$r->print ('<input type="hidden" name="acts" value="" />');
	$r->print ('<input src="'.$iconpath.'folder_pointer_'.
		   $diropen.'.gif"'); 
	$r->print (' name="'.$msg.'" type="image" class="LC_indexer_icon" />'.
		   "\n");
	my $quotable_filecom = &Apache::loncommon::escape_single($filecom[0]);
	$r->print ('<a href="javascript:gothere(\''.$quotable_filecom.
		   '\')"><img alt="" src="'.$iconpath.'server.gif"');
	$r->print (' class="LC_icon" /></a>'."\n");
	$r->print (&mt("Domain")." - $listname ");
	if (&Apache::lonnet::domain($listname,'description')) {
	    $r->print("(".&Apache::lonnet::domain($listname,'description').
		      ")");
	}
	$r->print("</form>$tabtag");
         $r->print(&Apache::loncommon::end_data_table_row());
	return OK;

# display user directory
    }
    if ($filecom[1] eq 'user') {
	# $r->print("<tr valign=$valign bgcolor=\"$fileclr\">$extrafield");
	my $curdir = $startdir.$filecom[0].'/';
	my $anchor = $curdir;
	$anchor =~ s/\W//g;
#SB	$r->print("<tr bgcolor=\"$fileclr\">$extrafield<td valign=$valign>");
        $r->print(&Apache::loncommon::start_data_table_row()
                 .$extrafield.'<td valign="'.$valign.'">');
	&begin_form ($r,$curdir);
	$r->print ('<a name="'.$anchor.'"></a><img alt="" src="'.$iconpath.
		   'whitespace1.gif" class="LC_icon" />'."\n");
	$r->print ('<input type="hidden" name="acts" value="" />');
	$r->print ('<input src="'.$iconpath.'folder_pointer_'.$diropen.
		   '.gif"'); 
	$r->print (' name="'.$msg.'" type="image" class="LC_indexer_icon" />'.
		   "\n");
	my $quotable_curdir = &Apache::loncommon::escape_single($curdir);
	$r->print ('<a href="javascript:gothere(\''.$quotable_curdir
		   .'\')"><img alt="'.$msg.'" src="'.
		   $iconpath.'quill.gif" class="LC_indexer_icon" /></a>');
	my $domain=(split(m|/|,$startdir))[2];
	my $plainname=&Apache::loncommon::plainname($listname,$domain);
	$r->print ($listname);
	if (defined($plainname) && $plainname) { $r->print(" ($plainname) "); }
	$r->print('</form>'.$tabtag);
        $r->print(&Apache::loncommon::end_data_table_row());
	return OK;
    }

# display file
    if (($fnptr == 0 and $filecom[3] ne '') or $absolute) {
        my $title;
	my $filelink = $pathprefix.$filecom[0];
	if ($hash{'display_attrs_0'} == 1) {
	    $title = &Apache::lonnet::gettitle($filelink);
	}
	my @file_ext = split (/\./,$listname);
	my $curfext = $file_ext[-1];
        if (@Omit) {
            foreach (@Omit) { return OK if (lc($curfext) eq $_); }
        }
        if (@Only) {
            my $skip = 1;
            foreach (@Only) { $skip = 0 if (lc($curfext) eq $_); }
            return OK if ($skip > 0);
        }
	# Set the icon for the file
	my $iconname = &Apache::loncommon::icon($listname);
#SB	$r->print("<tr valign='$valign' bgcolor=\"$fileclr\"><td nowrap='nowrap' valign='top'>");
	$r->print(&Apache::loncommon::start_data_table_row()); # valign="$valign" ?!?"
        $r->print('<td nowrap="nowrap" valign="top">');
	
        if ($env{'form.catalogmode'} eq 'interactive') {
	    my $quotable_filelink = &Apache::loncommon::escape_single($filelink);
            $r->print("<a href=\"javascript:select_data(\'",
                      $quotable_filelink,"')\">");
	    $r->print("<img alt=\"\" src='",$iconpath,"select.gif' class='LC_icon' /></a>".
		      "\n");
	    $r->print("</td><td nowrap='nowrap' valign='top'>");
	} elsif ($env{'form.catalogmode'} eq 'import') {
	    $r->print("<form name='form$fnum' action=''>\n");
	    $r->print("<input type='checkbox' name='filelink"."' ".
		      "value='$filelink' onClick='".
		      "javascript:queue(\"form$fnum\")' ");
	    if ($hash{'store_'.$filelink}) {
		$r->print("checked");
	    }
	    $r->print(" />\n");
	    $r->print("</form></td><td nowrap='nowrap' valign='top'>");
	    $hash{"pre_${fnum}_link"}=$filelink;
	    $hash{"pre_${fnum}_title"}=$title;
	    if (!$hash{"pre_${fnum}_title"}) {
	        $hash{"pre_${fnum}_title"} = 'Not_retrieved';
	    }
  	    $fnum++;
	}
# Form to open or close sequences
	if ($filelink=~/\.(page|sequence)$/) {
	    my $curdir = $startdir.$filecom[0].'/';
	    &begin_form($r,$curdir);
	    $indent--;
	}
# General indentation
	if ($indent > 0 and $indent < 11) {
	    $r->print("<img alt=\"\" src='",$iconpath,"whitespace",$indent,
		      ".gif' class='LC_icon' />\n");
	} elsif ($indent >0) {
	    my $ten = int($indent/10.);
	    my $rem = $indent%10.0;
	    my $count = 0;
	    while ($count < $ten) {
		$r->print("<img alt=\"\" src=".$iconpath.
			  "whitespace10.gif class='LC_icon' />\n");
	    $count++;
	    }
	    $r->print("<img alt=\"\" src=",$iconpath,"whitespace",$rem,
		      ".gif class='LC_icon' />\n") if $rem > 0;
	}
# Sequence open/close icon
	if ($filelink=~/\.(page|sequence)$/) {
	    my $curdir = $startdir.$filecom[0].'/';
	    my $anchor = $curdir;
	    $anchor =~ s/\W//g;
	    $r->print ('<input type="hidden" name="acts" value="" />');
	    $r->print ('<a name="'.$anchor.'"></a><input src="'.$iconpath.
		       'folder_pointer_'.$diropen.'.gif"');
	    $r->print (' name="'.$msg.'" type="image" class="LC_indexer_icon" />'.
		       "\n");
	}
# Filetype icons
	$r->print("<img alt=\"\" src='$iconname' class='LC_icon' />\n");
	my $quotable_filelink = &Apache::loncommon::escape_single($filelink);

	$r->print (" <a href=\"javascript:openWindow('".$quotable_filelink.
		   "?inhibitmenu=yes','previewfile','450','500','no','yes','yes');\"".
		   " target=\"_self\">$listname</a> ");
	$quotable_filelink = &Apache::loncommon::escape_single($filelink.'.meta');
	&Apache::loncommon::inhibit_menu_check(\$quotable_filelink);
	$r->print (" (<a href=\"javascript:openWindow('".$quotable_filelink.
		   "?inhibitmenu=yes','metadatafile','500','550','no','yes','no');\" ".
		   " target=\"_self\">".&mt('metadata')."</a>) ");
# Close form to open/close sequence
	if ($filelink=~/\.(page|sequence)$/) {
	    $r->print('</form>');
	}
	$r->print("</td>\n");
	if ($hash{'display_attrs_0'} == 1) {
	    $r->print('<td> '.($title eq '' ? '&nbsp;' : $title).
		      ' </td>'."\n");
	}
	$r->print('<td align=right> ',
		  $filecom[8]," </td>\n") 
	    if $hash{'display_attrs_1'} == 1;
	$r->print('<td> '.
                  (&Apache::lonlocal::locallocaltime($filecom[9]))." </td>\n")
	    if $hash{'display_attrs_2'} == 1;
	$r->print('<td> '.
                  (&Apache::lonlocal::locallocaltime($filecom[10]))." </td>\n")
	    if $hash{'display_attrs_3'} == 1;

	if ($hash{'display_attrs_4'} == 1) {
	    my $author = &Apache::lonnet::metadata($filelink,'author');
	    $r->print('<td> '.($author eq '' ? '&nbsp;' : $author).
		      " </td>\n");
	}
	if ($hash{'display_attrs_5'} == 1) {
	    my $keywords = &Apache::lonnet::metadata($filelink,'keywords');
	    # $keywords = '&nbsp;' if (!$keywords);
	    $r->print('<td> '.($keywords eq '' ? '&nbsp;' : $keywords).
		      " </td>\n");
	}
#'

	if ($hash{'display_attrs_6'} == 1) {
	    my $lang = &Apache::lonnet::metadata($filelink,'language');
	    $lang = &Apache::loncommon::languagedescription($lang);
	    $r->print('<td> '.($lang eq '' ? '&nbsp;' : $lang).
		      " </td>\n");
	}
	if ($hash{'display_attrs_8'} == 1) {
# statistics
	    &dynmetaread($filelink);
	    $r->print("<td>");
	    &dynmetaprint($r,$filelink,'count');
	    &dynmetaprint($r,$filelink,'course');
	    &dynmetaprint($r,$filelink,'stdno');
	    &dynmetaprint($r,$filelink,'avetries');
	    &dynmetaprint($r,$filelink,'difficulty');
	    &dynmetaprint($r,$filelink,'disc');
	    &dynmetaprint($r,$filelink,'clear');
	    &dynmetaprint($r,$filelink,'technical');
	    &dynmetaprint($r,$filelink,'correct');
	    &dynmetaprint($r,$filelink,'helpful');
	    &dynmetaprint($r,$filelink,'depth');
	    $r->print("&nbsp;</td>\n");

	}
	if ($hash{'display_attrs_10'} == 1) {
	    my $source = &Apache::lonnet::metadata($filelink,'sourceavail');
	    if($source eq 'open') {
		my $sourcelink = &Apache::lonsource::make_link($filelink,$listname);
		my $quotable_sourcelink = &Apache::loncommon::escape_single($sourcelink);
		&Apache::loncommon::inhibit_menu_check(\$quotable_sourcelink);
		$r->print('<td>'."<a href=\"javascript:openWindow('"
			  .$quotable_sourcelink.
			  "', 'previewsource', '700', '700', 'no', 'yes','yes');\"".
			  " target=\"_self\">".&mt('Source Code')."</a> "."</td>\n");
	    } else { #A cuddled else. :P
		$r->print("<td>&nbsp;</td>\n");
	    }
	}
	if ($hash{'display_attrs_11'} == 1) {
# links
	   &dynmetaread($filelink);
	   $r->print('<td>');
	   &coursecontext($r,$filelink);
	   &dynmetaprint($r,$filelink,'goto_list');
	   &dynmetaprint($r,$filelink,'comefrom_list');
	   &dynmetaprint($r,$filelink,'sequsage_list');
	   &dynmetaprint($r,$filelink,'dependencies');
           &dynmetaprint($r,$filelink,'course_list');
	   $r->print('</td>');
        }
        if ($hash{'display_attrs_7'} == 1) {
# Show resource
	   my $output=&showpreview($filelink);
           $r->print('<td> '.($output eq '' ? '&nbsp;':$output).
		      " </td>\n");
        }
        $r->print(&Apache::loncommon::end_data_table_row());
    }

# -- display directory
    if ($fnptr == $dirptr) {
	my $curdir = $startdir.$filecom[0].'/';
	my $anchor = $curdir;
	$anchor =~ s/\W//g;
#SB	$r->print("<tr bgcolor=\"$fileclr\">$extrafield<td valign=$valign>");
        $r->print(&Apache::loncommon::start_data_table_row()); # SB: bgcolor suggestion: darkgrey ("LC_info_row"?!?)
#	$r->print('<tr class="LC_info_row">');
        $r->print($extrafield.'<td valign="'.$valign.'">');
	&begin_form ($r,$curdir);
	my $indentm1 = $indent-1;
	if ($indentm1 < 11 and $indentm1 > 0) {
	    $r->print("<img alt=\"\" src=",$iconpath,"whitespace",$indentm1,
		      ".gif class='LC_icon' />\n");
	} else {
	    my $ten = int($indentm1/10.);
	    my $rem = $indentm1%10.0;
	    my $count = 0;
	    while ($count < $ten) {
		$r->print ("<img alt=\"\" src=",$iconpath
			   ,"whitespace10.gif class='LC_icon' />\n");
		$count++;
	    }
	    $r->print ("<img alt=\"\" src=",$iconpath,"whitespace",$rem,
		       ".gif class='LC_icon' />\n") if $rem > 0;
	}
	$r->print ('<input type="hidden" name="acts" value="" />');
	$r->print ('<a name="'.$anchor.'"></a><input src="'.$iconpath.
		   'folder_pointer_'.$diropen.'.gif"');
	$r->print (' name="'.$msg.'" type="image" class="LC_indexer_icon" />'.
		   "\n");
	my $quotable_curdir = &Apache::loncommon::escape_single($curdir);
	$r->print ('<a href="javascript:gothere(\''
		   .$quotable_curdir.'\')">'
		   .'<img alt="'.&mt('Open Folder').'"src="'.
		   $iconpath.'folder_'.$diropen.'.gif" class="LC_icon" /></a>'.
		   "\n");
	$r->print ("$listname</form></td>\n");
# Attributes
	my $filelink = $startdir.$filecom[0].'/default';

	if ($hash{'display_attrs_0'} == 1) {
	    my $title = &Apache::lonnet::gettitle($filelink);
	    $r->print('<td> '.($title eq '' ? '&nbsp;' : $title).
		      ' </td>'."\n");
	}
	$r->print('<td align="right"> ',
		  $filecom[8]," </td>\n") 
	    if $hash{'display_attrs_1'} == 1;
	$r->print('<td> '.
                  (&Apache::lonlocal::locallocaltime($filecom[9]))." </td>\n")
	    if $hash{'display_attrs_2'} == 1;
	$r->print('<td> '.
                  (&Apache::lonlocal::locallocaltime($filecom[10]))." </td>\n")
	    if $hash{'display_attrs_3'} == 1;

	if ($hash{'display_attrs_4'} == 1) {
	    my $author = &Apache::lonnet::metadata($filelink,'author');
	    $r->print('<td> '.($author eq '' ? '&nbsp;' : $author).
		      " </td>\n");
	}
	if ($hash{'display_attrs_5'} == 1) {
	    my $keywords = &Apache::lonnet::metadata($filelink,'keywords');
	    # $keywords = '&nbsp;' if (!$keywords);
	    $r->print('<td> '.($keywords eq '' ? '&nbsp;' : $keywords).
		      " </td>\n");
	}
	if ($hash{'display_attrs_6'} == 1) {
	    my $lang = &Apache::lonnet::metadata($filelink,'language');
	    $lang = &Apache::loncommon::languagedescription($lang);
	    $r->print('<td> '.($lang eq '' ? '&nbsp;' : $lang).
		      " </td>\n");
	}
	if ($hash{'display_attrs_8'} == 1) {
	   $r->print('<td>&nbsp;</td>');
	}
 	if ($hash{'display_attrs_10'} == 1) {
	   $r->print('<td>&nbsp;</td>');
	}
	if ($hash{'display_attrs_11'} == 1) {
	   $r->print('<td>&nbsp;</td>');
	}
	if ($hash{'display_attrs_7'} == 1) {
	   $r->print('<td>&nbsp;</td>');
        }
        $r->print(&Apache::loncommon::end_data_table_row());
    }

}

sub coursecontext {
    my ($r,$filelink)=@_;
    my $filesymb=&Apache::lonnet::symbread($filelink);
    if ($filesymb) {
	my ($map,$index,$resource)=&Apache::lonnet::decode_symb($filesymb);
	$r->print(&mt('Already in this course:<br />[_1] in folder/map [_2].<br />',
	      &Apache::lonnet::gettitle($resource),
	      &Apache::lonnet::gettitle($map)));
    }
}

sub showpreview {
    my ($filelink)=@_;
    if ($filelink=~m-^(/ext/|http://)-) {
	return &mt('External Resource, preview not enabled');
    }
    my ($curfext)=($filelink=~/\.(\w+)$/);
    my $output='';
    my $embstyle=&Apache::loncommon::fileembstyle($curfext);
    if ($embstyle eq 'ssi') {
       my $cache=$Apache::lonnet::perlvar{'lonDocRoot'}.$filelink.
                    '.tmp';
       if ((!$env{'form.updatedisplay'}) &&
                    (-e $cache)) {
          open(FH,$cache);
          $output=join("\n",<FH>);
          close(FH);
       } else {
# In update display mode, remove old cache. This is done to retroactively
# clean up course context renderings.
	  if (-e $cache) {
	       unlink($cache);
	  }
          $output=&Apache::lonnet::ssi_body($filelink);
# Is access denied? Don't render, don't store
          if ($output=~/LONCAPAACCESSCONTROLERRORSCREEN/s) {
             $output='';
# Was this rendered in course content? Don't store
          } elsif (!&Apache::lonnet::symbread($filelink)) {
             open(FH,">$cache");
             print FH $output;
             close(FH);
          }
       }
       $output='<font size="-2">'.$output.'</font>';
    } elsif ($embstyle eq 'img') {
       $output='<img alt="'.&mt('Preview').'" src="'.$filelink.'" />';
    } elsif ($filelink=~m{^/res/($match_domain)/($match_username)/}) {
       $output='<img  alt="'.&mt('Preview').'" src="http://'.
                 &Apache::lonnet::hostname(&Apache::lonnet::homeserver($2,$1)).
                 '/cgi-bin/thumbnail.gif?url='.$filelink.'" />';
    }
    return $output;
}

sub dynmetaprint {
    my ($r,$filelink,$item)=@_;
    if ($dynhash{$filelink}->{$item}) {
	$r->print("\n<br />".$fieldnames{$item}.': '.
		  &Apache::lonmeta::prettyprint($item,
						$dynhash{$filelink}->{$item},
		  (($env{'form.catalogmode'} ne 'import')?'preview':''),
		  '',
		  (($env{'form.catalogmode'} eq 'import')?'document.forms.fileattr':''),1));
    }
}

# ------------------- prints the beginning of a form for directory or file link
sub begin_form {
    my ($r,$uri) = @_;
    my $anchor = $uri;
    $anchor =~ s/\W//g;
    $uri=&Apache::loncommon::escape_single($uri);
    $r->print ('<form method="post" name="dirpath'.$dnum.'" action="'.$uri.
	       '#'.$anchor.
	       '" onSubmit="return rep_dirpath(\''.$dnum.'\''.
	       ',document.forms.fileattr.acts.value)" '.
	       'enctype="application/x-www-form-urlencoded">'."\n");
    $r->print ('<input type="hidden" name="openuri" value="'.$uri.'" />'.
	       "\n");
    $r->print ('<input type="hidden" name="dirPointer" value="on" />'."\n");
    $r->print(&Apache::loncommon::inhibit_menu_check('input'));
    $dnum++;
}

# --------- settings whenever the user causes the indexer window to be launched
sub start_fresh_session {
    my ($hash) = @_;
    delete $hash->{'form.catalogmode'};
    delete $hash->{'form.mode'};
    delete $hash->{'form.form'};
    delete $hash->{'form.element'};
    delete $hash->{'form.omit'};
    delete $hash->{'form.only'};
    foreach (keys %{$hash}) {
        delete $hash->{$_} if (/^(pre_|store)/);
    }
}

# ------------------------------------------------------------------- setvalues
sub setvalues {
    # setvalues is used in registerurl to synchronize the database
    # hash and environment hashes
    my ($H1,$h1key,$H2,$h2key) =@_;
    #
    if (exists $H2->{$h2key}) {
	$H1->{$h1key} = $H2->{$h2key};
    } elsif (exists $H1->{$h1key}) {
	$H2->{$h2key} = $H1->{$h1key};
    } 
}

1;

sub cleanup {
    if (tied(%hash)){
	&Apache::lonnet::logthis('Cleanup indexer: hash');
    }
    return OK;
}





=head1 NAME

Apache::lonindexer - mod_perl module for cross server filesystem browsing

=head1 SYNOPSIS

Invoked by /etc/httpd/conf/srm.conf:

 <LocationMatch "^/res.*/$">
 SetHandler perl-script
 PerlHandler Apache::lonindexer
 </LocationMatch>

=head1 INTRODUCTION

This module enables a scheme of browsing across a cross server.

This is part of the LearningOnline Network with CAPA project
described at http://www.lon-capa.org.

=head1 BEGIN SUBROUTINE

This routine is only run once after compilation.

=over 4

=item *

Initializes %language hash table.

=back

=head1 HANDLER SUBROUTINE

This routine is called by Apache and mod_perl.

=over 4

=item *

read in machine configuration variables

=item *

see if called from an interactive mode

=item *

refresh environment with user database values (in %hash)

=item *

define extra fields and buttons in case of special mode

=item *

set catalogmodefunctions to have extra needed javascript functionality

=item *

print header

=item *

evaluate actions from previous page (both cumulatively and chronologically)

=item *

output title

=item *

get state of file attributes to be showing

=item *

output state of file attributes to be showing

=item *

output starting row to the indexed file/directory hierarchy

=item *

read in what directories have previously been set to "open"

=item *

if not at top level, provide an uplink arrow

=item *

recursively go through all the directories and output as appropriate

=item *

information useful for group import

=item *

end the tables

=item *

end the output and return

=back

=head1 OTHER SUBROUTINES

=over 4

=item *

scanDir - recursive scan of a directory

=item *

get_list - get complete matched list based on the uri (returns an array)

=item *

match_ext - filters out files based on extensions (returns an array)

=item *

display_line - displays one line in appropriate table format

=item *

begin_form - prints the beginning of a form for directory or file link

=item *

start_fresh_session - settings whenever the user causes the indexer window
to be launched

=back

=cut

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>