--- loncom/interface/loncommon.pm 2002/04/22 15:26:46 1.32 +++ loncom/interface/loncommon.pm 2004/12/20 20:53:40 1.244 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.32 2002/04/22 15:26:46 matthew Exp $ +# $Id: loncommon.pm,v 1.244 2004/12/20 20:53:40 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,127 +25,1253 @@ # # http://www.lon-capa.org/ # -# YEAR=2001 -# 2/13-12/7 Guy Albertelli -# 12/11,12/12,12/17 Scott Harrison -# 12/21 Gerd Kortemeyer -# 12/21 Scott Harrison -# 12/25,12/28 Gerd Kortemeyer -# YEAR=2002 -# 1/4 Gerd Kortemeyer # Makes a table out of the previous attempts # Inputs result_from_symbread, user, domain, course_id # Reads in non-network-related .tab files +# POD header: + +=pod + +=head1 NAME + +Apache::loncommon - pile of common routines + +=head1 SYNOPSIS + +Common routines for manipulating connections, student answers, + domains, common Javascript fragments, etc. + +=head1 OVERVIEW + +A collection of commonly used subroutines that don't have a natural +home anywhere else. This collection helps remove +redundancy from other modules and increase efficiency of memory usage. + +=cut + +# End of POD header package Apache::loncommon; use strict; use Apache::lonnet(); -use POSIX qw(strftime); -use Apache::Constants qw(:common); -use Apache::lonmsg(); +use GDBM_File; +use POSIX qw(strftime mktime); +use Apache::Constants qw(:common :http :methods); +use Apache::lonmenu(); +use Apache::lonlocal; +use HTML::Entities; my $readit; +## +## Global Variables +## + # ----------------------------------------------- Filetypes/Languages/Copyright my %language; +my %supported_language; my %cprtag; +my %scprtag; my %fe; my %fd; -my %fc; +my %category_extensions; -# -------------------------------------------------------------- Thesaurus data -my @therelated; -my @theword; -my @thecount; -my %theindex; -my $thetotalcount; -my $thefuzzy=2; -my $thethreshold=0.1/$thefuzzy; -my $theavecount; +# ---------------------------------------------- Designs -# ----------------------------------------------------------------------- BEGIN -BEGIN { +my %designhash; +# ---------------------------------------------- Thesaurus variables +# +# %Keywords: +# A hash used by &keyword to determine if a word is considered a keyword. +# $thesaurus_db_file +# Scalar containing the full path to the thesaurus database. + +my %Keywords; +my $thesaurus_db_file; + +# +# Initialize values from language.tab, copyright.tab, filetypes.tab, +# thesaurus.tab, and filecategories.tab. +# +BEGIN { + # Variable initialization + $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db"; + # unless ($readit) { # ------------------------------------------------------------------- languages { - my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}. - '/language.tab'); - if ($fh) { - while (<$fh>) { - next if /^\#/; - chomp; - my ($key,$val)=(split(/\s+/,$_,2)); - $language{$key}=$val; - } - } + my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}. + '/language.tab'; + if ( open(my $fh,"<$langtabfile") ) { + while (<$fh>) { + next if /^\#/; + chomp; + my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$_)); + $language{$key}=$val.' - '.$enc; + if ($sup) { + $supported_language{$key}=$sup; + } + } + close($fh); + } } # ------------------------------------------------------------------ copyrights { - my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonIncludes'}. - '/copyright.tab'); - if ($fh) { - while (<$fh>) { - next if /^\#/; - chomp; - my ($key,$val)=(split(/\s+/,$_,2)); - $cprtag{$key}=$val; - } - } + my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}. + '/copyright.tab'; + if ( open (my $fh,"<$copyrightfile") ) { + while (<$fh>) { + next if /^\#/; + chomp; + my ($key,$val)=(split(/\s+/,$_,2)); + $cprtag{$key}=$val; + } + close($fh); + } } -# ------------------------------------------------------------- file categories +# ------------------------------------------------------------------ source copyrights { - my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}. - '/filecategories.tab'); - if ($fh) { - while (<$fh>) { - next if /^\#/; - chomp; - my ($key,$val)=(split(/\s+/,$_,2)); - push @{$fc{$key}},$val; - } - } + my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}. + '/source_copyright.tab'; + if ( open (my $fh,"<$sourcecopyrightfile") ) { + while (<$fh>) { + next if /^\#/; + chomp; + my ($key,$val)=(split(/\s+/,$_,2)); + $scprtag{$key}=$val; + } + close($fh); + } } -# ------------------------------------------------------------------ file types + +# -------------------------------------------------------------- domain designs + + my $filename; + my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors'; + opendir(DIR,$designdir); + while ($filename=readdir(DIR)) { + my ($domain)=($filename=~/^(\w+)\./); { - my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}. - '/filetypes.tab'); - if ($fh) { + my $designfile = $designdir.'/'.$filename; + if ( open (my $fh,"<$designfile") ) { while (<$fh>) { - next if (/^\#/); - chomp; - my ($ending,$emb,$descr)=split(/\s+/,$_,3); - if ($descr ne '') { - $fe{$ending}=lc($emb); - $fd{$ending}=$descr; - } - } - } + next if /^\#/; + chomp; + my ($key,$val)=(split(/\=/,$_)); + if ($val) { $designhash{$domain.'.'.$key}=$val; } + } + close($fh); + } + } + } -# -------------------------------------------------------------- Thesaurus data + closedir(DIR); + + +# ------------------------------------------------------------- file categories { - my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}. - '/thesaurus.dat'); - if ($fh) { + my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}. + '/filecategories.tab'; + if ( open (my $fh,"<$categoryfile") ) { while (<$fh>) { - my ($tword,$tindex,$tcount,$trelated)=split(/\@/,$_); - $theindex{$tword}=$tindex; - $theword[$tindex]=$tword; - $thecount[$tindex]=$tcount; - $thetotalcount+=$tcount; - $therelated[$tindex]=$trelated; - } + next if /^\#/; + chomp; + my ($extension,$category)=(split(/\s+/,$_,2)); + push @{$category_extensions{lc($category)}},$extension; + } + close($fh); + } + + } +# ------------------------------------------------------------------ file types + { + my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}. + '/filetypes.tab'; + if ( open (my $fh,"<$typesfile") ) { + while (<$fh>) { + next if (/^\#/); + chomp; + my ($ending,$emb,$descr)=split(/\s+/,$_,3); + if ($descr ne '') { + $fe{$ending}=lc($emb); + $fd{$ending}=$descr; + } + } + close($fh); } - $theavecount=$thetotalcount/$#thecount; } &Apache::lonnet::logthis( - "INFO: Read file types and thesaurus"); + "INFO: Read file types"); $readit=1; -} + } # end of unless($readit) } -# ============================================================= END BEGIN BLOCK + +############################################################### +## HTML and Javascript Helper Functions ## +############################################################### + +=pod + +=head1 HTML and Javascript Functions + +=over 4 + +=item * browser_and_searcher_javascript () + +XXReturns a string +containing javascript with two functions, C and +C. Returned string does not contain EscriptE +tags. + +=item * openbrowser(formname,elementname,only,omit) [javascript] + +inputs: formname, elementname, only, omit + +formname and elementname indicate the name of the html form and name of +the element that the results of the browsing selection are to be placed in. + +Specifying 'only' will restrict the browser to displaying only files +with the given extension. Can be a comma separated list. + +Specifying 'omit' will restrict the browser to NOT displaying files +with the given extension. Can be a comma separated list. + +=item * opensearcher(formname, elementname) [javascript] + +Inputs: formname, elementname + +formname and elementname specify the name of the html form and the name +of the element the selection from the search results will be placed in. + +=cut + +sub browser_and_searcher_javascript { + my ($mode)=@_; + if (!defined($mode)) { $mode='edit'; } + my $resurl=&lastresurl(); + return < +END +} + +sub lastresurl { + if ($ENV{'environment.lastresurl'}) { + return $ENV{'environment.lastresurl'} + } else { + return '/res'; + } +} + +sub storeresurl { + my $resurl=&Apache::lonnet::clutter(shift); + unless ($resurl=~/^\/res/) { return 0; } + $resurl=~s/\/$//; + &Apache::lonnet::put('environment',{'lastresurl' => $resurl}); + &Apache::lonnet::appenv('environment.lastresurl' => $resurl); + return 1; +} + +sub studentbrowser_javascript { + unless ( + (($ENV{'request.course.id'}) && + (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'}))) + || ($ENV{'request.role'}=~/^(au|dc|su)/) + ) { return ''; } + return (<<'ENDSTDBRW'); + +ENDSTDBRW +} + +sub selectstudent_link { + my ($form,$unameele,$udomele)=@_; + if ($ENV{'request.course.id'}) { + unless (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'})) { + return ''; + } + return "".&mt('Select User').""; + } + if ($ENV{'request.role'}=~/^(au|dc|su)/) { + return "".&mt('Select User').""; + } + return ''; +} + +sub coursebrowser_javascript { + my ($domainfilter)=@_; + return (< + var stdeditbrowser; + function opencrsbrowser(formname,uname,udom,desc,extra_element) { + var url = '/adm/pickcourse?'; + var filter; + if (filter != null) { + if (filter != '') { + url += 'filter='+filter+'&'; + } + } + var domainfilter='$domainfilter'; + if (domainfilter != null) { + if (domainfilter != '') { + url += 'domainfilter='+domainfilter+'&'; + } + } + url += 'form=' + formname + '&cnumelement='+uname+ + '&cdomelement='+udom+ + '&cnameelement='+desc; + if (extra_element !=null && extra_element != '' && formname == 'rolechoice') { + url += '&roleelement='+extra_element; + if (domainfilter == null || domainfilter == '') { + url += '&domainfilter='+extra_element; + } + } + var title = 'Course_Browser'; + var options = 'scrollbars=1,resizable=1,menubar=0'; + options += ',width=700,height=600'; + stdeditbrowser = open(url,title,options,'1'); + stdeditbrowser.focus(); + } + +ENDSTDBRW +} + +sub selectcourse_link { + my ($form,$unameele,$udomele,$desc,$extra_element)=@_; + return "".&mt('Select Course').""; +} + +=pod + +=item * linked_select_forms(...) + +linked_select_forms returns a string containing a block +and html for two tags + +=item * $firstdefault, the default value for the first menu + +=item * $firstselectname, the name of the first tag + +=item * $hashref, a reference to a hash containing the data for the menus. + +=back + +Below is an example of such a hash. Only the 'text', 'default', and +'select2' keys must appear as stated. keys(%menu) are the possible +values for the first select menu. The text that coincides with the +first menu value is given in $menu{$choice1}->{'text'}. The values +and text for the second menu are given in the hash pointed to by +$menu{$choice1}->{'select2'}. + + my %menu = ( A1 => { text =>"Choice A1" , + default => "B3", + select2 => { + B1 => "Choice B1", + B2 => "Choice B2", + B3 => "Choice B3", + B4 => "Choice B4" + } + }, + A2 => { text =>"Choice A2" , + default => "C2", + select2 => { + C1 => "Choice C1", + C2 => "Choice C2", + C3 => "Choice C3" + } + }, + A3 => { text =>"Choice A3" , + default => "D6", + select2 => { + D1 => "Choice D1", + D2 => "Choice D2", + D3 => "Choice D3", + D4 => "Choice D4", + D5 => "Choice D5", + D6 => "Choice D6", + D7 => "Choice D7" + } + } + ); + +=cut + +sub linked_select_forms { + my ($formname, + $middletext, + $firstdefault, + $firstselectname, + $secondselectname, + $hashref + ) = @_; + my $second = "document.$formname.$secondselectname"; + my $first = "document.$formname.$firstselectname"; + # output the javascript to do the changing + my $result = ''; + $result.=" +END + # output the initial values for the selection lists + $result .= "\n"; + my %select2 = %{$hashref->{$firstdefault}->{'select2'}}; + $result .= $middletext; + $result .= "\n"; + # return $debug; + return $result; +} # end of sub linked_select_forms { + +=pod + +=item * help_open_topic($topic, $text, $stayOnPage, $width, $height) + +Returns a string corresponding to an HTML link to the given help +$topic, where $topic corresponds to the name of a .tex file in +/home/httpd/html/adm/help/tex, with underscores replaced by +spaces. + +$text will optionally be linked to the same topic, allowing you to +link text in addition to the graphic. If you do not want to link +text, but wish to specify one of the later parameters, pass an +empty string. + +$stayOnPage is a value that will be interpreted as a boolean. If true, +the link will not open a new window. If false, the link will open +a new window using Javascript. (Default is false.) + +$width and $height are optional numerical parameters that will +override the width and height of the popped up window, which may +be useful for certain help topics with big pictures included. + +=cut + +sub help_open_topic { + my ($topic, $text, $stayOnPage, $width, $height) = @_; + $text = "" if (not defined $text); + $stayOnPage = 0 if (not defined $stayOnPage); + if ($ENV{'browser.interface'} eq 'textual' || + $ENV{'environment.remote'} eq 'off' ) { + $stayOnPage=1; + } + $width = 350 if (not defined $width); + $height = 400 if (not defined $height); + my $filename = $topic; + $filename =~ s/ /_/g; + + my $template = ""; + my $link; + + $topic=~s/\W/\_/g; + + if (!$stayOnPage) + { + $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))"; + } + else + { + $link = "/adm/help/${filename}.hlp"; + } + + # Add the text + if ($text ne "") + { + $template .= + "". + "
$text"; + } + + # Add the graphic + my $title = &mt('Online Help'); + my $helpicon=&lonhttpdurl("/adm/help/gif/smallHelp.gif"); + $template .= <<"ENDTEMPLATE"; + (Help: $topic) +ENDTEMPLATE + if ($text ne '') { $template.='
' }; + return $template; + +} + +# This is a quicky function for Latex cheatsheet editing, since it +# appears in at least four places +sub helpLatexCheatsheet { + my $other = shift; + my $addOther = ''; + if ($other) { + $addOther = Apache::loncommon::help_open_topic($other, shift, + undef, undef, 600) . + ''; + } + return '
'. + $addOther . + &Apache::loncommon::help_open_topic("Greek_Symbols",'Greek Symbols', + undef,undef,600) + .''. + &Apache::loncommon::help_open_topic("Other_Symbols",'Other Symbols', + undef,undef,600) + .'
'; +} + +sub help_open_menu { + my ($color,$topic,$component_help,$function,$faq,$bug,$stayOnPage,$width,$height,$text) = @_; + $text = "" if (not defined $text); + $stayOnPage = 0 if (not defined $stayOnPage); + if ($ENV{'browser.interface'} eq 'textual' || + $ENV{'environment.remote'} eq 'off' ) { + $stayOnPage=1; + } + $width = 620 if (not defined $width); + $height = 600 if (not defined $height); + my $link=''; + my $title = &mt('Get help'); + my $origurl = $ENV{'REQUEST_URI'}; + $origurl=~s|^/~|/priv/|; + my $timestamp = time; + foreach (\$color,\$function,\$topic,\$component_help,\$faq,\$bug,\$origurl) { + $$_ = &Apache::lonnet::escape($$_); + } + + if (!$stayOnPage) { + $link = "javascript:helpMenu('open')"; + } else { + $link = "javascript:helpMenu('display')"; + } + my $banner_link = "/adm/helpmenu?page=banner&color=$color&function=$function&topic=$topic&component_help=$component_help&faq=$faq&bug=$bug&origurl=$origurl&stamp=$timestamp&stayonpage=$stayOnPage"; + my $details_link = "/adm/helpmenu?page=body&color=$color&function=$function&topic=$topic&component_help=$component_help&faq=$faq&bug=$bug&origurl=$origurl&stamp=$timestamp"; + my $template; + if ($text ne "") { + $template .= + "". + "
$text"; + } + my $helpicon=&lonhttpdurl("/adm/lonIcons/helpgateway.gif"); + $template .= <<"ENDTEMPLATE"; + + (Help Menu) +ENDTEMPLATE + if ($component_help) { + if (!$text) { + $template=&help_open_topic($component_help,undef,$stayOnPage, + $width,$height).' '.$template; + } else { + my $help_text; + $help_text=&Apache::lonnet::unescape($topic); + $template='
'. + &help_open_topic($component_help,$help_text,$stayOnPage, + $width,$height).''.$template. + '
'; + } + } + if ($text ne '') { $template.='
' }; + return $template; +} + +sub help_open_bug { + my ($topic, $text, $stayOnPage, $width, $height) = @_; + unless ($ENV{'user.adv'}) { return ''; } + unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; } + $text = "" if (not defined $text); + $stayOnPage = 0 if (not defined $stayOnPage); + if ($ENV{'browser.interface'} eq 'textual' || + $ENV{'environment.remote'} eq 'off' ) { + $stayOnPage=1; + } + $width = 600 if (not defined $width); + $height = 600 if (not defined $height); + + $topic=~s/\W+/\+/g; + my $link=''; + my $template=''; + my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&bug_file_loc='. + &Apache::lonnet::escape($ENV{'REQUEST_URI'}).'&component='.$topic; + if (!$stayOnPage) + { + $link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))"; + } + else + { + $link = $url; + } + # Add the text + if ($text ne "") + { + $template .= + "". + "
$text"; + } + + # Add the graphic + my $title = &mt('Report a Bug'); + my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif"); + $template .= <<"ENDTEMPLATE"; + (Bug: $topic) +ENDTEMPLATE + if ($text ne '') { $template.='
' }; + return $template; + +} + +sub help_open_faq { + my ($topic, $text, $stayOnPage, $width, $height) = @_; + unless ($ENV{'user.adv'}) { return ''; } + unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; } + $text = "" if (not defined $text); + $stayOnPage = 0 if (not defined $stayOnPage); + if ($ENV{'browser.interface'} eq 'textual' || + $ENV{'environment.remote'} eq 'off' ) { + $stayOnPage=1; + } + $width = 350 if (not defined $width); + $height = 400 if (not defined $height); + + $topic=~s/\W+/\+/g; + my $link=''; + my $template=''; + my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html'; + if (!$stayOnPage) + { + $link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))"; + } + else + { + $link = $url; + } + + # Add the text + if ($text ne "") + { + $template .= + "". + "
$text"; + } + + # Add the graphic + my $title = &mt('View the FAQ'); + my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif"); + $template .= <<"ENDTEMPLATE"; + (FAQ: $topic) +ENDTEMPLATE + if ($text ne '') { $template.='
' }; + return $template; + +} + +############################################################### +############################################################### + +=pod + +=item * csv_translate($text) + +Translate $text to allow it to be output as a 'comma separated values' +format. + +=cut + +############################################################### +############################################################### +sub csv_translate { + my $text = shift; + $text =~ s/\"/\"\"/g; + $text =~ s/\n/ /g; + return $text; +} + + +############################################################### +############################################################### + +=pod + +=item * define_excel_formats + +Define some commonly used Excel cell formats. + +Currently supported formats: + +=over 4 + +=item header + +=item bold + +=item h1 + +=item h2 + +=item h3 + +=item date + +=back + +Inputs: $workbook + +Returns: $format, a hash reference. + +=cut + +############################################################### +############################################################### +sub define_excel_formats { + my ($workbook) = @_; + my $format; + $format->{'header'} = $workbook->add_format(bold => 1, + bottom => 1, + align => 'center'); + $format->{'bold'} = $workbook->add_format(bold=>1); + $format->{'h1'} = $workbook->add_format(bold=>1, size=>18); + $format->{'h2'} = $workbook->add_format(bold=>1, size=>16); + $format->{'h3'} = $workbook->add_format(bold=>1, size=>14); + $format->{'date'} = $workbook->add_format(num_format=> + 'mm/dd/yyyy hh:mm:ss'); + return $format; +} + +############################################################### +############################################################### + +=pod + +=item * change_content_javascript(): + +This and the next function allow you to create small sections of an +otherwise static HTML page that you can update on the fly with +Javascript, even in Netscape 4. + +The Javascript fragment returned by this function (no EscriptE tag) +must be written to the HTML page once. It will prove the Javascript +function "change(name, content)". Calling the change function with the +name of the section +you want to update, matching the name passed to C, and +the new content you want to put in there, will put the content into +that area. + +B: Netscape 4 only reserves enough space for the changable area +to contain room for the original contents. You need to "make space" +for whatever changes you wish to make, and be B to check your +code in Netscape 4. This feature in Netscape 4 is B powerful; +it's adequate for updating a one-line status display, but little more. +This script will set the space to 100% width, so you only need to +worry about height in Netscape 4. + +Modern browsers are much less limiting, and if you can commit to the +user not using Netscape 4, this feature may be used freely with +pretty much any HTML. + +=cut + +sub change_content_javascript { + # If we're on Netscape 4, we need to use Layer-based code + if ($ENV{'browser.type'} eq 'netscape' && + $ENV{'browser.version'} =~ /^4\./) { + return (<. $name is +the name you will use to reference the area later; do not repeat the +same name on a given HTML page more then once. $origContent is what +the area will originally contain, which can be left blank. + +=cut + +sub changable_area { + my ($name, $origContent) = @_; + + if ($ENV{'browser.type'} eq 'netscape' && + $ENV{'browser.version'} =~ /^4\./) { + # If this is netscape 4, we need to use the Layer tag + return "$origContent"; + } else { + return "$origContent"; + } +} + +=pod + +=back + +=cut + +############################################################### +## Home server \n"; + } + $selectform.=""; + return $selectform; +} + +sub gradeleveldescription { + my $gradelevel=shift; + my %gradelevels=(0 => 'Not specified', + 1 => 'Grade 1', + 2 => 'Grade 2', + 3 => 'Grade 3', + 4 => 'Grade 4', + 5 => 'Grade 5', + 6 => 'Grade 6', + 7 => 'Grade 7', + 8 => 'Grade 8', + 9 => 'Grade 9', + 10 => 'Grade 10', + 11 => 'Grade 11', + 12 => 'Grade 12', + 13 => 'Grade 13', + 14 => '100 Level', + 15 => '200 Level', + 16 => '300 Level', + 17 => '400 Level', + 18 => 'Graduate Level'); + return &mt($gradelevels{$gradelevel}); +} + +sub select_level_form { + my ($deflevel,$name)=@_; + unless ($deflevel) { $deflevel=0; } + my $selectform = ""; + return $selectform; +} + +#------------------------------------------- + +=pod + +=item * select_dom_form($defdom,$name,$includeempty) + +Returns a string containing a \n"; + foreach (@domains) { + $selectdomain.="\n"; + } + $selectdomain.=""; + return $selectdomain; +} + +#------------------------------------------- + +=pod + +=item * get_library_servers($domain) + +Returns a hash which contains keys like '103l3' and values like +'kirk.lite.msu.edu'. All of the keys will be for machines in the +given $domain. + +=cut + +#------------------------------------------- +sub get_library_servers { + my $domain = shift; + my %library_servers; + foreach (keys(%Apache::lonnet::libserv)) { + if ($Apache::lonnet::hostdom{$_} eq $domain) { + $library_servers{$_} = $Apache::lonnet::hostname{$_}; + } + } + return %library_servers; +} + +#------------------------------------------- + +=pod + +=item * home_server_option_list($domain) + +returns a string which contains an