Annotation of loncom/interface/loncommon.pm, revision 1.658

1.10      albertel    1: # The LearningOnline Network with CAPA
1.1       albertel    2: # a pile of common routines
1.10      albertel    3: #
1.658   ! raeburn     4: # $Id: loncommon.pm,v 1.657 2008/05/29 19:35:53 raeburn Exp $
1.10      albertel    5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
1.1       albertel   28: 
                     29: # Makes a table out of the previous attempts
1.2       albertel   30: # Inputs result_from_symbread, user, domain, course_id
1.16      harris41   31: # Reads in non-network-related .tab files
1.1       albertel   32: 
1.35      matthew    33: # POD header:
                     34: 
1.45      matthew    35: =pod
                     36: 
1.35      matthew    37: =head1 NAME
                     38: 
                     39: Apache::loncommon - pile of common routines
                     40: 
                     41: =head1 SYNOPSIS
                     42: 
1.112     bowersj2   43: Common routines for manipulating connections, student answers,
                     44:     domains, common Javascript fragments, etc.
1.35      matthew    45: 
1.112     bowersj2   46: =head1 OVERVIEW
1.35      matthew    47: 
1.112     bowersj2   48: A collection of commonly used subroutines that don't have a natural
                     49: home anywhere else. This collection helps remove
1.35      matthew    50: redundancy from other modules and increase efficiency of memory usage.
                     51: 
                     52: =cut 
                     53: 
                     54: # End of POD header
1.1       albertel   55: package Apache::loncommon;
                     56: 
                     57: use strict;
1.258     albertel   58: use Apache::lonnet;
1.46      matthew    59: use GDBM_File;
1.51      www        60: use POSIX qw(strftime mktime);
1.82      www        61: use Apache::lonmenu();
1.498     albertel   62: use Apache::lonenc();
1.117     www        63: use Apache::lonlocal;
1.139     matthew    64: use HTML::Entities;
1.334     albertel   65: use Apache::lonhtmlcommon();
                     66: use Apache::loncoursedata();
1.344     albertel   67: use Apache::lontexconvert();
1.444     albertel   68: use Apache::lonclonecourse();
1.479     albertel   69: use LONCAPA qw(:DEFAULT :match);
1.657     raeburn    70: use DateTime::TimeZone;
1.117     www        71: 
1.517     raeburn    72: # ---------------------------------------------- Designs
                     73: use vars qw(%defaultdesign);
                     74: 
1.22      www        75: my $readit;
                     76: 
1.517     raeburn    77: 
1.157     matthew    78: ##
                     79: ## Global Variables
                     80: ##
1.46      matthew    81: 
1.643     foxr       82: 
                     83: # ----------------------------------------------- SSI with retries:
                     84: #
                     85: 
                     86: =pod
                     87: 
1.648     raeburn    88: =head1 Server Side include with retries:
1.643     foxr       89: 
                     90: =over 4
                     91: 
1.648     raeburn    92: =item * &ssi_with_retries(resource,retries form)
1.643     foxr       93: 
                     94: Performs an ssi with some number of retries.  Retries continue either
                     95: until the result is ok or until the retry count supplied by the
                     96: caller is exhausted.  
                     97: 
                     98: Inputs:
1.648     raeburn    99: 
                    100: =over 4
                    101: 
1.643     foxr      102: resource   - Identifies the resource to insert.
1.648     raeburn   103: 
1.643     foxr      104: retries    - Count of the number of retries allowed.
1.648     raeburn   105: 
1.643     foxr      106: form       - Hash that identifies the rendering options.
                    107: 
1.648     raeburn   108: =back
                    109: 
                    110: Returns:
                    111: 
                    112: =over 4
                    113: 
1.643     foxr      114: content    - The content of the response.  If retries were exhausted this is empty.
1.648     raeburn   115: 
1.643     foxr      116: response   - The response from the last attempt (which may or may not have been successful.
                    117: 
1.648     raeburn   118: =back
                    119: 
                    120: =back
                    121: 
1.643     foxr      122: =cut
                    123: 
                    124: sub ssi_with_retries {
                    125:     my ($resource, $retries, %form) = @_;
                    126: 
                    127: 
                    128:     my $ok = 0;			# True if we got a good response.
                    129:     my $content;
                    130:     my $response;
                    131: 
                    132:     # Try to get the ssi done. within the retries count:
                    133: 
                    134:     do {
                    135: 	($content, $response) = &Apache::lonnet::ssi($resource, %form);
                    136: 	$ok      = $response->is_success;
1.650     www       137:         if (!$ok) {
                    138:             &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
                    139:         }
1.643     foxr      140: 	$retries--;
                    141:     } while (!$ok && ($retries > 0));
                    142: 
                    143:     if (!$ok) {
                    144: 	$content = '';		# On error return an empty content.
                    145:     }
                    146:     return ($content, $response);
                    147: 
                    148: }
                    149: 
                    150: 
                    151: 
1.20      www       152: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12      harris41  153: my %language;
1.124     www       154: my %supported_language;
1.12      harris41  155: my %cprtag;
1.192     taceyjo1  156: my %scprtag;
1.351     www       157: my %fe; my %fd; my %fm;
1.41      ng        158: my %category_extensions;
1.12      harris41  159: 
1.46      matthew   160: # ---------------------------------------------- Thesaurus variables
1.144     matthew   161: #
                    162: # %Keywords:
                    163: #      A hash used by &keyword to determine if a word is considered a keyword.
                    164: # $thesaurus_db_file 
                    165: #      Scalar containing the full path to the thesaurus database.
1.46      matthew   166: 
                    167: my %Keywords;
                    168: my $thesaurus_db_file;
                    169: 
1.144     matthew   170: #
                    171: # Initialize values from language.tab, copyright.tab, filetypes.tab,
                    172: # thesaurus.tab, and filecategories.tab.
                    173: #
1.18      www       174: BEGIN {
1.46      matthew   175:     # Variable initialization
                    176:     $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
                    177:     #
1.22      www       178:     unless ($readit) {
1.12      harris41  179: # ------------------------------------------------------------------- languages
                    180:     {
1.158     raeburn   181:         my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    182:                                    '/language.tab';
                    183:         if ( open(my $fh,"<$langtabfile") ) {
1.356     albertel  184:             while (my $line = <$fh>) {
                    185:                 next if ($line=~/^\#/);
                    186:                 chomp($line);
                    187:                 my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$line));
1.158     raeburn   188:                 $language{$key}=$val.' - '.$enc;
                    189:                 if ($sup) {
                    190:                     $supported_language{$key}=$sup;
                    191:                 }
                    192:             }
                    193:             close($fh);
                    194:         }
1.12      harris41  195:     }
                    196: # ------------------------------------------------------------------ copyrights
                    197:     {
1.158     raeburn   198:         my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    199:                                   '/copyright.tab';
                    200:         if ( open (my $fh,"<$copyrightfile") ) {
1.356     albertel  201:             while (my $line = <$fh>) {
                    202:                 next if ($line=~/^\#/);
                    203:                 chomp($line);
                    204:                 my ($key,$val)=(split(/\s+/,$line,2));
1.158     raeburn   205:                 $cprtag{$key}=$val;
                    206:             }
                    207:             close($fh);
                    208:         }
1.12      harris41  209:     }
1.351     www       210: # ----------------------------------------------------------- source copyrights
1.192     taceyjo1  211:     {
                    212:         my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    213:                                   '/source_copyright.tab';
                    214:         if ( open (my $fh,"<$sourcecopyrightfile") ) {
1.356     albertel  215:             while (my $line = <$fh>) {
                    216:                 next if ($line =~ /^\#/);
                    217:                 chomp($line);
                    218:                 my ($key,$val)=(split(/\s+/,$line,2));
1.192     taceyjo1  219:                 $scprtag{$key}=$val;
                    220:             }
                    221:             close($fh);
                    222:         }
                    223:     }
1.63      www       224: 
1.517     raeburn   225: # -------------------------------------------------------------- default domain designs
1.63      www       226:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517     raeburn   227:     my $designfile = $designdir.'/default.tab';
                    228:     if ( open (my $fh,"<$designfile") ) {
                    229:         while (my $line = <$fh>) {
                    230:             next if ($line =~ /^\#/);
                    231:             chomp($line);
                    232:             my ($key,$val)=(split(/\=/,$line));
                    233:             if ($val) { $defaultdesign{$key}=$val; }
                    234:         }
                    235:         close($fh);
1.63      www       236:     }
                    237: 
1.15      harris41  238: # ------------------------------------------------------------- file categories
                    239:     {
1.158     raeburn   240:         my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    241:                                   '/filecategories.tab';
                    242:         if ( open (my $fh,"<$categoryfile") ) {
1.356     albertel  243: 	    while (my $line = <$fh>) {
                    244: 		next if ($line =~ /^\#/);
                    245: 		chomp($line);
                    246:                 my ($extension,$category)=(split(/\s+/,$line,2));
1.158     raeburn   247:                 push @{$category_extensions{lc($category)}},$extension;
                    248:             }
                    249:             close($fh);
                    250:         }
                    251: 
1.15      harris41  252:     }
1.12      harris41  253: # ------------------------------------------------------------------ file types
                    254:     {
1.158     raeburn   255:         my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    256:                '/filetypes.tab';
                    257:         if ( open (my $fh,"<$typesfile") ) {
1.356     albertel  258:             while (my $line = <$fh>) {
                    259: 		next if ($line =~ /^\#/);
                    260: 		chomp($line);
                    261:                 my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158     raeburn   262:                 if ($descr ne '') {
                    263:                     $fe{$ending}=lc($emb);
                    264:                     $fd{$ending}=$descr;
1.351     www       265:                     if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158     raeburn   266:                 }
                    267:             }
                    268:             close($fh);
                    269:         }
1.12      harris41  270:     }
1.22      www       271:     &Apache::lonnet::logthis(
1.46      matthew   272:               "<font color=yellow>INFO: Read file types</font>");
1.22      www       273:     $readit=1;
1.46      matthew   274:     }  # end of unless($readit) 
1.32      matthew   275:     
                    276: }
1.112     bowersj2  277: 
1.42      matthew   278: ###############################################################
                    279: ##           HTML and Javascript Helper Functions            ##
                    280: ###############################################################
                    281: 
                    282: =pod 
                    283: 
1.112     bowersj2  284: =head1 HTML and Javascript Functions
1.42      matthew   285: 
1.112     bowersj2  286: =over 4
                    287: 
1.648     raeburn   288: =item * &browser_and_searcher_javascript()
1.112     bowersj2  289: 
                    290: X<browsing, javascript>X<searching, javascript>Returns a string
                    291: containing javascript with two functions, C<openbrowser> and
                    292: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
                    293: tags.
1.42      matthew   294: 
1.648     raeburn   295: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42      matthew   296: 
                    297: inputs: formname, elementname, only, omit
                    298: 
                    299: formname and elementname indicate the name of the html form and name of
                    300: the element that the results of the browsing selection are to be placed in. 
                    301: 
                    302: Specifying 'only' will restrict the browser to displaying only files
1.185     www       303: with the given extension.  Can be a comma separated list.
1.42      matthew   304: 
                    305: Specifying 'omit' will restrict the browser to NOT displaying files
1.185     www       306: with the given extension.  Can be a comma separated list.
1.42      matthew   307: 
1.648     raeburn   308: =item * &opensearcher(formname,elementname) [javascript]
1.42      matthew   309: 
                    310: Inputs: formname, elementname
                    311: 
                    312: formname and elementname specify the name of the html form and the name
                    313: of the element the selection from the search results will be placed in.
1.542     raeburn   314: 
1.42      matthew   315: =cut
                    316: 
                    317: sub browser_and_searcher_javascript {
1.199     albertel  318:     my ($mode)=@_;
                    319:     if (!defined($mode)) { $mode='edit'; }
1.453     albertel  320:     my $resurl=&escape_single(&lastresurl());
1.42      matthew   321:     return <<END;
1.219     albertel  322: // <!-- BEGIN LON-CAPA Internal
1.50      matthew   323:     var editbrowser = null;
1.135     albertel  324:     function openbrowser(formname,elementname,only,omit,titleelement) {
1.170     www       325:         var url = '$resurl/?';
1.42      matthew   326:         if (editbrowser == null) {
                    327:             url += 'launch=1&';
                    328:         }
                    329:         url += 'catalogmode=interactive&';
1.199     albertel  330:         url += 'mode=$mode&';
1.611     albertel  331:         url += 'inhibitmenu=yes&';
1.42      matthew   332:         url += 'form=' + formname + '&';
                    333:         if (only != null) {
                    334:             url += 'only=' + only + '&';
1.217     albertel  335:         } else {
                    336:             url += 'only=&';
                    337: 	}
1.42      matthew   338:         if (omit != null) {
                    339:             url += 'omit=' + omit + '&';
1.217     albertel  340:         } else {
                    341:             url += 'omit=&';
                    342: 	}
1.135     albertel  343:         if (titleelement != null) {
                    344:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  345:         } else {
                    346: 	    url += 'titleelement=&';
                    347: 	}
1.42      matthew   348:         url += 'element=' + elementname + '';
                    349:         var title = 'Browser';
1.435     albertel  350:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   351:         options += ',width=700,height=600';
                    352:         editbrowser = open(url,title,options,'1');
                    353:         editbrowser.focus();
                    354:     }
                    355:     var editsearcher;
1.135     albertel  356:     function opensearcher(formname,elementname,titleelement) {
1.42      matthew   357:         var url = '/adm/searchcat?';
                    358:         if (editsearcher == null) {
                    359:             url += 'launch=1&';
                    360:         }
                    361:         url += 'catalogmode=interactive&';
1.199     albertel  362:         url += 'mode=$mode&';
1.42      matthew   363:         url += 'form=' + formname + '&';
1.135     albertel  364:         if (titleelement != null) {
                    365:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  366:         } else {
                    367: 	    url += 'titleelement=&';
                    368: 	}
1.42      matthew   369:         url += 'element=' + elementname + '';
                    370:         var title = 'Search';
1.435     albertel  371:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   372:         options += ',width=700,height=600';
                    373:         editsearcher = open(url,title,options,'1');
                    374:         editsearcher.focus();
                    375:     }
1.219     albertel  376: // END LON-CAPA Internal -->
1.42      matthew   377: END
1.170     www       378: }
                    379: 
                    380: sub lastresurl {
1.258     albertel  381:     if ($env{'environment.lastresurl'}) {
                    382: 	return $env{'environment.lastresurl'}
1.170     www       383:     } else {
                    384: 	return '/res';
                    385:     }
                    386: }
                    387: 
                    388: sub storeresurl {
                    389:     my $resurl=&Apache::lonnet::clutter(shift);
                    390:     unless ($resurl=~/^\/res/) { return 0; }
                    391:     $resurl=~s/\/$//;
                    392:     &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646     raeburn   393:     &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170     www       394:     return 1;
1.42      matthew   395: }
                    396: 
1.74      www       397: sub studentbrowser_javascript {
1.111     www       398:    unless (
1.258     albertel  399:             (($env{'request.course.id'}) && 
1.302     albertel  400:              (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    401: 	      || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    402: 					  '/'.$env{'request.course.sec'})
                    403: 	      ))
1.258     albertel  404:          || ($env{'request.role'}=~/^(au|dc|su)/)
1.111     www       405:           ) { return ''; }  
1.74      www       406:    return (<<'ENDSTDBRW');
                    407: <script type="text/javascript" language="Javascript" >
                    408:     var stdeditbrowser;
1.558     albertel  409:     function openstdbrowser(formname,uname,udom,roleflag,ignorefilter) {
1.74      www       410:         var url = '/adm/pickstudent?';
                    411:         var filter;
1.558     albertel  412: 	if (!ignorefilter) {
                    413: 	    eval('filter=document.'+formname+'.'+uname+'.value;');
                    414: 	}
1.74      www       415:         if (filter != null) {
                    416:            if (filter != '') {
                    417:                url += 'filter='+filter+'&';
                    418: 	   }
                    419:         }
                    420:         url += 'form=' + formname + '&unameelement='+uname+
                    421:                                     '&udomelement='+udom;
1.111     www       422: 	if (roleflag) { url+="&roles=1"; }
1.102     www       423:         var title = 'Student_Browser';
1.74      www       424:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    425:         options += ',width=700,height=600';
                    426:         stdeditbrowser = open(url,title,options,'1');
                    427:         stdeditbrowser.focus();
                    428:     }
                    429: </script>
                    430: ENDSTDBRW
                    431: }
1.42      matthew   432: 
1.74      www       433: sub selectstudent_link {
1.111     www       434:    my ($form,$unameele,$udomele)=@_;
1.258     albertel  435:    if ($env{'request.course.id'}) {  
1.302     albertel  436:        if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    437: 	   && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    438: 					'/'.$env{'request.course.sec'})) {
1.111     www       439: 	   return '';
                    440:        }
                    441:        return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele.
1.607     albertel  442:         '","'.$udomele.'");'."'>".&mt('Select User')."</a>";
1.74      www       443:    }
1.258     albertel  444:    if ($env{'request.role'}=~/^(au|dc|su)/) {
1.111     www       445:        return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele.
1.119     www       446:         '","'.$udomele.'",1);'."'>".&mt('Select User')."</a>";
1.111     www       447:    }
                    448:    return '';
1.91      www       449: }
                    450: 
1.653     raeburn   451: sub authorbrowser_javascript {
                    452:     return <<"ENDAUTHORBRW";
                    453: <script type="text/javascript">
                    454: var stdeditbrowser;
                    455: 
                    456: function openauthorbrowser(formname,udom) {
                    457:     var url = '/adm/pickauthor?';
                    458:     url += 'form='+formname+'&roledom='+udom;
                    459:     var title = 'Author_Browser';
                    460:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    461:     options += ',width=700,height=600';
                    462:     stdeditbrowser = open(url,title,options,'1');
                    463:     stdeditbrowser.focus();
                    464: }
                    465: 
                    466: </script>
                    467: ENDAUTHORBRW
                    468: }
                    469: 
1.91      www       470: sub coursebrowser_javascript {
1.468     raeburn   471:     my ($domainfilter,$sec_element,$formname)=@_;
1.377     raeburn   472:     my $crs_or_grp_alert = &mt('Please select the type of LON-CAPA entity - Course or Group - for which you wish to add/modify a user role');
1.468     raeburn   473:    my $output = '
1.538     albertel  474: <script type="text/javascript">
1.468     raeburn   475:     var stdeditbrowser;'."\n";
                    476:    $output .= <<"ENDSTDBRW";
1.377     raeburn   477:     function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,crstype) {
1.91      www       478:         var url = '/adm/pickcourse?';
1.468     raeburn   479:         var domainfilter = '';
                    480:         var formid = getFormIdByName(formname);
                    481:         if (formid > -1) {
                    482:             var domid = getIndexByName(formid,udom);
                    483:             if (domid > -1) {
                    484:                 if (document.forms[formid].elements[domid].type == 'select-one') {
                    485:                     domainfilter=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
                    486:                 }
                    487:                 if (document.forms[formid].elements[domid].type == 'hidden') {
                    488:                     domainfilter=document.forms[formid].elements[domid].value;
                    489:                 }
                    490:             }
1.91      www       491:         }
1.128     albertel  492:         if (domainfilter != null) {
                    493:            if (domainfilter != '') {
                    494:                url += 'domainfilter='+domainfilter+'&';
                    495: 	   }
                    496:         }
1.91      www       497:         url += 'form=' + formname + '&cnumelement='+uname+
1.187     albertel  498: 	                            '&cdomelement='+udom+
                    499:                                     '&cnameelement='+desc;
1.468     raeburn   500:         if (extra_element !=null && extra_element != '') {
1.594     raeburn   501:             if (formname == 'rolechoice' || formname == 'studentform') {
1.468     raeburn   502:                 url += '&roleelement='+extra_element;
                    503:                 if (domainfilter == null || domainfilter == '') {
                    504:                     url += '&domainfilter='+extra_element;
                    505:                 }
1.234     raeburn   506:             }
1.468     raeburn   507:             else {
                    508:                 if (formname == 'portform') {
                    509:                     url += '&setroles='+extra_element;
                    510:                 }
                    511:             }     
1.230     raeburn   512:         }
1.293     raeburn   513:         if (multflag !=null && multflag != '') {
                    514:             url += '&multiple='+multflag;
                    515:         }
1.377     raeburn   516:         if (crstype == 'Course/Group') {
                    517:             if (formname == 'cu') {
                    518:                 crstype = document.cu.crstype.options[document.cu.crstype.selectedIndex].value; 
                    519:                 if (crstype == "") {
                    520:                     alert("$crs_or_grp_alert");
                    521:                     return;
                    522:                 }
                    523:             }
                    524:         }
                    525:         if (crstype !=null && crstype != '') {
                    526:             url += '&type='+crstype;
                    527:         }
1.102     www       528:         var title = 'Course_Browser';
1.91      www       529:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    530:         options += ',width=700,height=600';
                    531:         stdeditbrowser = open(url,title,options,'1');
                    532:         stdeditbrowser.focus();
                    533:     }
1.468     raeburn   534: 
                    535:     function getFormIdByName(formname) {
                    536:         for (var i=0;i<document.forms.length;i++) {
                    537:             if (document.forms[i].name == formname) {
                    538:                 return i;
                    539:             }
                    540:         }
                    541:         return -1; 
                    542:     }
                    543: 
                    544:     function getIndexByName(formid,item) {
                    545:         for (var i=0;i<document.forms[formid].elements.length;i++) {
                    546:             if (document.forms[formid].elements[i].name == item) {
                    547:                 return i;
                    548:             }
                    549:         }
                    550:         return -1;
                    551:     }
1.91      www       552: ENDSTDBRW
1.468     raeburn   553:     if ($sec_element ne '') {
                    554:         $output .= &setsec_javascript($sec_element,$formname);
                    555:     }
                    556:     $output .= '
                    557: </script>';
                    558:     return $output;
                    559: }
                    560: 
                    561: sub setsec_javascript {
                    562:     my ($sec_element,$formname) = @_;
                    563:     my $setsections = qq|
                    564: function setSect(sectionlist) {
1.629     raeburn   565:     var sectionsArray = new Array();
                    566:     if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
                    567:         sectionsArray = sectionlist.split(",");
                    568:     }
1.468     raeburn   569:     var numSections = sectionsArray.length;
                    570:     document.$formname.$sec_element.length = 0;
                    571:     if (numSections == 0) {
                    572:         document.$formname.$sec_element.multiple=false;
                    573:         document.$formname.$sec_element.size=1;
                    574:         document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
                    575:     } else {
                    576:         if (numSections == 1) {
                    577:             document.$formname.$sec_element.multiple=false;
                    578:             document.$formname.$sec_element.size=1;
                    579:             document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
                    580:             document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
                    581:             document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
                    582:         } else {
                    583:             for (var i=0; i<numSections; i++) {
                    584:                 document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
                    585:             }
                    586:             document.$formname.$sec_element.multiple=true
                    587:             if (numSections < 3) {
                    588:                 document.$formname.$sec_element.size=numSections;
                    589:             } else {
                    590:                 document.$formname.$sec_element.size=3;
                    591:             }
                    592:             document.$formname.$sec_element.options[0].selected = false
                    593:         }
                    594:     }
1.91      www       595: }
1.468     raeburn   596: |;
                    597:     return $setsections;
                    598: }
                    599: 
1.91      www       600: 
                    601: sub selectcourse_link {
1.377     raeburn   602:    my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype)=@_;
1.492     albertel  603:    return "<a href='".'javascript:opencrsbrowser("'.$form.'","'.$unameele.
                    604:         '","'.$udomele.'","'.$desc.'","'.$extra_element.'","'.$multflag.'","'.$selecttype.'");'."'>".&mt('Select Course')."</a>";
1.74      www       605: }
1.42      matthew   606: 
1.653     raeburn   607: sub selectauthor_link {
                    608:    my ($form,$udom)=@_;
                    609:    return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
                    610:           &mt('Select Author').'</a>';
                    611: }
                    612: 
1.273     raeburn   613: sub check_uncheck_jscript {
                    614:     my $jscript = <<"ENDSCRT";
                    615: function checkAll(field) {
                    616:     if (field.length > 0) {
                    617:         for (i = 0; i < field.length; i++) {
                    618:             field[i].checked = true ;
                    619:         }
                    620:     } else {
                    621:         field.checked = true
                    622:     }
                    623: }
                    624:  
                    625: function uncheckAll(field) {
                    626:     if (field.length > 0) {
                    627:         for (i = 0; i < field.length; i++) {
                    628:             field[i].checked = false ;
1.543     albertel  629:         }
                    630:     } else {
1.273     raeburn   631:         field.checked = false ;
                    632:     }
                    633: }
                    634: ENDSCRT
                    635:     return $jscript;
                    636: }
                    637: 
1.656     www       638: sub select_timezone {
                    639:    my ($name,$selected,$onchange)=@_;
                    640:    my $output="<select name='$name' $onchange>\n";
1.657     raeburn   641:    my @timezones = DateTime::TimeZone->all_names;
                    642:    foreach my $tzone (@timezones) {
                    643:        $output.= '<option value="'.$tzone.'"';
                    644:        if ($tzone eq $selected) {
                    645:            $output.=' selected="selected"';
                    646:        }
                    647:        $output.=">$tzone</option>\n";
1.656     www       648:    }
                    649:    $output.="</select>";
                    650:    return $output;
                    651: }
1.273     raeburn   652: 
1.42      matthew   653: =pod
1.36      matthew   654: 
1.648     raeburn   655: =item * &linked_select_forms(...)
1.36      matthew   656: 
                    657: linked_select_forms returns a string containing a <script></script> block
                    658: and html for two <select> menus.  The select menus will be linked in that
                    659: changing the value of the first menu will result in new values being placed
                    660: in the second menu.  The values in the select menu will appear in alphabetical
1.609     raeburn   661: order unless a defined order is provided.
1.36      matthew   662: 
                    663: linked_select_forms takes the following ordered inputs:
                    664: 
                    665: =over 4
                    666: 
1.112     bowersj2  667: =item * $formname, the name of the <form> tag
1.36      matthew   668: 
1.112     bowersj2  669: =item * $middletext, the text which appears between the <select> tags
1.36      matthew   670: 
1.112     bowersj2  671: =item * $firstdefault, the default value for the first menu
1.36      matthew   672: 
1.112     bowersj2  673: =item * $firstselectname, the name of the first <select> tag
1.36      matthew   674: 
1.112     bowersj2  675: =item * $secondselectname, the name of the second <select> tag
1.36      matthew   676: 
1.112     bowersj2  677: =item * $hashref, a reference to a hash containing the data for the menus.
1.36      matthew   678: 
1.609     raeburn   679: =item * $menuorder, the order of values in the first menu
                    680: 
1.41      ng        681: =back 
                    682: 
1.36      matthew   683: Below is an example of such a hash.  Only the 'text', 'default', and 
                    684: 'select2' keys must appear as stated.  keys(%menu) are the possible 
                    685: values for the first select menu.  The text that coincides with the 
1.41      ng        686: first menu value is given in $menu{$choice1}->{'text'}.  The values 
1.36      matthew   687: and text for the second menu are given in the hash pointed to by 
                    688: $menu{$choice1}->{'select2'}.  
                    689: 
1.112     bowersj2  690:  my %menu = ( A1 => { text =>"Choice A1" ,
                    691:                        default => "B3",
                    692:                        select2 => { 
                    693:                            B1 => "Choice B1",
                    694:                            B2 => "Choice B2",
                    695:                            B3 => "Choice B3",
                    696:                            B4 => "Choice B4"
1.609     raeburn   697:                            },
                    698:                        order => ['B4','B3','B1','B2'],
1.112     bowersj2  699:                    },
                    700:                A2 => { text =>"Choice A2" ,
                    701:                        default => "C2",
                    702:                        select2 => { 
                    703:                            C1 => "Choice C1",
                    704:                            C2 => "Choice C2",
                    705:                            C3 => "Choice C3"
1.609     raeburn   706:                            },
                    707:                        order => ['C2','C1','C3'],
1.112     bowersj2  708:                    },
                    709:                A3 => { text =>"Choice A3" ,
                    710:                        default => "D6",
                    711:                        select2 => { 
                    712:                            D1 => "Choice D1",
                    713:                            D2 => "Choice D2",
                    714:                            D3 => "Choice D3",
                    715:                            D4 => "Choice D4",
                    716:                            D5 => "Choice D5",
                    717:                            D6 => "Choice D6",
                    718:                            D7 => "Choice D7"
1.609     raeburn   719:                            },
                    720:                        order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112     bowersj2  721:                    }
                    722:                );
1.36      matthew   723: 
                    724: =cut
                    725: 
                    726: sub linked_select_forms {
                    727:     my ($formname,
                    728:         $middletext,
                    729:         $firstdefault,
                    730:         $firstselectname,
                    731:         $secondselectname, 
1.609     raeburn   732:         $hashref,
                    733:         $menuorder,
1.36      matthew   734:         ) = @_;
                    735:     my $second = "document.$formname.$secondselectname";
                    736:     my $first = "document.$formname.$firstselectname";
                    737:     # output the javascript to do the changing
                    738:     my $result = '';
1.219     albertel  739:     $result.="<script type=\"text/javascript\">\n";
1.36      matthew   740:     $result.="var select2data = new Object();\n";
                    741:     $" = '","';
                    742:     my $debug = '';
                    743:     foreach my $s1 (sort(keys(%$hashref))) {
                    744:         $result.="select2data.d_$s1 = new Object();\n";        
                    745:         $result.="select2data.d_$s1.def = new String('".
                    746:             $hashref->{$s1}->{'default'}."');\n";
1.609     raeburn   747:         $result.="select2data.d_$s1.values = new Array(";
1.36      matthew   748:         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609     raeburn   749:         if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
                    750:             @s2values = @{$hashref->{$s1}->{'order'}};
                    751:         }
1.36      matthew   752:         $result.="\"@s2values\");\n";
                    753:         $result.="select2data.d_$s1.texts = new Array(";        
                    754:         my @s2texts;
                    755:         foreach my $value (@s2values) {
                    756:             push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
                    757:         }
                    758:         $result.="\"@s2texts\");\n";
                    759:     }
                    760:     $"=' ';
                    761:     $result.= <<"END";
                    762: 
                    763: function select1_changed() {
                    764:     // Determine new choice
                    765:     var newvalue = "d_" + $first.value;
                    766:     // update select2
                    767:     var values     = select2data[newvalue].values;
                    768:     var texts      = select2data[newvalue].texts;
                    769:     var select2def = select2data[newvalue].def;
                    770:     var i;
                    771:     // out with the old
                    772:     for (i = 0; i < $second.options.length; i++) {
                    773:         $second.options[i] = null;
                    774:     }
                    775:     // in with the nuclear
                    776:     for (i=0;i<values.length; i++) {
                    777:         $second.options[i] = new Option(values[i]);
1.143     matthew   778:         $second.options[i].value = values[i];
1.36      matthew   779:         $second.options[i].text = texts[i];
                    780:         if (values[i] == select2def) {
                    781:             $second.options[i].selected = true;
                    782:         }
                    783:     }
                    784: }
                    785: </script>
                    786: END
                    787:     # output the initial values for the selection lists
                    788:     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed()\">\n";
1.609     raeburn   789:     my @order = sort(keys(%{$hashref}));
                    790:     if (ref($menuorder) eq 'ARRAY') {
                    791:         @order = @{$menuorder};
                    792:     }
                    793:     foreach my $value (@order) {
1.36      matthew   794:         $result.="    <option value=\"$value\" ";
1.253     albertel  795:         $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119     www       796:         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36      matthew   797:     }
                    798:     $result .= "</select>\n";
                    799:     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
                    800:     $result .= $middletext;
                    801:     $result .= "<select size=\"1\" name=\"$secondselectname\">\n";
                    802:     my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609     raeburn   803:     
                    804:     my @secondorder = sort(keys(%select2));
                    805:     if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
                    806:         @secondorder = @{$hashref->{$firstdefault}->{'order'}};
                    807:     }
                    808:     foreach my $value (@secondorder) {
1.36      matthew   809:         $result.="    <option value=\"$value\" ";        
1.253     albertel  810:         $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119     www       811:         $result.=">".&mt($select2{$value})."</option>\n";
1.36      matthew   812:     }
                    813:     $result .= "</select>\n";
                    814:     #    return $debug;
                    815:     return $result;
                    816: }   #  end of sub linked_select_forms {
                    817: 
1.45      matthew   818: =pod
1.44      bowersj2  819: 
1.648     raeburn   820: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height)
1.44      bowersj2  821: 
1.112     bowersj2  822: Returns a string corresponding to an HTML link to the given help
                    823: $topic, where $topic corresponds to the name of a .tex file in
                    824: /home/httpd/html/adm/help/tex, with underscores replaced by
                    825: spaces. 
                    826: 
                    827: $text will optionally be linked to the same topic, allowing you to
                    828: link text in addition to the graphic. If you do not want to link
                    829: text, but wish to specify one of the later parameters, pass an
                    830: empty string. 
                    831: 
                    832: $stayOnPage is a value that will be interpreted as a boolean. If true,
                    833: the link will not open a new window. If false, the link will open
                    834: a new window using Javascript. (Default is false.) 
                    835: 
                    836: $width and $height are optional numerical parameters that will
                    837: override the width and height of the popped up window, which may
                    838: be useful for certain help topics with big pictures included. 
1.44      bowersj2  839: 
                    840: =cut
                    841: 
                    842: sub help_open_topic {
1.48      bowersj2  843:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
                    844:     $text = "" if (not defined $text);
1.44      bowersj2  845:     $stayOnPage = 0 if (not defined $stayOnPage);
1.552     banghart  846:     if ($env{'browser.interface'} eq 'textual') {
1.79      www       847: 	$stayOnPage=1;
                    848:     }
1.44      bowersj2  849:     $width = 350 if (not defined $width);
                    850:     $height = 400 if (not defined $height);
                    851:     my $filename = $topic;
                    852:     $filename =~ s/ /_/g;
                    853: 
1.48      bowersj2  854:     my $template = "";
                    855:     my $link;
1.572     banghart  856:     
1.159     www       857:     $topic=~s/\W/\_/g;
1.44      bowersj2  858: 
1.572     banghart  859:     if (!$stayOnPage) {
1.72      bowersj2  860: 	$link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1.572     banghart  861:     } else {
1.48      bowersj2  862: 	$link = "/adm/help/${filename}.hlp";
                    863:     }
                    864: 
                    865:     # Add the text
1.572     banghart  866:     if ($text ne "") {
1.77      www       867: 	$template .= 
1.572     banghart  868:             "<table bgcolor='#3333AA' cellspacing='1' cellpadding='1' border='0'><tr>".
                    869:             "<td bgcolor='#5555FF'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.48      bowersj2  870:     }
                    871: 
                    872:     # Add the graphic
1.179     matthew   873:     my $title = &mt('Online Help');
1.649     www       874:     my $helpicon=&lonhttpdurl("/res/adm/pages/help.png");
1.48      bowersj2  875:     $template .= <<"ENDTEMPLATE";
1.436     albertel  876:  <a target="_top" href="$link" title="$title"><img src="$helpicon" border="0" alt="(Help: $topic)" /></a>
1.44      bowersj2  877: ENDTEMPLATE
1.78      www       878:     if ($text ne '') { $template.='</td></tr></table>' };
1.44      bowersj2  879:     return $template;
                    880: 
1.106     bowersj2  881: }
                    882: 
                    883: # This is a quicky function for Latex cheatsheet editing, since it 
                    884: # appears in at least four places
                    885: sub helpLatexCheatsheet {
                    886:     my $other = shift;
                    887:     my $addOther = '';
                    888:     if ($other) {
                    889: 	$addOther = Apache::loncommon::help_open_topic($other, shift,
                    890: 						       undef, undef, 600) .
                    891: 							   '</td><td>';
                    892:     }
                    893:     return '<table><tr><td>'.
                    894: 	$addOther .
1.636     raeburn   895: 	&Apache::loncommon::help_open_topic("Greek_Symbols",&mt('Greek Symbols'),
1.106     bowersj2  896: 					    undef,undef,600)
                    897: 	.'</td><td>'.
1.636     raeburn   898: 	&Apache::loncommon::help_open_topic("Other_Symbols",&mt('Other Symbols'),
1.106     bowersj2  899: 					    undef,undef,600)
                    900: 	.'</td></tr></table>';
1.172     www       901: }
                    902: 
1.430     albertel  903: sub general_help {
                    904:     my $helptopic='Student_Intro';
                    905:     if ($env{'request.role'}=~/^(ca|au)/) {
                    906: 	$helptopic='Authoring_Intro';
                    907:     } elsif ($env{'request.role'}=~/^cc/) {
                    908: 	$helptopic='Course_Coordination_Intro';
                    909:     }
                    910:     return $helptopic;
                    911: }
                    912: 
                    913: sub update_help_link {
                    914:     my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
                    915:     my $origurl = $ENV{'REQUEST_URI'};
                    916:     $origurl=~s|^/~|/priv/|;
                    917:     my $timestamp = time;
                    918:     foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
                    919:         $$datum = &escape($$datum);
                    920:     }
                    921: 
                    922:     my $banner_link = "/adm/helpmenu?page=banner&amp;topic=$topic&amp;component_help=$component_help&amp;faq=$faq&amp;bug=$bug&amp;origurl=$origurl&amp;stamp=$timestamp&amp;stayonpage=$stayOnPage";
                    923:     my $output .= <<"ENDOUTPUT";
                    924: <script type="text/javascript">
                    925: banner_link = '$banner_link';
                    926: </script>
                    927: ENDOUTPUT
                    928:     return $output;
                    929: }
                    930: 
                    931: # now just updates the help link and generates a blue icon
1.193     raeburn   932: sub help_open_menu {
1.430     albertel  933:     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text) 
1.552     banghart  934: 	= @_;    
1.430     albertel  935:     $stayOnPage = 0 if (not defined $stayOnPage);
1.572     banghart  936:     # only use pop-up help (stayOnPage == 0)
1.552     banghart  937:     # if environment.remote is on (using remote control UI)
1.572     banghart  938:     if ($env{'browser.interface'} eq 'textual' ||
                    939:     	$env{'environment.remote'} eq 'off' ) {
1.552     banghart  940:         $stayOnPage=1;
1.430     albertel  941:     }
                    942:     my $output;
                    943:     if ($component_help) {
                    944: 	if (!$text) {
                    945: 	    $output=&help_open_topic($component_help,undef,$stayOnPage,
                    946: 				       $width,$height);
                    947: 	} else {
                    948: 	    my $help_text;
                    949: 	    $help_text=&unescape($topic);
                    950: 	    $output='<table><tr><td>'.
                    951: 		&help_open_topic($component_help,$help_text,$stayOnPage,
                    952: 				 $width,$height).'</td></tr></table>';
                    953: 	}
                    954:     }
                    955:     my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
                    956:     return $output.$banner_link;
                    957: }
                    958: 
                    959: sub top_nav_help {
                    960:     my ($text) = @_;
1.436     albertel  961:     $text = &mt($text);
1.572     banghart  962:     my $stay_on_page = 
1.436     albertel  963: 	($env{'browser.interface'}  eq 'textual' ||
                    964: 	 $env{'environment.remote'} eq 'off' );
1.572     banghart  965:     my $link = ($stay_on_page) ? "javascript:helpMenu('display')"
1.436     albertel  966: 	                     : "javascript:helpMenu('open')";
1.572     banghart  967:     my $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
1.436     albertel  968: 
1.201     raeburn   969:     my $title = &mt('Get help');
1.436     albertel  970: 
                    971:     return <<"END";
                    972: $banner_link
                    973:  <a href="$link" title="$title">$text</a>
                    974: END
                    975: }
                    976: 
                    977: sub help_menu_js {
                    978:     my ($text) = @_;
                    979: 
                    980:     my $stayOnPage = 
                    981: 	($env{'browser.interface'}  eq 'textual' ||
                    982: 	 $env{'environment.remote'} eq 'off' );
                    983: 
                    984:     my $width = 620;
                    985:     my $height = 600;
1.430     albertel  986:     my $helptopic=&general_help();
                    987:     my $details_link = '/adm/help/'.$helptopic.'.hlp';
1.261     albertel  988:     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331     albertel  989:     my $start_page =
                    990:         &Apache::loncommon::start_page('Help Menu', undef,
                    991: 				       {'frameset'    => 1,
                    992: 					'js_ready'    => 1,
                    993: 					'add_entries' => {
                    994: 					    'border' => '0',
1.579     raeburn   995: 					    'rows'   => "110,*",},});
1.331     albertel  996:     my $end_page =
                    997:         &Apache::loncommon::end_page({'frameset' => 1,
                    998: 				      'js_ready' => 1,});
                    999: 
1.436     albertel 1000:     my $template .= <<"ENDTEMPLATE";
                   1001: <script type="text/javascript">
1.253     albertel 1002: // <!-- BEGIN LON-CAPA Internal
                   1003: // <![CDATA[
1.430     albertel 1004: var banner_link = '';
1.243     raeburn  1005: function helpMenu(target) {
                   1006:     var caller = this;
                   1007:     if (target == 'open') {
                   1008:         var newWindow = null;
                   1009:         try {
1.262     albertel 1010:             newWindow =  window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243     raeburn  1011:         }
                   1012:         catch(error) {
                   1013:             writeHelp(caller);
                   1014:             return;
                   1015:         }
                   1016:         if (newWindow) {
                   1017:             caller = newWindow;
                   1018:         }
1.193     raeburn  1019:     }
1.243     raeburn  1020:     writeHelp(caller);
                   1021:     return;
                   1022: }
                   1023: function writeHelp(caller) {
1.430     albertel 1024:     caller.document.writeln('$start_page<frame name="bannerframe"  src="'+banner_link+'" /><frame name="bodyframe" src="$details_link" /> $end_page')
1.243     raeburn  1025:     caller.document.close()
                   1026:     caller.focus()
1.193     raeburn  1027: }
1.253     albertel 1028: // ]]>
1.219     albertel 1029: // END LON-CAPA Internal -->
1.436     albertel 1030: </script>
1.193     raeburn  1031: ENDTEMPLATE
                   1032:     return $template;
                   1033: }
                   1034: 
1.172     www      1035: sub help_open_bug {
                   1036:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1037:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1038:     unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
                   1039:     $text = "" if (not defined $text);
                   1040:     $stayOnPage = 0 if (not defined $stayOnPage);
1.258     albertel 1041:     if ($env{'browser.interface'} eq 'textual' ||
                   1042: 	$env{'environment.remote'} eq 'off' ) {
1.172     www      1043: 	$stayOnPage=1;
                   1044:     }
1.184     albertel 1045:     $width = 600 if (not defined $width);
                   1046:     $height = 600 if (not defined $height);
1.172     www      1047: 
                   1048:     $topic=~s/\W+/\+/g;
                   1049:     my $link='';
                   1050:     my $template='';
1.379     albertel 1051:     my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&amp;bug_file_loc='.
                   1052: 	&escape($ENV{'REQUEST_URI'}).'&amp;component='.$topic;
1.172     www      1053:     if (!$stayOnPage)
                   1054:     {
                   1055: 	$link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1056:     }
                   1057:     else
                   1058:     {
                   1059: 	$link = $url;
                   1060:     }
                   1061:     # Add the text
                   1062:     if ($text ne "")
                   1063:     {
                   1064: 	$template .= 
                   1065:   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436     albertel 1066:   "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.172     www      1067:     }
                   1068: 
                   1069:     # Add the graphic
1.179     matthew  1070:     my $title = &mt('Report a Bug');
1.215     albertel 1071:     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172     www      1072:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1073:  <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172     www      1074: ENDTEMPLATE
                   1075:     if ($text ne '') { $template.='</td></tr></table>' };
                   1076:     return $template;
                   1077: 
                   1078: }
                   1079: 
                   1080: sub help_open_faq {
                   1081:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1082:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1083:     unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
                   1084:     $text = "" if (not defined $text);
                   1085:     $stayOnPage = 0 if (not defined $stayOnPage);
1.258     albertel 1086:     if ($env{'browser.interface'} eq 'textual' ||
                   1087: 	$env{'environment.remote'} eq 'off' ) {
1.172     www      1088: 	$stayOnPage=1;
                   1089:     }
                   1090:     $width = 350 if (not defined $width);
                   1091:     $height = 400 if (not defined $height);
                   1092: 
                   1093:     $topic=~s/\W+/\+/g;
                   1094:     my $link='';
                   1095:     my $template='';
                   1096:     my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
                   1097:     if (!$stayOnPage)
                   1098:     {
                   1099: 	$link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1100:     }
                   1101:     else
                   1102:     {
                   1103: 	$link = $url;
                   1104:     }
                   1105: 
                   1106:     # Add the text
                   1107:     if ($text ne "")
                   1108:     {
                   1109: 	$template .= 
1.173     www      1110:   "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436     albertel 1111:   "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.172     www      1112:     }
                   1113: 
                   1114:     # Add the graphic
1.179     matthew  1115:     my $title = &mt('View the FAQ');
1.215     albertel 1116:     my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172     www      1117:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1118:  <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172     www      1119: ENDTEMPLATE
                   1120:     if ($text ne '') { $template.='</td></tr></table>' };
                   1121:     return $template;
                   1122: 
1.44      bowersj2 1123: }
1.37      matthew  1124: 
1.180     matthew  1125: ###############################################################
                   1126: ###############################################################
                   1127: 
1.45      matthew  1128: =pod
                   1129: 
1.648     raeburn  1130: =item * &change_content_javascript():
1.256     matthew  1131: 
                   1132: This and the next function allow you to create small sections of an
                   1133: otherwise static HTML page that you can update on the fly with
                   1134: Javascript, even in Netscape 4.
                   1135: 
                   1136: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
                   1137: must be written to the HTML page once. It will prove the Javascript
                   1138: function "change(name, content)". Calling the change function with the
                   1139: name of the section 
                   1140: you want to update, matching the name passed to C<changable_area>, and
                   1141: the new content you want to put in there, will put the content into
                   1142: that area.
                   1143: 
                   1144: B<Note>: Netscape 4 only reserves enough space for the changable area
                   1145: to contain room for the original contents. You need to "make space"
                   1146: for whatever changes you wish to make, and be B<sure> to check your
                   1147: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
                   1148: it's adequate for updating a one-line status display, but little more.
                   1149: This script will set the space to 100% width, so you only need to
                   1150: worry about height in Netscape 4.
                   1151: 
                   1152: Modern browsers are much less limiting, and if you can commit to the
                   1153: user not using Netscape 4, this feature may be used freely with
                   1154: pretty much any HTML.
                   1155: 
                   1156: =cut
                   1157: 
                   1158: sub change_content_javascript {
                   1159:     # If we're on Netscape 4, we need to use Layer-based code
1.258     albertel 1160:     if ($env{'browser.type'} eq 'netscape' &&
                   1161: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1162: 	return (<<NETSCAPE4);
                   1163: 	function change(name, content) {
                   1164: 	    doc = document.layers[name+"___escape"].layers[0].document;
                   1165: 	    doc.open();
                   1166: 	    doc.write(content);
                   1167: 	    doc.close();
                   1168: 	}
                   1169: NETSCAPE4
                   1170:     } else {
                   1171: 	# Otherwise, we need to use semi-standards-compliant code
                   1172: 	# (technically, "innerHTML" isn't standard but the equivalent
                   1173: 	# is really scary, and every useful browser supports it
                   1174: 	return (<<DOMBASED);
                   1175: 	function change(name, content) {
                   1176: 	    element = document.getElementById(name);
                   1177: 	    element.innerHTML = content;
                   1178: 	}
                   1179: DOMBASED
                   1180:     }
                   1181: }
                   1182: 
                   1183: =pod
                   1184: 
1.648     raeburn  1185: =item * &changable_area($name,$origContent):
1.256     matthew  1186: 
                   1187: This provides a "changable area" that can be modified on the fly via
                   1188: the Javascript code provided in C<change_content_javascript>. $name is
                   1189: the name you will use to reference the area later; do not repeat the
                   1190: same name on a given HTML page more then once. $origContent is what
                   1191: the area will originally contain, which can be left blank.
                   1192: 
                   1193: =cut
                   1194: 
                   1195: sub changable_area {
                   1196:     my ($name, $origContent) = @_;
                   1197: 
1.258     albertel 1198:     if ($env{'browser.type'} eq 'netscape' &&
                   1199: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1200: 	# If this is netscape 4, we need to use the Layer tag
                   1201: 	return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
                   1202:     } else {
                   1203: 	return "<span id='$name'>$origContent</span>";
                   1204:     }
                   1205: }
                   1206: 
                   1207: =pod
                   1208: 
1.648     raeburn  1209: =item * &viewport_geometry_js 
1.590     raeburn  1210: 
                   1211: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
                   1212: 
                   1213: =cut
                   1214: 
                   1215: 
                   1216: sub viewport_geometry_js { 
                   1217:     return <<"GEOMETRY";
                   1218: var Geometry = {};
                   1219: function init_geometry() {
                   1220:     if (Geometry.init) { return };
                   1221:     Geometry.init=1;
                   1222:     if (window.innerHeight) {
                   1223:         Geometry.getViewportHeight   = function() { return window.innerHeight; };
                   1224:         Geometry.getViewportWidth   = function() { return window.innerWidth; };
                   1225:         Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
                   1226:         Geometry.getVerticalScroll   = function() { return window.pageYOffset; };
                   1227:     }
                   1228:     else if (document.documentElement && document.documentElement.clientHeight) {
                   1229:         Geometry.getViewportHeight =
                   1230:             function() { return document.documentElement.clientHeight; };
                   1231:         Geometry.getViewportWidth =
                   1232:             function() { return document.documentElement.clientWidth; };
                   1233: 
                   1234:         Geometry.getHorizontalScroll =
                   1235:             function() { return document.documentElement.scrollLeft; };
                   1236:         Geometry.getVerticalScroll =
                   1237:             function() { return document.documentElement.scrollTop; };
                   1238:     }
                   1239:     else if (document.body.clientHeight) {
                   1240:         Geometry.getViewportHeight =
                   1241:             function() { return document.body.clientHeight; };
                   1242:         Geometry.getViewportWidth =
                   1243:             function() { return document.body.clientWidth; };
                   1244:         Geometry.getHorizontalScroll =
                   1245:             function() { return document.body.scrollLeft; };
                   1246:         Geometry.getVerticalScroll =
                   1247:             function() { return document.body.scrollTop; };
                   1248:     }
                   1249: }
                   1250: 
                   1251: GEOMETRY
                   1252: }
                   1253: 
                   1254: =pod
                   1255: 
1.648     raeburn  1256: =item * &viewport_size_js()
1.590     raeburn  1257: 
                   1258: Provides a javascript function to set values of two form elements - width and height (elements are passed in as arguments to the javascript function) to the dimensions of the user's browser window. 
                   1259: 
                   1260: =cut
                   1261: 
                   1262: sub viewport_size_js {
                   1263:     my $geometry = &viewport_geometry_js();
                   1264:     return <<"DIMS";
                   1265: 
                   1266: $geometry
                   1267: 
                   1268: function getViewportDims(width,height) {
                   1269:     init_geometry();
                   1270:     width.value = Geometry.getViewportWidth();
                   1271:     height.value = Geometry.getViewportHeight();
                   1272:     return;
                   1273: }
                   1274: 
                   1275: DIMS
                   1276: }
                   1277: 
                   1278: =pod
                   1279: 
1.648     raeburn  1280: =item * &resize_textarea_js()
1.565     albertel 1281: 
                   1282: emits the needed javascript to resize a textarea to be as big as possible
                   1283: 
                   1284: creates a function resize_textrea that takes two IDs first should be
                   1285: the id of the element to resize, second should be the id of a div that
                   1286: surrounds everything that comes after the textarea, this routine needs
                   1287: to be attached to the <body> for the onload and onresize events.
                   1288: 
1.648     raeburn  1289: =back
1.565     albertel 1290: 
                   1291: =cut
                   1292: 
                   1293: sub resize_textarea_js {
1.590     raeburn  1294:     my $geometry = &viewport_geometry_js();
1.565     albertel 1295:     return <<"RESIZE";
                   1296:     <script type="text/javascript">
1.590     raeburn  1297: $geometry
1.565     albertel 1298: 
1.588     albertel 1299: function getX(element) {
                   1300:     var x = 0;
                   1301:     while (element) {
                   1302: 	x += element.offsetLeft;
                   1303: 	element = element.offsetParent;
                   1304:     }
                   1305:     return x;
                   1306: }
                   1307: function getY(element) {
                   1308:     var y = 0;
                   1309:     while (element) {
                   1310: 	y += element.offsetTop;
                   1311: 	element = element.offsetParent;
                   1312:     }
                   1313:     return y;
                   1314: }
                   1315: 
                   1316: 
1.565     albertel 1317: function resize_textarea(textarea_id,bottom_id) {
                   1318:     init_geometry();
                   1319:     var textarea        = document.getElementById(textarea_id);
                   1320:     //alert(textarea);
                   1321: 
1.588     albertel 1322:     var textarea_top    = getY(textarea);
1.565     albertel 1323:     var textarea_height = textarea.offsetHeight;
                   1324:     var bottom          = document.getElementById(bottom_id);
1.588     albertel 1325:     var bottom_top      = getY(bottom);
1.565     albertel 1326:     var bottom_height   = bottom.offsetHeight;
                   1327:     var window_height   = Geometry.getViewportHeight();
1.588     albertel 1328:     var fudge           = 23;
1.565     albertel 1329:     var new_height      = window_height-fudge-textarea_top-bottom_height;
                   1330:     if (new_height < 300) {
                   1331: 	new_height = 300;
                   1332:     }
                   1333:     textarea.style.height=new_height+'px';
                   1334: }
                   1335: </script>
                   1336: RESIZE
                   1337: 
                   1338: }
                   1339: 
                   1340: =pod
                   1341: 
1.256     matthew  1342: =head1 Excel and CSV file utility routines
                   1343: 
                   1344: =over 4
                   1345: 
                   1346: =cut
                   1347: 
                   1348: ###############################################################
                   1349: ###############################################################
                   1350: 
                   1351: =pod
                   1352: 
1.648     raeburn  1353: =item * &csv_translate($text) 
1.37      matthew  1354: 
1.185     www      1355: Translate $text to allow it to be output as a 'comma separated values' 
1.37      matthew  1356: format.
                   1357: 
                   1358: =cut
                   1359: 
1.180     matthew  1360: ###############################################################
                   1361: ###############################################################
1.37      matthew  1362: sub csv_translate {
                   1363:     my $text = shift;
                   1364:     $text =~ s/\"/\"\"/g;
1.209     albertel 1365:     $text =~ s/\n/ /g;
1.37      matthew  1366:     return $text;
                   1367: }
1.180     matthew  1368: 
                   1369: ###############################################################
                   1370: ###############################################################
                   1371: 
                   1372: =pod
                   1373: 
1.648     raeburn  1374: =item * &define_excel_formats()
1.180     matthew  1375: 
                   1376: Define some commonly used Excel cell formats.
                   1377: 
                   1378: Currently supported formats:
                   1379: 
                   1380: =over 4
                   1381: 
                   1382: =item header
                   1383: 
                   1384: =item bold
                   1385: 
                   1386: =item h1
                   1387: 
                   1388: =item h2
                   1389: 
                   1390: =item h3
                   1391: 
1.256     matthew  1392: =item h4
                   1393: 
                   1394: =item i
                   1395: 
1.180     matthew  1396: =item date
                   1397: 
                   1398: =back
                   1399: 
                   1400: Inputs: $workbook
                   1401: 
                   1402: Returns: $format, a hash reference.
                   1403: 
                   1404: =cut
                   1405: 
                   1406: ###############################################################
                   1407: ###############################################################
                   1408: sub define_excel_formats {
                   1409:     my ($workbook) = @_;
                   1410:     my $format;
                   1411:     $format->{'header'} = $workbook->add_format(bold      => 1, 
                   1412:                                                 bottom    => 1,
                   1413:                                                 align     => 'center');
                   1414:     $format->{'bold'} = $workbook->add_format(bold=>1);
                   1415:     $format->{'h1'}   = $workbook->add_format(bold=>1, size=>18);
                   1416:     $format->{'h2'}   = $workbook->add_format(bold=>1, size=>16);
                   1417:     $format->{'h3'}   = $workbook->add_format(bold=>1, size=>14);
1.255     matthew  1418:     $format->{'h4'}   = $workbook->add_format(bold=>1, size=>12);
1.246     matthew  1419:     $format->{'i'}    = $workbook->add_format(italic=>1);
1.180     matthew  1420:     $format->{'date'} = $workbook->add_format(num_format=>
1.207     matthew  1421:                                             'mm/dd/yyyy hh:mm:ss');
1.180     matthew  1422:     return $format;
                   1423: }
                   1424: 
                   1425: ###############################################################
                   1426: ###############################################################
1.113     bowersj2 1427: 
                   1428: =pod
                   1429: 
1.648     raeburn  1430: =item * &create_workbook()
1.255     matthew  1431: 
                   1432: Create an Excel worksheet.  If it fails, output message on the
                   1433: request object and return undefs.
                   1434: 
                   1435: Inputs: Apache request object
                   1436: 
                   1437: Returns (undef) on failure, 
                   1438:     Excel worksheet object, scalar with filename, and formats 
                   1439:     from &Apache::loncommon::define_excel_formats on success
                   1440: 
                   1441: =cut
                   1442: 
                   1443: ###############################################################
                   1444: ###############################################################
                   1445: sub create_workbook {
                   1446:     my ($r) = @_;
                   1447:         #
                   1448:     # Create the excel spreadsheet
                   1449:     my $filename = '/prtspool/'.
1.258     albertel 1450:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255     matthew  1451:         time.'_'.rand(1000000000).'.xls';
                   1452:     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
                   1453:     if (! defined($workbook)) {
                   1454:         $r->log_error("Error creating excel spreadsheet $filename: $!");
                   1455:         $r->print('<p>'.&mt("Unable to create new Excel file.  ".
                   1456:                             "This error has been logged.  ".
                   1457:                             "Please alert your LON-CAPA administrator").
                   1458:                   '</p>');
                   1459:         return (undef);
                   1460:     }
                   1461:     #
                   1462:     $workbook->set_tempdir('/home/httpd/perl/tmp');
                   1463:     #
                   1464:     my $format = &Apache::loncommon::define_excel_formats($workbook);
                   1465:     return ($workbook,$filename,$format);
                   1466: }
                   1467: 
                   1468: ###############################################################
                   1469: ###############################################################
                   1470: 
                   1471: =pod
                   1472: 
1.648     raeburn  1473: =item * &create_text_file()
1.113     bowersj2 1474: 
1.542     raeburn  1475: Create a file to write to and eventually make available to the user.
1.256     matthew  1476: If file creation fails, outputs an error message on the request object and 
                   1477: return undefs.
1.113     bowersj2 1478: 
1.256     matthew  1479: Inputs: Apache request object, and file suffix
1.113     bowersj2 1480: 
1.256     matthew  1481: Returns (undef) on failure, 
                   1482:     Filehandle and filename on success.
1.113     bowersj2 1483: 
                   1484: =cut
                   1485: 
1.256     matthew  1486: ###############################################################
                   1487: ###############################################################
                   1488: sub create_text_file {
                   1489:     my ($r,$suffix) = @_;
                   1490:     if (! defined($suffix)) { $suffix = 'txt'; };
                   1491:     my $fh;
                   1492:     my $filename = '/prtspool/'.
1.258     albertel 1493:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256     matthew  1494:         time.'_'.rand(1000000000).'.'.$suffix;
                   1495:     $fh = Apache::File->new('>/home/httpd'.$filename);
                   1496:     if (! defined($fh)) {
                   1497:         $r->log_error("Couldn't open $filename for output $!");
                   1498:         $r->print("Problems occured in creating the output file.  ".
                   1499:                   "This error has been logged.  ".
                   1500:                   "Please alert your LON-CAPA administrator.");
1.113     bowersj2 1501:     }
1.256     matthew  1502:     return ($fh,$filename)
1.113     bowersj2 1503: }
                   1504: 
                   1505: 
1.256     matthew  1506: =pod 
1.113     bowersj2 1507: 
                   1508: =back
                   1509: 
                   1510: =cut
1.37      matthew  1511: 
                   1512: ###############################################################
1.33      matthew  1513: ##        Home server <option> list generating code          ##
                   1514: ###############################################################
1.35      matthew  1515: 
1.169     www      1516: # ------------------------------------------
                   1517: 
                   1518: sub domain_select {
                   1519:     my ($name,$value,$multiple)=@_;
                   1520:     my %domains=map { 
1.514     albertel 1521: 	$_ => $_.' '. &Apache::lonnet::domain($_,'description') 
1.512     albertel 1522:     } &Apache::lonnet::all_domains();
1.169     www      1523:     if ($multiple) {
                   1524: 	$domains{''}=&mt('Any domain');
1.550     albertel 1525: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287     albertel 1526: 	return &multiple_select_form($name,$value,4,\%domains);
1.169     www      1527:     } else {
1.550     albertel 1528: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.169     www      1529: 	return &select_form($name,$value,%domains);
                   1530:     }
                   1531: }
                   1532: 
1.282     albertel 1533: #-------------------------------------------
                   1534: 
                   1535: =pod
                   1536: 
1.519     raeburn  1537: =head1 Routines for form select boxes
                   1538: 
                   1539: =over 4
                   1540: 
1.648     raeburn  1541: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282     albertel 1542: 
                   1543: Returns a string containing a <select> element int multiple mode
                   1544: 
                   1545: 
                   1546: Args:
                   1547:   $name - name of the <select> element
1.506     raeburn  1548:   $value - scalar or array ref of values that should already be selected
1.282     albertel 1549:   $size - number of rows long the select element is
1.283     albertel 1550:   $hash - the elements should be 'option' => 'shown text'
1.282     albertel 1551:           (shown text should already have been &mt())
1.506     raeburn  1552:   $order - (optional) array ref of the order to show the elements in
1.283     albertel 1553: 
1.282     albertel 1554: =cut
                   1555: 
                   1556: #-------------------------------------------
1.169     www      1557: sub multiple_select_form {
1.284     albertel 1558:     my ($name,$value,$size,$hash,$order)=@_;
1.169     www      1559:     my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
                   1560:     my $output='';
1.191     matthew  1561:     if (! defined($size)) {
                   1562:         $size = 4;
1.283     albertel 1563:         if (scalar(keys(%$hash))<4) {
                   1564:             $size = scalar(keys(%$hash));
1.191     matthew  1565:         }
                   1566:     }
1.169     www      1567:     $output.="\n<select name='$name' size='$size' multiple='1'>";
1.501     banghart 1568:     my @order;
1.506     raeburn  1569:     if (ref($order) eq 'ARRAY')  {
                   1570:         @order = @{$order};
                   1571:     } else {
                   1572:         @order = sort(keys(%$hash));
1.501     banghart 1573:     }
                   1574:     if (exists($$hash{'select_form_order'})) {
                   1575:         @order = @{$$hash{'select_form_order'}};
                   1576:     }
                   1577:         
1.284     albertel 1578:     foreach my $key (@order) {
1.356     albertel 1579:         $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284     albertel 1580:         $output.='selected="selected" ' if ($selected{$key});
                   1581:         $output.='>'.$hash->{$key}."</option>\n";
1.169     www      1582:     }
                   1583:     $output.="</select>\n";
                   1584:     return $output;
                   1585: }
                   1586: 
1.88      www      1587: #-------------------------------------------
                   1588: 
                   1589: =pod
                   1590: 
1.648     raeburn  1591: =item * &select_form($defdom,$name,%hash)
1.88      www      1592: 
                   1593: Returns a string containing a <select name='$name' size='1'> form to 
                   1594: allow a user to select options from a hash option_name => displayed text.  
                   1595: See lonrights.pm for an example invocation and use.
                   1596: 
                   1597: =cut
                   1598: 
                   1599: #-------------------------------------------
                   1600: sub select_form {
                   1601:     my ($def,$name,%hash) = @_;
                   1602:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
1.128     albertel 1603:     my @keys;
                   1604:     if (exists($hash{'select_form_order'})) {
                   1605: 	@keys=@{$hash{'select_form_order'}};
                   1606:     } else {
                   1607: 	@keys=sort(keys(%hash));
                   1608:     }
1.356     albertel 1609:     foreach my $key (@keys) {
                   1610:         $selectform.=
                   1611: 	    '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
                   1612:             ($key eq $def ? 'selected="selected" ' : '').
                   1613:                 ">".&mt($hash{$key})."</option>\n";
1.88      www      1614:     }
                   1615:     $selectform.="</select>";
                   1616:     return $selectform;
                   1617: }
                   1618: 
1.475     www      1619: # For display filters
                   1620: 
                   1621: sub display_filter {
                   1622:     if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477     www      1623:     if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.475     www      1624:     return '<nobr><label>'.&mt('Records [_1]',
                   1625: 			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
                   1626: 							   (&mt('all'),10,20,50,100,1000,10000))).
1.478     www      1627: 	   '</label></nobr> <nobr>'.
1.475     www      1628:            &mt('Filter [_1]',
1.477     www      1629: 	   &select_form($env{'form.displayfilter'},
                   1630: 			'displayfilter',
                   1631: 			('currentfolder' => 'Current folder/page',
                   1632: 			 'containing' => 'Containing phrase',
                   1633: 			 'none' => 'None'))).
1.478     www      1634: 			 '<input type="text" name="containingphrase" size="30" value="'.&HTML::Entities::encode($env{'form.containingphrase'}).'" /></nobr>';
1.475     www      1635: }
                   1636: 
1.167     www      1637: sub gradeleveldescription {
                   1638:     my $gradelevel=shift;
                   1639:     my %gradelevels=(0 => 'Not specified',
                   1640: 		     1 => 'Grade 1',
                   1641: 		     2 => 'Grade 2',
                   1642: 		     3 => 'Grade 3',
                   1643: 		     4 => 'Grade 4',
                   1644: 		     5 => 'Grade 5',
                   1645: 		     6 => 'Grade 6',
                   1646: 		     7 => 'Grade 7',
                   1647: 		     8 => 'Grade 8',
                   1648: 		     9 => 'Grade 9',
                   1649: 		     10 => 'Grade 10',
                   1650: 		     11 => 'Grade 11',
                   1651: 		     12 => 'Grade 12',
                   1652: 		     13 => 'Grade 13',
                   1653: 		     14 => '100 Level',
                   1654: 		     15 => '200 Level',
                   1655: 		     16 => '300 Level',
                   1656: 		     17 => '400 Level',
                   1657: 		     18 => 'Graduate Level');
                   1658:     return &mt($gradelevels{$gradelevel});
                   1659: }
                   1660: 
1.163     www      1661: sub select_level_form {
                   1662:     my ($deflevel,$name)=@_;
                   1663:     unless ($deflevel) { $deflevel=0; }
1.167     www      1664:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
                   1665:     for (my $i=0; $i<=18; $i++) {
                   1666:         $selectform.="<option value=\"$i\" ".
1.253     albertel 1667:             ($i==$deflevel ? 'selected="selected" ' : '').
1.167     www      1668:                 ">".&gradeleveldescription($i)."</option>\n";
                   1669:     }
                   1670:     $selectform.="</select>";
                   1671:     return $selectform;
1.163     www      1672: }
1.167     www      1673: 
1.35      matthew  1674: #-------------------------------------------
                   1675: 
1.45      matthew  1676: =pod
                   1677: 
1.648     raeburn  1678: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc)
1.35      matthew  1679: 
                   1680: Returns a string containing a <select name='$name' size='1'> form to 
                   1681: allow a user to select the domain to preform an operation in.  
                   1682: See loncreateuser.pm for an example invocation and use.
                   1683: 
1.90      www      1684: If the $includeempty flag is set, it also includes an empty choice ("no domain
                   1685: selected");
                   1686: 
1.563     raeburn  1687: If the $showdomdesc flag is set, the domain name is followed by the domain description. 
                   1688: 
1.35      matthew  1689: =cut
                   1690: 
                   1691: #-------------------------------------------
1.34      matthew  1692: sub select_dom_form {
1.563     raeburn  1693:     my ($defdom,$name,$includeempty,$showdomdesc) = @_;
1.550     albertel 1694:     my @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
1.90      www      1695:     if ($includeempty) { @domains=('',@domains); }
1.34      matthew  1696:     my $selectdomain = "<select name=\"$name\" size=\"1\">\n";
1.356     albertel 1697:     foreach my $dom (@domains) {
                   1698:         $selectdomain.="<option value=\"$dom\" ".
1.563     raeburn  1699:             ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
                   1700:         if ($showdomdesc) {
                   1701:             if ($dom ne '') {
                   1702:                 my $domdesc = &Apache::lonnet::domain($dom,'description');
                   1703:                 if ($domdesc ne '') {
                   1704:                     $selectdomain .= ' ('.$domdesc.')';
                   1705:                 }
                   1706:             } 
                   1707:         }
                   1708:         $selectdomain .= "</option>\n";
1.34      matthew  1709:     }
                   1710:     $selectdomain.="</select>";
                   1711:     return $selectdomain;
                   1712: }
                   1713: 
1.35      matthew  1714: #-------------------------------------------
                   1715: 
1.45      matthew  1716: =pod
                   1717: 
1.648     raeburn  1718: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35      matthew  1719: 
1.586     raeburn  1720: input: 4 arguments (two required, two optional) - 
                   1721:     $domain - domain of new user
                   1722:     $name - name of form element
                   1723:     $default - Value of 'default' causes a default item to be first 
                   1724:                             option, and selected by default. 
                   1725:     $hide - Value of 'hide' causes hiding of the name of the server, 
                   1726:                             if 1 server found, or default, if 0 found.
1.594     raeburn  1727: output: returns 2 items: 
1.586     raeburn  1728: (a) form element which contains either:
                   1729:    (i) <select name="$name">
                   1730:         <option value="$hostid1">$hostid $servers{$hostid}</option>
                   1731:         <option value="$hostid2">$hostid $servers{$hostid}</option>       
                   1732:        </select>
                   1733:        form item if there are multiple library servers in $domain, or
                   1734:    (ii) an <input type="hidden" name="$name" value="$hostid" /> form item 
                   1735:        if there is only one library server in $domain.
                   1736: 
                   1737: (b) number of library servers found.
                   1738: 
                   1739: See loncreateuser.pm for example of use.
1.35      matthew  1740: 
                   1741: =cut
                   1742: 
                   1743: #-------------------------------------------
1.586     raeburn  1744: sub home_server_form_item {
                   1745:     my ($domain,$name,$default,$hide) = @_;
1.513     albertel 1746:     my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586     raeburn  1747:     my $result;
                   1748:     my $numlib = keys(%servers);
                   1749:     if ($numlib > 1) {
                   1750:         $result .= '<select name="'.$name.'" />'."\n";
                   1751:         if ($default) {
                   1752:             $result .= '<option value="default" selected>'.&mt('default').
                   1753:                        '</option>'."\n";
                   1754:         }
                   1755:         foreach my $hostid (sort(keys(%servers))) {
                   1756:             $result.= '<option value="'.$hostid.'">'.
                   1757: 	              $hostid.' '.$servers{$hostid}."</option>\n";
                   1758:         }
                   1759:         $result .= '</select>'."\n";
                   1760:     } elsif ($numlib == 1) {
                   1761:         my $hostid;
                   1762:         foreach my $item (keys(%servers)) {
                   1763:             $hostid = $item;
                   1764:         }
                   1765:         $result .= '<input type="hidden" name="'.$name.'" value="'.
                   1766:                    $hostid.'" />';
                   1767:                    if (!$hide) {
                   1768:                        $result .= $hostid.' '.$servers{$hostid};
                   1769:                    }
                   1770:                    $result .= "\n";
                   1771:     } elsif ($default) {
                   1772:         $result .= '<input type="hidden" name="'.$name.
                   1773:                    '" value="default" />';
                   1774:                    if (!$hide) {
                   1775:                        $result .= &mt('default');
                   1776:                    }
                   1777:                    $result .= "\n";
1.33      matthew  1778:     }
1.586     raeburn  1779:     return ($result,$numlib);
1.33      matthew  1780: }
1.112     bowersj2 1781: 
                   1782: =pod
                   1783: 
1.534     albertel 1784: =back 
                   1785: 
1.112     bowersj2 1786: =cut
1.87      matthew  1787: 
                   1788: ###############################################################
1.112     bowersj2 1789: ##                  Decoding User Agent                      ##
1.87      matthew  1790: ###############################################################
                   1791: 
                   1792: =pod
                   1793: 
1.112     bowersj2 1794: =head1 Decoding the User Agent
                   1795: 
                   1796: =over 4
                   1797: 
                   1798: =item * &decode_user_agent()
1.87      matthew  1799: 
                   1800: Inputs: $r
                   1801: 
                   1802: Outputs:
                   1803: 
                   1804: =over 4
                   1805: 
1.112     bowersj2 1806: =item * $httpbrowser
1.87      matthew  1807: 
1.112     bowersj2 1808: =item * $clientbrowser
1.87      matthew  1809: 
1.112     bowersj2 1810: =item * $clientversion
1.87      matthew  1811: 
1.112     bowersj2 1812: =item * $clientmathml
1.87      matthew  1813: 
1.112     bowersj2 1814: =item * $clientunicode
1.87      matthew  1815: 
1.112     bowersj2 1816: =item * $clientos
1.87      matthew  1817: 
                   1818: =back
                   1819: 
1.157     matthew  1820: =back 
                   1821: 
1.87      matthew  1822: =cut
                   1823: 
                   1824: ###############################################################
                   1825: ###############################################################
                   1826: sub decode_user_agent {
1.247     albertel 1827:     my ($r)=@_;
1.87      matthew  1828:     my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
                   1829:     my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
                   1830:     my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247     albertel 1831:     if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87      matthew  1832:     my $clientbrowser='unknown';
                   1833:     my $clientversion='0';
                   1834:     my $clientmathml='';
                   1835:     my $clientunicode='0';
                   1836:     for (my $i=0;$i<=$#browsertype;$i++) {
                   1837:         my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\:/,$browsertype[$i]);
                   1838: 	if (($httpbrowser=~/$match/i)  && ($httpbrowser!~/$notmatch/i)) {
                   1839: 	    $clientbrowser=$bname;
                   1840:             $httpbrowser=~/$vreg/i;
                   1841: 	    $clientversion=$1;
                   1842:             $clientmathml=($clientversion>=$minv);
                   1843:             $clientunicode=($clientversion>=$univ);
                   1844: 	}
                   1845:     }
                   1846:     my $clientos='unknown';
                   1847:     if (($httpbrowser=~/linux/i) ||
                   1848:         ($httpbrowser=~/unix/i) ||
                   1849:         ($httpbrowser=~/ux/i) ||
                   1850:         ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
                   1851:     if (($httpbrowser=~/vax/i) ||
                   1852:         ($httpbrowser=~/vms/i)) { $clientos='vms'; }
                   1853:     if ($httpbrowser=~/next/i) { $clientos='next'; }
                   1854:     if (($httpbrowser=~/mac/i) ||
                   1855:         ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
                   1856:     if ($httpbrowser=~/win/i) { $clientos='win'; }
                   1857:     if ($httpbrowser=~/embed/i) { $clientos='pda'; }
                   1858:     return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
                   1859:             $clientunicode,$clientos,);
                   1860: }
                   1861: 
1.32      matthew  1862: ###############################################################
                   1863: ##    Authentication changing form generation subroutines    ##
                   1864: ###############################################################
                   1865: ##
                   1866: ## All of the authform_xxxxxxx subroutines take their inputs in a
                   1867: ## hash, and have reasonable default values.
                   1868: ##
                   1869: ##    formname = the name given in the <form> tag.
1.35      matthew  1870: #-------------------------------------------
                   1871: 
1.45      matthew  1872: =pod
                   1873: 
1.112     bowersj2 1874: =head1 Authentication Routines
                   1875: 
                   1876: =over 4
                   1877: 
1.648     raeburn  1878: =item * &authform_xxxxxx()
1.35      matthew  1879: 
                   1880: The authform_xxxxxx subroutines provide javascript and html forms which 
                   1881: handle some of the conveniences required for authentication forms.  
                   1882: This is not an optimal method, but it works.  
                   1883: 
                   1884: =over 4
                   1885: 
1.112     bowersj2 1886: =item * authform_header
1.35      matthew  1887: 
1.112     bowersj2 1888: =item * authform_authorwarning
1.35      matthew  1889: 
1.112     bowersj2 1890: =item * authform_nochange
1.35      matthew  1891: 
1.112     bowersj2 1892: =item * authform_kerberos
1.35      matthew  1893: 
1.112     bowersj2 1894: =item * authform_internal
1.35      matthew  1895: 
1.112     bowersj2 1896: =item * authform_filesystem
1.35      matthew  1897: 
                   1898: =back
                   1899: 
1.648     raeburn  1900: See loncreateuser.pm for invocation and use examples.
1.157     matthew  1901: 
1.35      matthew  1902: =cut
                   1903: 
                   1904: #-------------------------------------------
1.32      matthew  1905: sub authform_header{  
                   1906:     my %in = (
                   1907:         formname => 'cu',
1.80      albertel 1908:         kerb_def_dom => '',
1.32      matthew  1909:         @_,
                   1910:     );
                   1911:     $in{'formname'} = 'document.' . $in{'formname'};
                   1912:     my $result='';
1.80      albertel 1913: 
                   1914: #---------------------------------------------- Code for upper case translation
                   1915:     my $Javascript_toUpperCase;
                   1916:     unless ($in{kerb_def_dom}) {
                   1917:         $Javascript_toUpperCase =<<"END";
                   1918:         switch (choice) {
                   1919:            case 'krb': currentform.elements[choicearg].value =
                   1920:                currentform.elements[choicearg].value.toUpperCase();
                   1921:                break;
                   1922:            default:
                   1923:         }
                   1924: END
                   1925:     } else {
                   1926:         $Javascript_toUpperCase = "";
                   1927:     }
                   1928: 
1.165     raeburn  1929:     my $radioval = "'nochange'";
1.591     raeburn  1930:     if (defined($in{'curr_authtype'})) {
                   1931:         if ($in{'curr_authtype'} ne '') {
                   1932:             $radioval = "'".$in{'curr_authtype'}."arg'";
                   1933:         }
1.174     matthew  1934:     }
1.165     raeburn  1935:     my $argfield = 'null';
1.591     raeburn  1936:     if (defined($in{'mode'})) {
1.165     raeburn  1937:         if ($in{'mode'} eq 'modifycourse')  {
1.591     raeburn  1938:             if (defined($in{'curr_autharg'})) {
                   1939:                 if ($in{'curr_autharg'} ne '') {
1.165     raeburn  1940:                     $argfield = "'$in{'curr_autharg'}'";
                   1941:                 }
                   1942:             }
                   1943:         }
                   1944:     }
                   1945: 
1.32      matthew  1946:     $result.=<<"END";
                   1947: var current = new Object();
1.165     raeburn  1948: current.radiovalue = $radioval;
                   1949: current.argfield = $argfield;
1.32      matthew  1950: 
                   1951: function changed_radio(choice,currentform) {
                   1952:     var choicearg = choice + 'arg';
                   1953:     // If a radio button in changed, we need to change the argfield
                   1954:     if (current.radiovalue != choice) {
                   1955:         current.radiovalue = choice;
                   1956:         if (current.argfield != null) {
                   1957:             currentform.elements[current.argfield].value = '';
                   1958:         }
                   1959:         if (choice == 'nochange') {
                   1960:             current.argfield = null;
                   1961:         } else {
                   1962:             current.argfield = choicearg;
                   1963:             switch(choice) {
                   1964:                 case 'krb': 
                   1965:                     currentform.elements[current.argfield].value = 
                   1966:                         "$in{'kerb_def_dom'}";
                   1967:                 break;
                   1968:               default:
                   1969:                 break;
                   1970:             }
                   1971:         }
                   1972:     }
                   1973:     return;
                   1974: }
1.22      www      1975: 
1.32      matthew  1976: function changed_text(choice,currentform) {
                   1977:     var choicearg = choice + 'arg';
                   1978:     if (currentform.elements[choicearg].value !='') {
1.80      albertel 1979:         $Javascript_toUpperCase
1.32      matthew  1980:         // clear old field
                   1981:         if ((current.argfield != choicearg) && (current.argfield != null)) {
                   1982:             currentform.elements[current.argfield].value = '';
                   1983:         }
                   1984:         current.argfield = choicearg;
                   1985:     }
                   1986:     set_auth_radio_buttons(choice,currentform);
                   1987:     return;
1.20      www      1988: }
1.32      matthew  1989: 
                   1990: function set_auth_radio_buttons(newvalue,currentform) {
                   1991:     var i=0;
                   1992:     while (i < currentform.login.length) {
                   1993:         if (currentform.login[i].value == newvalue) { break; }
                   1994:         i++;
                   1995:     }
                   1996:     if (i == currentform.login.length) {
                   1997:         return;
                   1998:     }
                   1999:     current.radiovalue = newvalue;
                   2000:     currentform.login[i].checked = true;
                   2001:     return;
                   2002: }
                   2003: END
                   2004:     return $result;
                   2005: }
                   2006: 
                   2007: sub authform_authorwarning{
                   2008:     my $result='';
1.144     matthew  2009:     $result='<i>'.
                   2010:         &mt('As a general rule, only authors or co-authors should be '.
                   2011:             'filesystem authenticated '.
                   2012:             '(which allows access to the server filesystem).')."</i>\n";
1.32      matthew  2013:     return $result;
                   2014: }
                   2015: 
                   2016: sub authform_nochange{  
                   2017:     my %in = (
                   2018:               formname => 'document.cu',
                   2019:               kerb_def_dom => 'MSU.EDU',
                   2020:               @_,
                   2021:           );
1.586     raeburn  2022:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'}); 
                   2023:     my $result;
                   2024:     if (keys(%can_assign) == 0) {
                   2025:         $result = &mt('Under you current role you are not permitted to change login settings for this user');  
                   2026:     } else {
                   2027:         $result = '<label>'.&mt('[_1] Do not change login data',
                   2028:                   '<input type="radio" name="login" value="nochange" '.
                   2029:                   'checked="checked" onclick="'.
1.281     albertel 2030:             "javascript:changed_radio('nochange',$in{'formname'});".'" />').
                   2031: 	    '</label>';
1.586     raeburn  2032:     }
1.32      matthew  2033:     return $result;
                   2034: }
                   2035: 
1.591     raeburn  2036: sub authform_kerberos {
1.32      matthew  2037:     my %in = (
                   2038:               formname => 'document.cu',
                   2039:               kerb_def_dom => 'MSU.EDU',
1.80      albertel 2040:               kerb_def_auth => 'krb4',
1.32      matthew  2041:               @_,
                   2042:               );
1.586     raeburn  2043:     my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
                   2044:         $autharg,$jscall);
                   2045:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.80      albertel 2046:     if ($in{'kerb_def_auth'} eq 'krb5') {
1.586     raeburn  2047:        $check5 = ' checked="on"';
1.80      albertel 2048:     } else {
1.586     raeburn  2049:        $check4 = ' checked="on"';
1.80      albertel 2050:     }
1.165     raeburn  2051:     $krbarg = $in{'kerb_def_dom'};
1.591     raeburn  2052:     if (defined($in{'curr_authtype'})) {
                   2053:         if ($in{'curr_authtype'} eq 'krb') {
1.586     raeburn  2054:             $krbcheck = ' checked="on"';
1.623     raeburn  2055:             if (defined($in{'mode'})) {
                   2056:                 if ($in{'mode'} eq 'modifyuser') {
                   2057:                     $krbcheck = '';
                   2058:                 }
                   2059:             }
1.591     raeburn  2060:             if (defined($in{'curr_kerb_ver'})) {
                   2061:                 if ($in{'curr_krb_ver'} eq '5') {
                   2062:                     $check5 = ' checked="on"';
                   2063:                     $check4 = '';
                   2064:                 } else {
                   2065:                     $check4 = ' checked="on"';
                   2066:                     $check5 = '';
                   2067:                 }
1.586     raeburn  2068:             }
1.591     raeburn  2069:             if (defined($in{'curr_autharg'})) {
1.165     raeburn  2070:                 $krbarg = $in{'curr_autharg'};
                   2071:             }
1.586     raeburn  2072:             if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591     raeburn  2073:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2074:                     $result = 
                   2075:     &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
                   2076:         $in{'curr_autharg'},$krbver);
                   2077:                 } else {
                   2078:                     $result =
                   2079:     &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
                   2080:                 }
                   2081:                 return $result; 
                   2082:             }
                   2083:         }
                   2084:     } else {
                   2085:         if ($authnum == 1) {
                   2086:             $authtype = '<input type="hidden" name="login" value="krb">';
1.165     raeburn  2087:         }
                   2088:     }
1.586     raeburn  2089:     if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
                   2090:         return;
1.587     raeburn  2091:     } elsif ($authtype eq '') {
1.591     raeburn  2092:         if (defined($in{'mode'})) {
1.587     raeburn  2093:             if ($in{'mode'} eq 'modifycourse') {
                   2094:                 if ($authnum == 1) {
                   2095:                     $authtype = '<input type="hidden" name="login" value="krb">';
                   2096:                 }
                   2097:             }
                   2098:         }
1.586     raeburn  2099:     }
                   2100:     $jscall = "javascript:changed_radio('krb',$in{'formname'});";
                   2101:     if ($authtype eq '') {
                   2102:         $authtype = '<input type="radio" name="login" value="krb" '.
                   2103:                     'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
                   2104:                     $krbcheck.' />';
                   2105:     }
                   2106:     if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
                   2107:         ($can_assign{'krb4'} && !$can_assign{'krb5'} && 
                   2108:          $in{'curr_authtype'} eq 'krb5') ||
                   2109:         (!$can_assign{'krb4'} && $can_assign{'krb5'} && 
                   2110:          $in{'curr_authtype'} eq 'krb4')) {
                   2111:         $result .= &mt
1.144     matthew  2112:         ('[_1] Kerberos authenticated with domain [_2] '.
1.281     albertel 2113:          '[_3] Version 4 [_4] Version 5 [_5]',
1.586     raeburn  2114:          '<label>'.$authtype,
1.281     albertel 2115:          '</label><input type="text" size="10" name="krbarg" '.
1.165     raeburn  2116:              'value="'.$krbarg.'" '.
1.144     matthew  2117:              'onchange="'.$jscall.'" />',
1.281     albertel 2118:          '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
                   2119:          '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
                   2120: 	 '</label>');
1.586     raeburn  2121:     } elsif ($can_assign{'krb4'}) {
                   2122:         $result .= &mt
                   2123:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2124:          '[_3] Version 4 [_4]',
                   2125:          '<label>'.$authtype,
                   2126:          '</label><input type="text" size="10" name="krbarg" '.
                   2127:              'value="'.$krbarg.'" '.
                   2128:              'onchange="'.$jscall.'" />',
                   2129:          '<label><input type="hidden" name="krbver" value="4" />',
                   2130:          '</label>');
                   2131:     } elsif ($can_assign{'krb5'}) {
                   2132:         $result .= &mt
                   2133:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2134:          '[_3] Version 5 [_4]',
                   2135:          '<label>'.$authtype,
                   2136:          '</label><input type="text" size="10" name="krbarg" '.
                   2137:              'value="'.$krbarg.'" '.
                   2138:              'onchange="'.$jscall.'" />',
                   2139:          '<label><input type="hidden" name="krbver" value="5" />',
                   2140:          '</label>');
                   2141:     }
1.32      matthew  2142:     return $result;
                   2143: }
                   2144: 
                   2145: sub authform_internal{  
1.586     raeburn  2146:     my %in = (
1.32      matthew  2147:                 formname => 'document.cu',
                   2148:                 kerb_def_dom => 'MSU.EDU',
                   2149:                 @_,
                   2150:                 );
1.586     raeburn  2151:     my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
                   2152:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2153:     if (defined($in{'curr_authtype'})) {
                   2154:         if ($in{'curr_authtype'} eq 'int') {
1.586     raeburn  2155:             if ($can_assign{'int'}) {
                   2156:                 $intcheck = 'checked="on" ';
1.623     raeburn  2157:                 if (defined($in{'mode'})) {
                   2158:                     if ($in{'mode'} eq 'modifyuser') {
                   2159:                         $intcheck = '';
                   2160:                     }
                   2161:                 }
1.591     raeburn  2162:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2163:                     $intarg = $in{'curr_autharg'};
                   2164:                 }
                   2165:             } else {
                   2166:                 $result = &mt('Currently internally authenticated.');
                   2167:                 return $result;
1.165     raeburn  2168:             }
                   2169:         }
1.586     raeburn  2170:     } else {
                   2171:         if ($authnum == 1) {
                   2172:             $authtype = '<input type="hidden" name="login" value="int">';
                   2173:         }
                   2174:     }
                   2175:     if (!$can_assign{'int'}) {
                   2176:         return;
1.587     raeburn  2177:     } elsif ($authtype eq '') {
1.591     raeburn  2178:         if (defined($in{'mode'})) {
1.587     raeburn  2179:             if ($in{'mode'} eq 'modifycourse') {
                   2180:                 if ($authnum == 1) {
                   2181:                     $authtype = '<input type="hidden" name="login" value="int">';
                   2182:                 }
                   2183:             }
                   2184:         }
1.165     raeburn  2185:     }
1.586     raeburn  2186:     $jscall = "javascript:changed_radio('int',$in{'formname'});";
                   2187:     if ($authtype eq '') {
                   2188:         $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
                   2189:                     ' onchange="'.$jscall.'" onclick="'.$jscall.'" />';
                   2190:     }
1.605     bisitz   2191:     $autharg = '<input type="password" size="10" name="intarg" value="'.
1.586     raeburn  2192:                $intarg.'" onchange="'.$jscall.'" />';
                   2193:     $result = &mt
1.144     matthew  2194:         ('[_1] Internally authenticated (with initial password [_2])',
1.586     raeburn  2195:          '<label>'.$authtype,'</label>'.$autharg);
1.620     www      2196:     $result.="<label><input type=\"checkbox\" name=\"visible\" onClick='if (this.checked) { this.form.intarg.type=\"text\" } else { this.form.intarg.type=\"password\" }' />".&mt('Visible input').'</label>';
1.32      matthew  2197:     return $result;
                   2198: }
                   2199: 
                   2200: sub authform_local{  
                   2201:     my %in = (
                   2202:               formname => 'document.cu',
                   2203:               kerb_def_dom => 'MSU.EDU',
                   2204:               @_,
                   2205:               );
1.586     raeburn  2206:     my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
                   2207:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2208:     if (defined($in{'curr_authtype'})) {
                   2209:         if ($in{'curr_authtype'} eq 'loc') {
1.586     raeburn  2210:             if ($can_assign{'loc'}) {
                   2211:                 $loccheck = 'checked="on" ';
1.623     raeburn  2212:                 if (defined($in{'mode'})) {
                   2213:                     if ($in{'mode'} eq 'modifyuser') {
                   2214:                         $loccheck = '';
                   2215:                     }
                   2216:                 }
1.591     raeburn  2217:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2218:                     $locarg = $in{'curr_autharg'};
                   2219:                 }
                   2220:             } else {
                   2221:                 $result = &mt('Currently using local (institutional) authentication.');
                   2222:                 return $result;
1.165     raeburn  2223:             }
                   2224:         }
1.586     raeburn  2225:     } else {
                   2226:         if ($authnum == 1) {
                   2227:             $authtype = '<input type="hidden" name="login" value="loc">';
                   2228:         }
                   2229:     }
                   2230:     if (!$can_assign{'loc'}) {
                   2231:         return;
1.587     raeburn  2232:     } elsif ($authtype eq '') {
1.591     raeburn  2233:         if (defined($in{'mode'})) {
1.587     raeburn  2234:             if ($in{'mode'} eq 'modifycourse') {
                   2235:                 if ($authnum == 1) {
                   2236:                     $authtype = '<input type="hidden" name="login" value="loc">';
                   2237:                 }
                   2238:             }
                   2239:         }
1.165     raeburn  2240:     }
1.586     raeburn  2241:     $jscall = "javascript:changed_radio('loc',$in{'formname'});";
                   2242:     if ($authtype eq '') {
                   2243:         $authtype = '<input type="radio" name="login" value="loc" '.
                   2244:                     $loccheck.' onchange="'.$jscall.'" onclick="'.
                   2245:                     $jscall.'" />';
                   2246:     }
                   2247:     $autharg = '<input type="text" size="10" name="locarg" value="'.
                   2248:                $locarg.'" onchange="'.$jscall.'" />';
                   2249:     $result = &mt('[_1] Local Authentication with argument [_2]',
                   2250:                   '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  2251:     return $result;
                   2252: }
                   2253: 
                   2254: sub authform_filesystem{  
                   2255:     my %in = (
                   2256:               formname => 'document.cu',
                   2257:               kerb_def_dom => 'MSU.EDU',
                   2258:               @_,
                   2259:               );
1.586     raeburn  2260:     my ($fsyscheck,$result,$authtype,$autharg,$jscall);
                   2261:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2262:     if (defined($in{'curr_authtype'})) {
                   2263:         if ($in{'curr_authtype'} eq 'fsys') {
1.586     raeburn  2264:             if ($can_assign{'fsys'}) {
                   2265:                 $fsyscheck = 'checked="on" ';
1.623     raeburn  2266:                 if (defined($in{'mode'})) {
                   2267:                     if ($in{'mode'} eq 'modifyuser') {
                   2268:                         $fsyscheck = '';
                   2269:                     }
                   2270:                 }
1.586     raeburn  2271:             } else {
                   2272:                 $result = &mt('Currently Filesystem Authenticated.');
                   2273:                 return $result;
                   2274:             }           
                   2275:         }
                   2276:     } else {
                   2277:         if ($authnum == 1) {
                   2278:             $authtype = '<input type="hidden" name="login" value="fsys">';
                   2279:         }
                   2280:     }
                   2281:     if (!$can_assign{'fsys'}) {
                   2282:         return;
1.587     raeburn  2283:     } elsif ($authtype eq '') {
1.591     raeburn  2284:         if (defined($in{'mode'})) {
1.587     raeburn  2285:             if ($in{'mode'} eq 'modifycourse') {
                   2286:                 if ($authnum == 1) {
                   2287:                     $authtype = '<input type="hidden" name="login" value="fsys">';
                   2288:                 }
                   2289:             }
                   2290:         }
1.586     raeburn  2291:     }
                   2292:     $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
                   2293:     if ($authtype eq '') {
                   2294:         $authtype = '<input type="radio" name="login" value="fsys" '.
                   2295:                     $fsyscheck.' onchange="'.$jscall.'" onclick="'.
                   2296:                     $jscall.'" />';
                   2297:     }
                   2298:     $autharg = '<input type="text" size="10" name="fsysarg" value=""'.
                   2299:                ' onchange="'.$jscall.'" />';
                   2300:     $result = &mt
1.144     matthew  2301:         ('[_1] Filesystem Authenticated (with initial password [_2])',
1.281     albertel 2302:          '<label><input type="radio" name="login" value="fsys" '.
1.586     raeburn  2303:          $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.605     bisitz   2304:          '</label><input type="password" size="10" name="fsysarg" value="" '.
1.144     matthew  2305:                   'onchange="'.$jscall.'" />');
1.32      matthew  2306:     return $result;
                   2307: }
                   2308: 
1.586     raeburn  2309: sub get_assignable_auth {
                   2310:     my ($dom) = @_;
                   2311:     if ($dom eq '') {
                   2312:         $dom = $env{'request.role.domain'};
                   2313:     }
                   2314:     my %can_assign = (
                   2315:                           krb4 => 1,
                   2316:                           krb5 => 1,
                   2317:                           int  => 1,
                   2318:                           loc  => 1,
                   2319:                      );
                   2320:     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
                   2321:     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   2322:         if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
                   2323:             my $authhash = $domconfig{'usercreation'}{'authtypes'};
                   2324:             my $context;
                   2325:             if ($env{'request.role'} =~ /^au/) {
                   2326:                 $context = 'author';
                   2327:             } elsif ($env{'request.role'} =~ /^dc/) {
                   2328:                 $context = 'domain';
                   2329:             } elsif ($env{'request.course.id'}) {
                   2330:                 $context = 'course';
                   2331:             }
                   2332:             if ($context) {
                   2333:                 if (ref($authhash->{$context}) eq 'HASH') {
                   2334:                    %can_assign = %{$authhash->{$context}}; 
                   2335:                 }
                   2336:             }
                   2337:         }
                   2338:     }
                   2339:     my $authnum = 0;
                   2340:     foreach my $key (keys(%can_assign)) {
                   2341:         if ($can_assign{$key}) {
                   2342:             $authnum ++;
                   2343:         }
                   2344:     }
                   2345:     if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
                   2346:         $authnum --;
                   2347:     }
                   2348:     return ($authnum,%can_assign);
                   2349: }
                   2350: 
1.80      albertel 2351: ###############################################################
                   2352: ##    Get Kerberos Defaults for Domain                 ##
                   2353: ###############################################################
                   2354: ##
                   2355: ## Returns default kerberos version and an associated argument
                   2356: ## as listed in file domain.tab. If not listed, provides
                   2357: ## appropriate default domain and kerberos version.
                   2358: ##
                   2359: #-------------------------------------------
                   2360: 
                   2361: =pod
                   2362: 
1.648     raeburn  2363: =item * &get_kerberos_defaults()
1.80      albertel 2364: 
                   2365: get_kerberos_defaults($target_domain) returns the default kerberos
1.641     raeburn  2366: version and domain. If not found, it defaults to version 4 and the 
                   2367: domain of the server.
1.80      albertel 2368: 
1.648     raeburn  2369: =over 4
                   2370: 
1.80      albertel 2371: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
                   2372: 
1.648     raeburn  2373: =back
                   2374: 
                   2375: =back
                   2376: 
1.80      albertel 2377: =cut
                   2378: 
                   2379: #-------------------------------------------
                   2380: sub get_kerberos_defaults {
                   2381:     my $domain=shift;
1.641     raeburn  2382:     my ($krbdef,$krbdefdom);
                   2383:     my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
                   2384:     if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
                   2385:         $krbdef = $domdefaults{'auth_def'};
                   2386:         $krbdefdom = $domdefaults{'auth_arg_def'};
                   2387:     } else {
1.80      albertel 2388:         $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
                   2389:         my $krbdefdom=$1;
                   2390:         $krbdefdom=~tr/a-z/A-Z/;
                   2391:         $krbdef = "krb4";
                   2392:     }
                   2393:     return ($krbdef,$krbdefdom);
                   2394: }
1.112     bowersj2 2395: 
1.32      matthew  2396: 
1.46      matthew  2397: ###############################################################
                   2398: ##                Thesaurus Functions                        ##
                   2399: ###############################################################
1.20      www      2400: 
1.46      matthew  2401: =pod
1.20      www      2402: 
1.112     bowersj2 2403: =head1 Thesaurus Functions
                   2404: 
                   2405: =over 4
                   2406: 
1.648     raeburn  2407: =item * &initialize_keywords()
1.46      matthew  2408: 
                   2409: Initializes the package variable %Keywords if it is empty.  Uses the
                   2410: package variable $thesaurus_db_file.
                   2411: 
                   2412: =cut
                   2413: 
                   2414: ###################################################
                   2415: 
                   2416: sub initialize_keywords {
                   2417:     return 1 if (scalar keys(%Keywords));
                   2418:     # If we are here, %Keywords is empty, so fill it up
                   2419:     #   Make sure the file we need exists...
                   2420:     if (! -e $thesaurus_db_file) {
                   2421:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
                   2422:                                  " failed because it does not exist");
                   2423:         return 0;
                   2424:     }
                   2425:     #   Set up the hash as a database
                   2426:     my %thesaurus_db;
                   2427:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 2428:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  2429:         &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
                   2430:                                  $thesaurus_db_file);
                   2431:         return 0;
                   2432:     } 
                   2433:     #  Get the average number of appearances of a word.
                   2434:     my $avecount = $thesaurus_db{'average.count'};
                   2435:     #  Put keywords (those that appear > average) into %Keywords
                   2436:     while (my ($word,$data)=each (%thesaurus_db)) {
                   2437:         my ($count,undef) = split /:/,$data;
                   2438:         $Keywords{$word}++ if ($count > $avecount);
                   2439:     }
                   2440:     untie %thesaurus_db;
                   2441:     # Remove special values from %Keywords.
1.356     albertel 2442:     foreach my $value ('total.count','average.count') {
                   2443:         delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586     raeburn  2444:   }
1.46      matthew  2445:     return 1;
                   2446: }
                   2447: 
                   2448: ###################################################
                   2449: 
                   2450: =pod
                   2451: 
1.648     raeburn  2452: =item * &keyword($word)
1.46      matthew  2453: 
                   2454: Returns true if $word is a keyword.  A keyword is a word that appears more 
                   2455: than the average number of times in the thesaurus database.  Calls 
                   2456: &initialize_keywords
                   2457: 
                   2458: =cut
                   2459: 
                   2460: ###################################################
1.20      www      2461: 
                   2462: sub keyword {
1.46      matthew  2463:     return if (!&initialize_keywords());
                   2464:     my $word=lc(shift());
                   2465:     $word=~s/\W//g;
                   2466:     return exists($Keywords{$word});
1.20      www      2467: }
1.46      matthew  2468: 
                   2469: ###############################################################
                   2470: 
                   2471: =pod 
1.20      www      2472: 
1.648     raeburn  2473: =item * &get_related_words()
1.46      matthew  2474: 
1.160     matthew  2475: Look up a word in the thesaurus.  Takes a scalar argument and returns
1.46      matthew  2476: an array of words.  If the keyword is not in the thesaurus, an empty array
                   2477: will be returned.  The order of the words returned is determined by the
                   2478: database which holds them.
                   2479: 
                   2480: Uses global $thesaurus_db_file.
                   2481: 
                   2482: =cut
                   2483: 
                   2484: ###############################################################
                   2485: sub get_related_words {
                   2486:     my $keyword = shift;
                   2487:     my %thesaurus_db;
                   2488:     if (! -e $thesaurus_db_file) {
                   2489:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
                   2490:                                  "failed because the file does not exist");
                   2491:         return ();
                   2492:     }
                   2493:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 2494:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  2495:         return ();
                   2496:     } 
                   2497:     my @Words=();
1.429     www      2498:     my $count=0;
1.46      matthew  2499:     if (exists($thesaurus_db{$keyword})) {
1.356     albertel 2500: 	# The first element is the number of times
                   2501: 	# the word appears.  We do not need it now.
1.429     www      2502: 	my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
                   2503: 	my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
                   2504: 	my $threshold=$mostfrequentcount/10;
                   2505:         foreach my $possibleword (@RelatedWords) {
                   2506:             my ($word,$wordcount)=split(/\,/,$possibleword);
                   2507:             if ($wordcount>$threshold) {
                   2508: 		push(@Words,$word);
                   2509:                 $count++;
                   2510:                 if ($count>10) { last; }
                   2511: 	    }
1.20      www      2512:         }
                   2513:     }
1.46      matthew  2514:     untie %thesaurus_db;
                   2515:     return @Words;
1.14      harris41 2516: }
1.46      matthew  2517: 
1.112     bowersj2 2518: =pod
                   2519: 
                   2520: =back
                   2521: 
                   2522: =cut
1.61      www      2523: 
                   2524: # -------------------------------------------------------------- Plaintext name
1.81      albertel 2525: =pod
                   2526: 
1.112     bowersj2 2527: =head1 User Name Functions
                   2528: 
                   2529: =over 4
                   2530: 
1.648     raeburn  2531: =item * &plainname($uname,$udom,$first)
1.81      albertel 2532: 
1.112     bowersj2 2533: Takes a users logon name and returns it as a string in
1.226     albertel 2534: "first middle last generation" form 
                   2535: if $first is set to 'lastname' then it returns it as
                   2536: 'lastname generation, firstname middlename' if their is a lastname
1.81      albertel 2537: 
                   2538: =cut
1.61      www      2539: 
1.295     www      2540: 
1.81      albertel 2541: ###############################################################
1.61      www      2542: sub plainname {
1.226     albertel 2543:     my ($uname,$udom,$first)=@_;
1.537     albertel 2544:     return if (!defined($uname) || !defined($udom));
1.295     www      2545:     my %names=&getnames($uname,$udom);
1.226     albertel 2546:     my $name=&Apache::lonnet::format_name($names{'firstname'},
                   2547: 					  $names{'middlename'},
                   2548: 					  $names{'lastname'},
                   2549: 					  $names{'generation'},$first);
                   2550:     $name=~s/^\s+//;
1.62      www      2551:     $name=~s/\s+$//;
                   2552:     $name=~s/\s+/ /g;
1.353     albertel 2553:     if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62      www      2554:     return $name;
1.61      www      2555: }
1.66      www      2556: 
                   2557: # -------------------------------------------------------------------- Nickname
1.81      albertel 2558: =pod
                   2559: 
1.648     raeburn  2560: =item * &nickname($uname,$udom)
1.81      albertel 2561: 
                   2562: Gets a users name and returns it as a string as
                   2563: 
                   2564: "&quot;nickname&quot;"
1.66      www      2565: 
1.81      albertel 2566: if the user has a nickname or
                   2567: 
                   2568: "first middle last generation"
                   2569: 
                   2570: if the user does not
                   2571: 
                   2572: =cut
1.66      www      2573: 
                   2574: sub nickname {
                   2575:     my ($uname,$udom)=@_;
1.537     albertel 2576:     return if (!defined($uname) || !defined($udom));
1.295     www      2577:     my %names=&getnames($uname,$udom);
1.68      albertel 2578:     my $name=$names{'nickname'};
1.66      www      2579:     if ($name) {
                   2580:        $name='&quot;'.$name.'&quot;'; 
                   2581:     } else {
                   2582:        $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
                   2583: 	     $names{'lastname'}.' '.$names{'generation'};
                   2584:        $name=~s/\s+$//;
                   2585:        $name=~s/\s+/ /g;
                   2586:     }
                   2587:     return $name;
                   2588: }
                   2589: 
1.295     www      2590: sub getnames {
                   2591:     my ($uname,$udom)=@_;
1.537     albertel 2592:     return if (!defined($uname) || !defined($udom));
1.433     albertel 2593:     if ($udom eq 'public' && $uname eq 'public') {
                   2594: 	return ('lastname' => &mt('Public'));
                   2595:     }
1.295     www      2596:     my $id=$uname.':'.$udom;
                   2597:     my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
                   2598:     if ($cached) {
                   2599: 	return %{$names};
                   2600:     } else {
                   2601: 	my %loadnames=&Apache::lonnet::get('environment',
                   2602:                     ['firstname','middlename','lastname','generation','nickname'],
                   2603: 					 $udom,$uname);
                   2604: 	&Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
                   2605: 	return %loadnames;
                   2606:     }
                   2607: }
1.61      www      2608: 
1.542     raeburn  2609: # -------------------------------------------------------------------- getemails
1.648     raeburn  2610: 
1.542     raeburn  2611: =pod
                   2612: 
1.648     raeburn  2613: =item * &getemails($uname,$udom)
1.542     raeburn  2614: 
                   2615: Gets a user's email information and returns it as a hash with keys:
                   2616: notification, critnotification, permanentemail
                   2617: 
                   2618: For notification and critnotification, values are comma-separated lists 
1.648     raeburn  2619: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542     raeburn  2620:  
1.648     raeburn  2621: 
1.542     raeburn  2622: =cut
                   2623: 
1.648     raeburn  2624: 
1.466     albertel 2625: sub getemails {
                   2626:     my ($uname,$udom)=@_;
                   2627:     if ($udom eq 'public' && $uname eq 'public') {
                   2628: 	return;
                   2629:     }
1.467     www      2630:     if (!$udom) { $udom=$env{'user.domain'}; }
                   2631:     if (!$uname) { $uname=$env{'user.name'}; }
1.466     albertel 2632:     my $id=$uname.':'.$udom;
                   2633:     my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
                   2634:     if ($cached) {
                   2635: 	return %{$names};
                   2636:     } else {
                   2637: 	my %loadnames=&Apache::lonnet::get('environment',
                   2638:                     			   ['notification','critnotification',
                   2639: 					    'permanentemail'],
                   2640: 					   $udom,$uname);
                   2641: 	&Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
                   2642: 	return %loadnames;
                   2643:     }
                   2644: }
                   2645: 
1.551     albertel 2646: sub flush_email_cache {
                   2647:     my ($uname,$udom)=@_;
                   2648:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   2649:     if (!$uname) { $uname=$env{'user.name'};   }
                   2650:     return if ($udom eq 'public' && $uname eq 'public');
                   2651:     my $id=$uname.':'.$udom;
                   2652:     &Apache::lonnet::devalidate_cache_new('emailscache',$id);
                   2653: }
                   2654: 
1.61      www      2655: # ------------------------------------------------------------------ Screenname
1.81      albertel 2656: 
                   2657: =pod
                   2658: 
1.648     raeburn  2659: =item * &screenname($uname,$udom)
1.81      albertel 2660: 
                   2661: Gets a users screenname and returns it as a string
                   2662: 
                   2663: =cut
1.61      www      2664: 
                   2665: sub screenname {
                   2666:     my ($uname,$udom)=@_;
1.258     albertel 2667:     if ($uname eq $env{'user.name'} &&
                   2668: 	$udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212     albertel 2669:     my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68      albertel 2670:     return $names{'screenname'};
1.62      www      2671: }
                   2672: 
1.212     albertel 2673: 
1.62      www      2674: # ------------------------------------------------------------- Message Wrapper
                   2675: 
                   2676: sub messagewrapper {
1.369     www      2677:     my ($link,$username,$domain,$subject,$text)=@_;
1.62      www      2678:     return 
1.441     albertel 2679:         '<a href="/adm/email?compose=individual&amp;'.
                   2680:         'recname='.$username.'&amp;recdom='.$domain.
                   2681: 	'&amp;subject='.&escape($subject).'&amp;text='.&escape($text).'" '.
1.200     matthew  2682:         'title="'.&mt('Send message').'">'.$link.'</a>';
1.74      www      2683: }
                   2684: # --------------------------------------------------------------- Notes Wrapper
                   2685: 
                   2686: sub noteswrapper {
                   2687:     my ($link,$un,$do)=@_;
                   2688:     return 
                   2689: "<a href='/adm/email?recordftf=retrieve&recname=$un&recdom=$do'>$link</a>";
1.62      www      2690: }
                   2691: # ------------------------------------------------------------- Aboutme Wrapper
                   2692: 
                   2693: sub aboutmewrapper {
1.166     www      2694:     my ($link,$username,$domain,$target)=@_;
1.447     raeburn  2695:     if (!defined($username)  && !defined($domain)) {
                   2696:         return;
                   2697:     }
1.205     www      2698:     return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.454     banghart 2699: 	($target?' target="$target"':'').' title="'.&mt("View this user's personal page").'">'.$link.'</a>';
1.62      www      2700: }
                   2701: 
                   2702: # ------------------------------------------------------------ Syllabus Wrapper
                   2703: 
                   2704: 
                   2705: sub syllabuswrapper {
1.109     matthew  2706:     my ($linktext,$coursedir,$domain,$fontcolor)=@_;
                   2707:     if ($fontcolor) { 
                   2708:         $linktext='<font color="'.$fontcolor.'">'.$linktext.'</font>'; 
                   2709:     }
1.208     matthew  2710:     return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61      www      2711: }
1.14      harris41 2712: 
1.208     matthew  2713: sub track_student_link {
1.268     albertel 2714:     my ($linktext,$sname,$sdom,$target,$start) = @_;
                   2715:     my $link ="/adm/trackstudent?";
1.208     matthew  2716:     my $title = 'View recent activity';
                   2717:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   2718:         defined($sdom)  && $sdom  !~ /^\s*$/) {
1.268     albertel 2719:         $link .= "selected_student=$sname:$sdom";
1.208     matthew  2720:         $title .= ' of this student';
1.268     albertel 2721:     } 
1.208     matthew  2722:     if (defined($target) && $target !~ /^\s*$/) {
                   2723:         $target = qq{target="$target"};
                   2724:     } else {
                   2725:         $target = '';
                   2726:     }
1.268     albertel 2727:     if ($start) { $link.='&amp;start='.$start; }
1.554     albertel 2728:     $title = &mt($title);
                   2729:     $linktext = &mt($linktext);
1.448     albertel 2730:     return qq{<a href="$link" title="$title" $target>$linktext</a>}.
                   2731: 	&help_open_topic('View_recent_activity');
1.208     matthew  2732: }
                   2733: 
1.508     www      2734: # ===================================================== Display a student photo
                   2735: 
                   2736: 
1.509     albertel 2737: sub student_image_tag {
1.508     www      2738:     my ($domain,$user)=@_;
                   2739:     my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
                   2740:     if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
                   2741: 	return '<img src="'.$imgsrc.'" align="right" />';
                   2742:     } else {
                   2743: 	return '';
                   2744:     }
                   2745: }
                   2746: 
1.112     bowersj2 2747: =pod
                   2748: 
                   2749: =back
                   2750: 
                   2751: =head1 Access .tab File Data
                   2752: 
                   2753: =over 4
                   2754: 
1.648     raeburn  2755: =item * &languageids() 
1.112     bowersj2 2756: 
                   2757: returns list of all language ids
                   2758: 
                   2759: =cut
                   2760: 
1.14      harris41 2761: sub languageids {
1.16      harris41 2762:     return sort(keys(%language));
1.14      harris41 2763: }
                   2764: 
1.112     bowersj2 2765: =pod
                   2766: 
1.648     raeburn  2767: =item * &languagedescription() 
1.112     bowersj2 2768: 
                   2769: returns description of a specified language id
                   2770: 
                   2771: =cut
                   2772: 
1.14      harris41 2773: sub languagedescription {
1.125     www      2774:     my $code=shift;
                   2775:     return  ($supported_language{$code}?'* ':'').
                   2776:             $language{$code}.
1.126     www      2777: 	    ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145     www      2778: }
                   2779: 
                   2780: sub plainlanguagedescription {
                   2781:     my $code=shift;
                   2782:     return $language{$code};
                   2783: }
                   2784: 
                   2785: sub supportedlanguagecode {
                   2786:     my $code=shift;
                   2787:     return $supported_language{$code};
1.97      www      2788: }
                   2789: 
1.112     bowersj2 2790: =pod
                   2791: 
1.648     raeburn  2792: =item * &copyrightids() 
1.112     bowersj2 2793: 
                   2794: returns list of all copyrights
                   2795: 
                   2796: =cut
                   2797: 
                   2798: sub copyrightids {
                   2799:     return sort(keys(%cprtag));
                   2800: }
                   2801: 
                   2802: =pod
                   2803: 
1.648     raeburn  2804: =item * &copyrightdescription() 
1.112     bowersj2 2805: 
                   2806: returns description of a specified copyright id
                   2807: 
                   2808: =cut
                   2809: 
                   2810: sub copyrightdescription {
1.166     www      2811:     return &mt($cprtag{shift(@_)});
1.112     bowersj2 2812: }
1.197     matthew  2813: 
                   2814: =pod
                   2815: 
1.648     raeburn  2816: =item * &source_copyrightids() 
1.192     taceyjo1 2817: 
                   2818: returns list of all source copyrights
                   2819: 
                   2820: =cut
                   2821: 
                   2822: sub source_copyrightids {
                   2823:     return sort(keys(%scprtag));
                   2824: }
                   2825: 
                   2826: =pod
                   2827: 
1.648     raeburn  2828: =item * &source_copyrightdescription() 
1.192     taceyjo1 2829: 
                   2830: returns description of a specified source copyright id
                   2831: 
                   2832: =cut
                   2833: 
                   2834: sub source_copyrightdescription {
                   2835:     return &mt($scprtag{shift(@_)});
                   2836: }
1.112     bowersj2 2837: 
                   2838: =pod
                   2839: 
1.648     raeburn  2840: =item * &filecategories() 
1.112     bowersj2 2841: 
                   2842: returns list of all file categories
                   2843: 
                   2844: =cut
                   2845: 
                   2846: sub filecategories {
                   2847:     return sort(keys(%category_extensions));
                   2848: }
                   2849: 
                   2850: =pod
                   2851: 
1.648     raeburn  2852: =item * &filecategorytypes() 
1.112     bowersj2 2853: 
                   2854: returns list of file types belonging to a given file
                   2855: category
                   2856: 
                   2857: =cut
                   2858: 
                   2859: sub filecategorytypes {
1.356     albertel 2860:     my ($cat) = @_;
                   2861:     return @{$category_extensions{lc($cat)}};
1.112     bowersj2 2862: }
                   2863: 
                   2864: =pod
                   2865: 
1.648     raeburn  2866: =item * &fileembstyle() 
1.112     bowersj2 2867: 
                   2868: returns embedding style for a specified file type
                   2869: 
                   2870: =cut
                   2871: 
                   2872: sub fileembstyle {
                   2873:     return $fe{lc(shift(@_))};
1.169     www      2874: }
                   2875: 
1.351     www      2876: sub filemimetype {
                   2877:     return $fm{lc(shift(@_))};
                   2878: }
                   2879: 
1.169     www      2880: 
                   2881: sub filecategoryselect {
                   2882:     my ($name,$value)=@_;
1.189     matthew  2883:     return &select_form($value,$name,
1.169     www      2884: 			'' => &mt('Any category'),
                   2885: 			map { $_,$_ } sort(keys(%category_extensions)));
1.112     bowersj2 2886: }
                   2887: 
                   2888: =pod
                   2889: 
1.648     raeburn  2890: =item * &filedescription() 
1.112     bowersj2 2891: 
                   2892: returns description for a specified file type
                   2893: 
                   2894: =cut
                   2895: 
                   2896: sub filedescription {
1.188     matthew  2897:     my $file_description = $fd{lc(shift())};
                   2898:     $file_description =~ s:([\[\]]):~$1:g;
                   2899:     return &mt($file_description);
1.112     bowersj2 2900: }
                   2901: 
                   2902: =pod
                   2903: 
1.648     raeburn  2904: =item * &filedescriptionex() 
1.112     bowersj2 2905: 
                   2906: returns description for a specified file type with
                   2907: extra formatting
                   2908: 
                   2909: =cut
                   2910: 
                   2911: sub filedescriptionex {
                   2912:     my $ex=shift;
1.188     matthew  2913:     my $file_description = $fd{lc($ex)};
                   2914:     $file_description =~ s:([\[\]]):~$1:g;
                   2915:     return '.'.$ex.' '.&mt($file_description);
1.112     bowersj2 2916: }
                   2917: 
                   2918: # End of .tab access
                   2919: =pod
                   2920: 
                   2921: =back
                   2922: 
                   2923: =cut
                   2924: 
                   2925: # ------------------------------------------------------------------ File Types
                   2926: sub fileextensions {
                   2927:     return sort(keys(%fe));
                   2928: }
                   2929: 
1.97      www      2930: # ----------------------------------------------------------- Display Languages
                   2931: # returns a hash with all desired display languages
                   2932: #
                   2933: 
                   2934: sub display_languages {
                   2935:     my %languages=();
1.356     albertel 2936:     foreach my $lang (&preferred_languages()) {
                   2937: 	$languages{$lang}=1;
1.97      www      2938:     }
                   2939:     &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258     albertel 2940:     if ($env{'form.displaylanguage'}) {
1.356     albertel 2941: 	foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
                   2942: 	    $languages{$lang}=1;
1.97      www      2943:         }
                   2944:     }
                   2945:     return %languages;
1.14      harris41 2946: }
                   2947: 
1.117     www      2948: sub preferred_languages {
                   2949:     my @languages=();
1.654     www      2950:     if (($env{'request.role.adv'}) && ($env{'form.languages'})) {
                   2951:         @languages=(@languages,split(/\s*(\,|\;|\:)\s*/,$env{'form.languages'}));
                   2952:     }
1.258     albertel 2953:     if ($env{'course.'.$env{'request.course.id'}.'.languages'}) {
1.117     www      2954: 	@languages=(@languages,split(/\s*(\,|\;|\:)\s*/,
1.258     albertel 2955: 	         $env{'course.'.$env{'request.course.id'}.'.languages'}));
1.177     www      2956:     }
1.654     www      2957: 
1.258     albertel 2958:     if ($env{'environment.languages'}) {
1.459     albertel 2959: 	@languages=(@languages,
                   2960: 		    split(/\s*(\,|\;|\:)\s*/,$env{'environment.languages'}));
1.118     www      2961:     }
1.583     albertel 2962:     my $browser=$ENV{'HTTP_ACCEPT_LANGUAGE'};
1.162     www      2963:     if ($browser) {
1.583     albertel 2964: 	my @browser = 
                   2965: 	    map { (split(/\s*;\s*/,$_))[0] } (split(/\s*,\s*/,$browser));
                   2966: 	push(@languages,@browser);
1.162     www      2967:     }
1.641     raeburn  2968: 
                   2969:     foreach my $domtype ($env{'user.domain'},$env{'request.role.domain'},
                   2970:                          $Apache::lonnet::perlvar{'lonDefDomain'}) {
                   2971:         if ($domtype ne '') {
                   2972:             my %domdefs = &Apache::lonnet::get_domain_defaults($domtype);
                   2973:             if ($domdefs{'lang_def'} ne '') {
                   2974:                 push(@languages,$domdefs{'lang_def'});
                   2975:             }
                   2976:         }
1.118     www      2977:     }
                   2978: # turn "en-ca" into "en-ca,en"
                   2979:     my @genlanguages;
1.356     albertel 2980:     foreach my $lang (@languages) {
                   2981: 	unless ($lang=~/\w/) { next; }
1.583     albertel 2982: 	push(@genlanguages,$lang);
1.356     albertel 2983: 	if ($lang=~/(\-|\_)/) {
                   2984: 	    push(@genlanguages,(split(/(\-|\_)/,$lang))[0]);
1.118     www      2985: 	}
                   2986:     }
1.583     albertel 2987:     #uniqueify the languages list
                   2988:     my %count;
                   2989:     @genlanguages = map { $count{$_}++ == 0 ? $_ : () } @genlanguages;
1.118     www      2990:     return @genlanguages;
1.117     www      2991: }
                   2992: 
1.582     albertel 2993: sub languages {
                   2994:     my ($possible_langs) = @_;
                   2995:     my @preferred_langs = &preferred_languages();
                   2996:     if (!ref($possible_langs)) {
                   2997: 	if( wantarray ) {
                   2998: 	    return @preferred_langs;
                   2999: 	} else {
                   3000: 	    return $preferred_langs[0];
                   3001: 	}
                   3002:     }
                   3003:     my %possibilities = map { $_ => 1 } (@$possible_langs);
                   3004:     my @preferred_possibilities;
                   3005:     foreach my $preferred_lang (@preferred_langs) {
                   3006: 	if (exists($possibilities{$preferred_lang})) {
                   3007: 	    push(@preferred_possibilities, $preferred_lang);
                   3008: 	}
                   3009:     }
                   3010:     if( wantarray ) {
                   3011: 	return @preferred_possibilities;
                   3012:     }
                   3013:     return $preferred_possibilities[0];
                   3014: }
                   3015: 
1.112     bowersj2 3016: ###############################################################
                   3017: ##               Student Answer Attempts                     ##
                   3018: ###############################################################
                   3019: 
                   3020: =pod
                   3021: 
                   3022: =head1 Alternate Problem Views
                   3023: 
                   3024: =over 4
                   3025: 
1.648     raeburn  3026: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.112     bowersj2 3027:     $getattempt, $regexp, $gradesub)
                   3028: 
                   3029: Return string with previous attempt on problem. Arguments:
                   3030: 
                   3031: =over 4
                   3032: 
                   3033: =item * $symb: Problem, including path
                   3034: 
                   3035: =item * $username: username of the desired student
                   3036: 
                   3037: =item * $domain: domain of the desired student
1.14      harris41 3038: 
1.112     bowersj2 3039: =item * $course: Course ID
1.14      harris41 3040: 
1.112     bowersj2 3041: =item * $getattempt: Leave blank for all attempts, otherwise put
                   3042:     something
1.14      harris41 3043: 
1.112     bowersj2 3044: =item * $regexp: if string matches this regexp, the string will be
                   3045:     sent to $gradesub
1.14      harris41 3046: 
1.112     bowersj2 3047: =item * $gradesub: routine that processes the string if it matches $regexp
1.14      harris41 3048: 
1.112     bowersj2 3049: =back
1.14      harris41 3050: 
1.112     bowersj2 3051: The output string is a table containing all desired attempts, if any.
1.16      harris41 3052: 
1.112     bowersj2 3053: =cut
1.1       albertel 3054: 
                   3055: sub get_previous_attempt {
1.43      ng       3056:   my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub)=@_;
1.1       albertel 3057:   my $prevattempts='';
1.43      ng       3058:   no strict 'refs';
1.1       albertel 3059:   if ($symb) {
1.3       albertel 3060:     my (%returnhash)=
                   3061:       &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1       albertel 3062:     if ($returnhash{'version'}) {
                   3063:       my %lasthash=();
                   3064:       my $version;
                   3065:       for ($version=1;$version<=$returnhash{'version'};$version++) {
1.356     albertel 3066:         foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
                   3067: 	  $lasthash{$key}=$returnhash{$version.':'.$key};
1.19      harris41 3068:         }
1.1       albertel 3069:       }
1.596     albertel 3070:       $prevattempts=&start_data_table().&start_data_table_header_row();
                   3071:       $prevattempts.='<th>'.&mt('History').'</th>';
1.356     albertel 3072:       foreach my $key (sort(keys(%lasthash))) {
                   3073: 	my ($ign,@parts) = split(/\./,$key);
1.41      ng       3074: 	if ($#parts > 0) {
1.31      albertel 3075: 	  my $data=$parts[-1];
                   3076: 	  pop(@parts);
1.596     albertel 3077: 	  $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.'&nbsp;</th>';
1.31      albertel 3078: 	} else {
1.41      ng       3079: 	  if ($#parts == 0) {
                   3080: 	    $prevattempts.='<th>'.$parts[0].'</th>';
                   3081: 	  } else {
                   3082: 	    $prevattempts.='<th>'.$ign.'</th>';
                   3083: 	  }
1.31      albertel 3084: 	}
1.16      harris41 3085:       }
1.596     albertel 3086:       $prevattempts.=&end_data_table_header_row();
1.40      ng       3087:       if ($getattempt eq '') {
                   3088: 	for ($version=1;$version<=$returnhash{'version'};$version++) {
1.596     albertel 3089: 	  $prevattempts.=&start_data_table_row().
                   3090: 	      '<td>'.&mt('Transaction [_1]',$version).'</td>';
1.356     albertel 3091: 	    foreach my $key (sort(keys(%lasthash))) {
1.581     albertel 3092: 		my $value = &format_previous_attempt_value($key,
                   3093: 							   $returnhash{$version.':'.$key});
                   3094: 		$prevattempts.='<td>'.$value.'&nbsp;</td>';   
1.40      ng       3095: 	    }
1.596     albertel 3096: 	  $prevattempts.=&end_data_table_row();
1.40      ng       3097: 	 }
1.1       albertel 3098:       }
1.596     albertel 3099:       $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356     albertel 3100:       foreach my $key (sort(keys(%lasthash))) {
1.581     albertel 3101: 	my $value = &format_previous_attempt_value($key,$lasthash{$key});
1.356     albertel 3102: 	if ($key =~/$regexp$/ && (defined &$gradesub)) {$value = &$gradesub($value)}
1.40      ng       3103: 	$prevattempts.='<td>'.$value.'&nbsp;</td>';
1.16      harris41 3104:       }
1.596     albertel 3105:       $prevattempts.= &end_data_table_row().&end_data_table();
1.1       albertel 3106:     } else {
1.596     albertel 3107:       $prevattempts=
                   3108: 	  &start_data_table().&start_data_table_row().
                   3109: 	  '<td>'.&mt('Nothing submitted - no attempts.').'</td>'.
                   3110: 	  &end_data_table_row().&end_data_table();
1.1       albertel 3111:     }
                   3112:   } else {
1.596     albertel 3113:     $prevattempts=
                   3114: 	  &start_data_table().&start_data_table_row().
                   3115: 	  '<td>'.&mt('No data.').'</td>'.
                   3116: 	  &end_data_table_row().&end_data_table();
1.1       albertel 3117:   }
1.10      albertel 3118: }
                   3119: 
1.581     albertel 3120: sub format_previous_attempt_value {
                   3121:     my ($key,$value) = @_;
                   3122:     if ($key =~ /timestamp/) {
                   3123: 	$value = &Apache::lonlocal::locallocaltime($value);
                   3124:     } elsif (ref($value) eq 'ARRAY') {
                   3125: 	$value = '('.join(', ', @{ $value }).')';
                   3126:     } else {
                   3127: 	$value = &unescape($value);
                   3128:     }
                   3129:     return $value;
                   3130: }
                   3131: 
                   3132: 
1.107     albertel 3133: sub relative_to_absolute {
                   3134:     my ($url,$output)=@_;
                   3135:     my $parser=HTML::TokeParser->new(\$output);
                   3136:     my $token;
                   3137:     my $thisdir=$url;
                   3138:     my @rlinks=();
                   3139:     while ($token=$parser->get_token) {
                   3140: 	if ($token->[0] eq 'S') {
                   3141: 	    if ($token->[1] eq 'a') {
                   3142: 		if ($token->[2]->{'href'}) {
                   3143: 		    $rlinks[$#rlinks+1]=$token->[2]->{'href'};
                   3144: 		}
                   3145: 	    } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
                   3146: 		$rlinks[$#rlinks+1]=$token->[2]->{'src'};
                   3147: 	    } elsif ($token->[1] eq 'base') {
                   3148: 		$thisdir=$token->[2]->{'href'};
                   3149: 	    }
                   3150: 	}
                   3151:     }
                   3152:     $thisdir=~s-/[^/]*$--;
1.356     albertel 3153:     foreach my $link (@rlinks) {
                   3154: 	unless (($link=~/^http:\/\//i) ||
                   3155: 		($link=~/^\//) ||
                   3156: 		($link=~/^javascript:/i) ||
                   3157: 		($link=~/^mailto:/i) ||
                   3158: 		($link=~/^\#/)) {
                   3159: 	    my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
                   3160: 	    $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107     albertel 3161: 	}
                   3162:     }
                   3163: # -------------------------------------------------- Deal with Applet codebases
                   3164:     $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
                   3165:     return $output;
                   3166: }
                   3167: 
1.112     bowersj2 3168: =pod
                   3169: 
1.648     raeburn  3170: =item * &get_student_view()
1.112     bowersj2 3171: 
                   3172: show a snapshot of what student was looking at
                   3173: 
                   3174: =cut
                   3175: 
1.10      albertel 3176: sub get_student_view {
1.186     albertel 3177:   my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114     www      3178:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 3179:   my (%form);
1.10      albertel 3180:   my @elements=('symb','courseid','domain','username');
                   3181:   foreach my $element (@elements) {
1.186     albertel 3182:       $form{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 3183:   }
1.186     albertel 3184:   if (defined($moreenv)) {
                   3185:       %form=(%form,%{$moreenv});
                   3186:   }
1.236     albertel 3187:   if (defined($target)) { $form{'grade_target'} = $target; }
1.107     albertel 3188:   $feedurl=&Apache::lonnet::clutter($feedurl);
1.650     www      3189:   my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11      albertel 3190:   $userview=~s/\<body[^\>]*\>//gi;
                   3191:   $userview=~s/\<\/body\>//gi;
                   3192:   $userview=~s/\<html\>//gi;
                   3193:   $userview=~s/\<\/html\>//gi;
                   3194:   $userview=~s/\<head\>//gi;
                   3195:   $userview=~s/\<\/head\>//gi;
                   3196:   $userview=~s/action\s*\=/would_be_action\=/gi;
1.107     albertel 3197:   $userview=&relative_to_absolute($feedurl,$userview);
1.650     www      3198:   if (wantarray) {
                   3199:      return ($userview,$response);
                   3200:   } else {
                   3201:      return $userview;
                   3202:   }
                   3203: }
                   3204: 
                   3205: sub get_student_view_with_retries {
                   3206:   my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
                   3207: 
                   3208:     my $ok = 0;                 # True if we got a good response.
                   3209:     my $content;
                   3210:     my $response;
                   3211: 
                   3212:     # Try to get the student_view done. within the retries count:
                   3213:     
                   3214:     do {
                   3215:          ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
                   3216:          $ok      = $response->is_success;
                   3217:          if (!$ok) {
                   3218:             &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
                   3219:          }
                   3220:          $retries--;
                   3221:     } while (!$ok && ($retries > 0));
                   3222:     
                   3223:     if (!$ok) {
                   3224:        $content = '';          # On error return an empty content.
                   3225:     }
1.651     www      3226:     if (wantarray) {
                   3227:        return ($content, $response);
                   3228:     } else {
                   3229:        return $content;
                   3230:     }
1.11      albertel 3231: }
                   3232: 
1.112     bowersj2 3233: =pod
                   3234: 
1.648     raeburn  3235: =item * &get_student_answers() 
1.112     bowersj2 3236: 
                   3237: show a snapshot of how student was answering problem
                   3238: 
                   3239: =cut
                   3240: 
1.11      albertel 3241: sub get_student_answers {
1.100     sakharuk 3242:   my ($symb,$username,$domain,$courseid,%form) = @_;
1.114     www      3243:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 3244:   my (%moreenv);
1.11      albertel 3245:   my @elements=('symb','courseid','domain','username');
                   3246:   foreach my $element (@elements) {
1.186     albertel 3247:     $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 3248:   }
1.186     albertel 3249:   $moreenv{'grade_target'}='answer';
                   3250:   %moreenv=(%form,%moreenv);
1.497     raeburn  3251:   $feedurl = &Apache::lonnet::clutter($feedurl);
                   3252:   my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10      albertel 3253:   return $userview;
1.1       albertel 3254: }
1.116     albertel 3255: 
                   3256: =pod
                   3257: 
                   3258: =item * &submlink()
                   3259: 
1.242     albertel 3260: Inputs: $text $uname $udom $symb $target
1.116     albertel 3261: 
                   3262: Returns: A link to grades.pm such as to see the SUBM view of a student
                   3263: 
                   3264: =cut
                   3265: 
                   3266: ###############################################
                   3267: sub submlink {
1.242     albertel 3268:     my ($text,$uname,$udom,$symb,$target)=@_;
1.116     albertel 3269:     if (!($uname && $udom)) {
                   3270: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 3271: 	    &Apache::lonnet::whichuser($symb);
1.116     albertel 3272: 	if (!$symb) { $symb=$cursymb; }
                   3273:     }
1.254     matthew  3274:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      3275:     $symb=&escape($symb);
1.242     albertel 3276:     if ($target) { $target="target=\"$target\""; }
                   3277:     return '<a href="/adm/grades?&command=submission&'.
                   3278: 	'symb='.$symb.'&student='.$uname.
                   3279: 	'&userdom='.$udom.'" '.$target.'>'.$text.'</a>';
                   3280: }
                   3281: ##############################################
                   3282: 
                   3283: =pod
                   3284: 
                   3285: =item * &pgrdlink()
                   3286: 
                   3287: Inputs: $text $uname $udom $symb $target
                   3288: 
                   3289: Returns: A link to grades.pm such as to see the PGRD view of a student
                   3290: 
                   3291: =cut
                   3292: 
                   3293: ###############################################
                   3294: sub pgrdlink {
                   3295:     my $link=&submlink(@_);
                   3296:     $link=~s/(&command=submission)/$1&showgrading=yes/;
                   3297:     return $link;
                   3298: }
                   3299: ##############################################
                   3300: 
                   3301: =pod
                   3302: 
                   3303: =item * &pprmlink()
                   3304: 
                   3305: Inputs: $text $uname $udom $symb $target
                   3306: 
                   3307: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283     albertel 3308: student and a specific resource
1.242     albertel 3309: 
                   3310: =cut
                   3311: 
                   3312: ###############################################
                   3313: sub pprmlink {
                   3314:     my ($text,$uname,$udom,$symb,$target)=@_;
                   3315:     if (!($uname && $udom)) {
                   3316: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 3317: 	    &Apache::lonnet::whichuser($symb);
1.242     albertel 3318: 	if (!$symb) { $symb=$cursymb; }
                   3319:     }
1.254     matthew  3320:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      3321:     $symb=&escape($symb);
1.242     albertel 3322:     if ($target) { $target="target=\"$target\""; }
1.595     albertel 3323:     return '<a href="/adm/parmset?command=set&amp;'.
                   3324: 	'symb='.$symb.'&amp;uname='.$uname.
                   3325: 	'&amp;udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116     albertel 3326: }
                   3327: ##############################################
1.37      matthew  3328: 
1.112     bowersj2 3329: =pod
                   3330: 
                   3331: =back
                   3332: 
                   3333: =cut
                   3334: 
1.37      matthew  3335: ###############################################
1.51      www      3336: 
                   3337: 
                   3338: sub timehash {
                   3339:     my @ltime=localtime(shift);
                   3340:     return ( 'seconds' => $ltime[0],
                   3341:              'minutes' => $ltime[1],
                   3342:              'hours'   => $ltime[2],
                   3343:              'day'     => $ltime[3],
                   3344:              'month'   => $ltime[4]+1,
                   3345:              'year'    => $ltime[5]+1900,
                   3346:              'weekday' => $ltime[6],
                   3347:              'dayyear' => $ltime[7]+1,
                   3348:              'dlsav'   => $ltime[8] );
                   3349: }
                   3350: 
1.370     www      3351: sub utc_string {
                   3352:     my ($date)=@_;
1.371     www      3353:     return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370     www      3354: }
                   3355: 
1.51      www      3356: sub maketime {
                   3357:     my %th=@_;
                   3358:     return POSIX::mktime(
                   3359:         ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210     www      3360:          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70      www      3361: }
                   3362: 
                   3363: #########################################
1.51      www      3364: 
                   3365: sub findallcourses {
1.482     raeburn  3366:     my ($roles,$uname,$udom) = @_;
1.355     albertel 3367:     my %roles;
                   3368:     if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348     albertel 3369:     my %courses;
1.51      www      3370:     my $now=time;
1.482     raeburn  3371:     if (!defined($uname)) {
                   3372:         $uname = $env{'user.name'};
                   3373:     }
                   3374:     if (!defined($udom)) {
                   3375:         $udom = $env{'user.domain'};
                   3376:     }
                   3377:     if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
                   3378:         my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
                   3379:         if (!%roles) {
                   3380:             %roles = (
                   3381:                        cc => 1,
                   3382:                        in => 1,
                   3383:                        ep => 1,
                   3384:                        ta => 1,
                   3385:                        cr => 1,
                   3386:                        st => 1,
                   3387:              );
                   3388:         }
                   3389:         foreach my $entry (keys(%roleshash)) {
                   3390:             my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
                   3391:             if ($trole =~ /^cr/) { 
                   3392:                 next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
                   3393:             } else {
                   3394:                 next if (!exists($roles{$trole}));
                   3395:             }
                   3396:             if ($tend) {
                   3397:                 next if ($tend < $now);
                   3398:             }
                   3399:             if ($tstart) {
                   3400:                 next if ($tstart > $now);
                   3401:             }
                   3402:             my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role,$realsec);
                   3403:             (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
                   3404:             if ($secpart eq '') {
                   3405:                 ($cnum,$role) = split(/_/,$cnumpart); 
                   3406:                 $sec = 'none';
                   3407:                 $realsec = '';
                   3408:             } else {
                   3409:                 $cnum = $cnumpart;
                   3410:                 ($sec,$role) = split(/_/,$secpart);
                   3411:                 $realsec = $sec;
1.490     raeburn  3412:             }
1.482     raeburn  3413:             $courses{$cdom.'_'.$cnum}{$sec} = $trole.'/'.$cdom.'/'.$cnum.'/'.$realsec;
                   3414:         }
                   3415:     } else {
                   3416:         foreach my $key (keys(%env)) {
1.483     albertel 3417: 	    if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
                   3418:                  $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482     raeburn  3419: 	        my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
                   3420: 	        next if ($role eq 'ca' || $role eq 'aa');
                   3421: 	        next if (%roles && !exists($roles{$role}));
                   3422: 	        my ($starttime,$endtime)=split(/\./,$env{$key});
                   3423:                 my $active=1;
                   3424:                 if ($starttime) {
                   3425: 		    if ($now<$starttime) { $active=0; }
                   3426:                 }
                   3427:                 if ($endtime) {
                   3428:                     if ($now>$endtime) { $active=0; }
                   3429:                 }
                   3430:                 if ($active) {
                   3431:                     if ($sec eq '') {
                   3432:                         $sec = 'none';
                   3433:                     }
                   3434:                     $courses{$cdom.'_'.$cnum}{$sec} = 
                   3435:                                      $role.'/'.$cdom.'/'.$cnum.'/'.$sec;
1.474     raeburn  3436:                 }
                   3437:             }
1.51      www      3438:         }
                   3439:     }
1.474     raeburn  3440:     return %courses;
1.51      www      3441: }
1.37      matthew  3442: 
1.54      www      3443: ###############################################
1.474     raeburn  3444: 
                   3445: sub blockcheck {
1.482     raeburn  3446:     my ($setters,$activity,$uname,$udom) = @_;
1.490     raeburn  3447: 
                   3448:     if (!defined($udom)) {
                   3449:         $udom = $env{'user.domain'};
                   3450:     }
                   3451:     if (!defined($uname)) {
                   3452:         $uname = $env{'user.name'};
                   3453:     }
                   3454: 
                   3455:     # If uname and udom are for a course, check for blocks in the course.
                   3456: 
                   3457:     if (&Apache::lonnet::is_course($udom,$uname)) {
                   3458:         my %records = &Apache::lonnet::dump('comm_block',$udom,$uname);
1.502     raeburn  3459:         my ($startblock,$endblock)=&get_blocks($setters,$activity,$udom,$uname);
1.490     raeburn  3460:         return ($startblock,$endblock);
                   3461:     }
1.474     raeburn  3462: 
1.502     raeburn  3463:     my $startblock = 0;
                   3464:     my $endblock = 0;
1.482     raeburn  3465:     my %live_courses = &findallcourses(undef,$uname,$udom);
1.474     raeburn  3466: 
1.490     raeburn  3467:     # If uname is for a user, and activity is course-specific, i.e.,
                   3468:     # boards, chat or groups, check for blocking in current course only.
1.474     raeburn  3469: 
1.490     raeburn  3470:     if (($activity eq 'boards' || $activity eq 'chat' ||
                   3471:          $activity eq 'groups') && ($env{'request.course.id'})) {
                   3472:         foreach my $key (keys(%live_courses)) {
                   3473:             if ($key ne $env{'request.course.id'}) {
                   3474:                 delete($live_courses{$key});
                   3475:             }
                   3476:         }
                   3477:     }
                   3478: 
                   3479:     my $otheruser = 0;
                   3480:     my %own_courses;
                   3481:     if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
                   3482:         # Resource belongs to user other than current user.
                   3483:         $otheruser = 1;
                   3484:         # Gather courses for current user
                   3485:         %own_courses = 
                   3486:             &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
                   3487:     }
                   3488: 
                   3489:     # Gather active course roles - course coordinator, instructor, 
                   3490:     # exam proctor, ta, student, or custom role.
1.474     raeburn  3491: 
                   3492:     foreach my $course (keys(%live_courses)) {
1.482     raeburn  3493:         my ($cdom,$cnum);
                   3494:         if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
                   3495:             $cdom = $env{'course.'.$course.'.domain'};
                   3496:             $cnum = $env{'course.'.$course.'.num'};
                   3497:         } else {
1.490     raeburn  3498:             ($cdom,$cnum) = split(/_/,$course); 
1.482     raeburn  3499:         }
                   3500:         my $no_ownblock = 0;
                   3501:         my $no_userblock = 0;
1.533     raeburn  3502:         if ($otheruser && $activity ne 'com') {
1.490     raeburn  3503:             # Check if current user has 'evb' priv for this
                   3504:             if (defined($own_courses{$course})) {
                   3505:                 foreach my $sec (keys(%{$own_courses{$course}})) {
                   3506:                     my $checkrole = 'cm./'.$cdom.'/'.$cnum;
                   3507:                     if ($sec ne 'none') {
                   3508:                         $checkrole .= '/'.$sec;
                   3509:                     }
                   3510:                     if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   3511:                         $no_ownblock = 1;
                   3512:                         last;
                   3513:                     }
                   3514:                 }
                   3515:             }
                   3516:             # if they have 'evb' priv and are currently not playing student
                   3517:             next if (($no_ownblock) &&
                   3518:                  ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
                   3519:         }
1.474     raeburn  3520:         foreach my $sec (keys(%{$live_courses{$course}})) {
1.482     raeburn  3521:             my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474     raeburn  3522:             if ($sec ne 'none') {
1.482     raeburn  3523:                 $checkrole .= '/'.$sec;
1.474     raeburn  3524:             }
1.490     raeburn  3525:             if ($otheruser) {
                   3526:                 # Resource belongs to user other than current user.
                   3527:                 # Assemble privs for that user, and check for 'evb' priv.
1.482     raeburn  3528:                 my ($trole,$tdom,$tnum,$tsec);
                   3529:                 my $entry = $live_courses{$course}{$sec};
                   3530:                 if ($entry =~ /^cr/) {
                   3531:                     ($trole,$tdom,$tnum,$tsec) = 
                   3532:                       ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
                   3533:                 } else {
                   3534:                     ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
                   3535:                 }
                   3536:                 my ($spec,$area,$trest,%allroles,%userroles);
                   3537:                 $area = '/'.$tdom.'/'.$tnum;
                   3538:                 $trest = $tnum;
                   3539:                 if ($tsec ne '') {
                   3540:                     $area .= '/'.$tsec;
                   3541:                     $trest .= '/'.$tsec;
                   3542:                 }
                   3543:                 $spec = $trole.'.'.$area;
                   3544:                 if ($trole =~ /^cr/) {
                   3545:                     &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
                   3546:                                                       $tdom,$spec,$trest,$area);
                   3547:                 } else {
                   3548:                     &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
                   3549:                                                        $tdom,$spec,$trest,$area);
                   3550:                 }
                   3551:                 my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
1.486     raeburn  3552:                 if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
                   3553:                     if ($1) {
                   3554:                         $no_userblock = 1;
                   3555:                         last;
                   3556:                     }
                   3557:                 }
1.490     raeburn  3558:             } else {
                   3559:                 # Resource belongs to current user
                   3560:                 # Check for 'evb' priv via lonnet::allowed().
1.482     raeburn  3561:                 if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   3562:                     $no_ownblock = 1;
                   3563:                     last;
                   3564:                 }
1.474     raeburn  3565:             }
                   3566:         }
                   3567:         # if they have the evb priv and are currently not playing student
1.482     raeburn  3568:         next if (($no_ownblock) &&
1.491     albertel 3569:                  ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482     raeburn  3570:         next if ($no_userblock);
1.474     raeburn  3571: 
1.490     raeburn  3572:         # Retrieve blocking times and identity of blocker for course
                   3573:         # of specified user, unless user has 'evb' privilege.
1.502     raeburn  3574:         
                   3575:         my ($start,$end)=&get_blocks($setters,$activity,$cdom,$cnum);
                   3576:         if (($start != 0) && 
                   3577:             (($startblock == 0) || ($startblock > $start))) {
                   3578:             $startblock = $start;
                   3579:         }
                   3580:         if (($end != 0)  &&
                   3581:             (($endblock == 0) || ($endblock < $end))) {
                   3582:             $endblock = $end;
                   3583:         }
1.490     raeburn  3584:     }
                   3585:     return ($startblock,$endblock);
                   3586: }
                   3587: 
                   3588: sub get_blocks {
                   3589:     my ($setters,$activity,$cdom,$cnum) = @_;
                   3590:     my $startblock = 0;
                   3591:     my $endblock = 0;
                   3592:     my $course = $cdom.'_'.$cnum;
                   3593:     $setters->{$course} = {};
                   3594:     $setters->{$course}{'staff'} = [];
                   3595:     $setters->{$course}{'times'} = [];
                   3596:     my %records = &Apache::lonnet::dump('comm_block',$cdom,$cnum);
                   3597:     foreach my $record (keys(%records)) {
                   3598:         my ($start,$end) = ($record =~ m/^(\d+)____(\d+)$/);
                   3599:         if ($start <= time && $end >= time) {
                   3600:             my ($staff_name,$staff_dom,$title,$blocks) =
                   3601:                 &parse_block_record($records{$record});
                   3602:             if ($blocks->{$activity} eq 'on') {
                   3603:                 push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
                   3604:                 push(@{$$setters{$course}{'times'}}, [$start,$end]);
1.491     albertel 3605:                 if ( ($startblock == 0) || ($startblock > $start) ) {
                   3606:                     $startblock = $start;
1.490     raeburn  3607:                 }
1.491     albertel 3608:                 if ( ($endblock == 0) || ($endblock < $end) ) {
                   3609:                     $endblock = $end;
1.474     raeburn  3610:                 }
                   3611:             }
                   3612:         }
                   3613:     }
                   3614:     return ($startblock,$endblock);
                   3615: }
                   3616: 
                   3617: sub parse_block_record {
                   3618:     my ($record) = @_;
                   3619:     my ($setuname,$setudom,$title,$blocks);
                   3620:     if (ref($record) eq 'HASH') {
                   3621:         ($setuname,$setudom) = split(/:/,$record->{'setter'});
                   3622:         $title = &unescape($record->{'event'});
                   3623:         $blocks = $record->{'blocks'};
                   3624:     } else {
                   3625:         my @data = split(/:/,$record,3);
                   3626:         if (scalar(@data) eq 2) {
                   3627:             $title = $data[1];
                   3628:             ($setuname,$setudom) = split(/@/,$data[0]);
                   3629:         } else {
                   3630:             ($setuname,$setudom,$title) = @data;
                   3631:         }
                   3632:         $blocks = { 'com' => 'on' };
                   3633:     }
                   3634:     return ($setuname,$setudom,$title,$blocks);
                   3635: }
                   3636: 
                   3637: sub build_block_table {
                   3638:     my ($startblock,$endblock,$setters) = @_;
                   3639:     my %lt = &Apache::lonlocal::texthash(
                   3640:         'cacb' => 'Currently active communication blocks',
                   3641:         'cour' => 'Course',
                   3642:         'dura' => 'Duration',
                   3643:         'blse' => 'Block set by'
                   3644:     );
                   3645:     my $output;
1.476     raeburn  3646:     $output = '<br />'.$lt{'cacb'}.':<br />';
1.474     raeburn  3647:     $output .= &start_data_table();
                   3648:     $output .= '
                   3649: <tr>
                   3650:  <th>'.$lt{'cour'}.'</th>
                   3651:  <th>'.$lt{'dura'}.'</th>
                   3652:  <th>'.$lt{'blse'}.'</th>
                   3653: </tr>
                   3654: ';
                   3655:     foreach my $course (keys(%{$setters})) {
                   3656:         my %courseinfo=&Apache::lonnet::coursedescription($course);
                   3657:         for (my $i=0; $i<@{$$setters{$course}{staff}}; $i++) {
                   3658:             my ($uname,$udom) = @{$$setters{$course}{staff}[$i]};
1.490     raeburn  3659:             my $fullname = &plainname($uname,$udom);
                   3660:             if (defined($env{'user.name'}) && defined($env{'user.domain'})
                   3661:                 && $env{'user.name'} ne 'public' 
                   3662:                 && $env{'user.domain'} ne 'public') {
                   3663:                 $fullname = &aboutmewrapper($fullname,$uname,$udom);
                   3664:             }
1.474     raeburn  3665:             my ($openblock,$closeblock) = @{$$setters{$course}{times}[$i]};
                   3666:             $openblock = &Apache::lonlocal::locallocaltime($openblock);
                   3667:             $closeblock= &Apache::lonlocal::locallocaltime($closeblock);
                   3668:             $output .= &Apache::loncommon::start_data_table_row().
                   3669:                        '<td>'.$courseinfo{'description'}.'</td>'.
                   3670:                        '<td>'.$openblock.' to '.$closeblock.'</td>'.
1.490     raeburn  3671:                        '<td>'.$fullname.'</td>'.
1.474     raeburn  3672:                         &Apache::loncommon::end_data_table_row();
                   3673:         }
                   3674:     }
                   3675:     $output .= &end_data_table();
                   3676: }
                   3677: 
1.490     raeburn  3678: sub blocking_status {
                   3679:     my ($activity,$uname,$udom) = @_;
                   3680:     my %setters;
                   3681:     my ($blocked,$output,$ownitem,$is_course);
                   3682:     my ($startblock,$endblock)=&blockcheck(\%setters,$activity,$uname,$udom);
                   3683:     if ($startblock && $endblock) {
                   3684:         $blocked = 1;
                   3685:         if (wantarray) {
                   3686:             my $category;
                   3687:             if ($activity eq 'boards') {
                   3688:                 $category = 'Discussion posts in this course';
                   3689:             } elsif ($activity eq 'blogs') {
                   3690:                 $category = 'Blogs';
                   3691:             } elsif ($activity eq 'port') {
                   3692:                 if (defined($uname) && defined($udom)) {
                   3693:                     if ($uname eq $env{'user.name'} &&
                   3694:                         $udom eq $env{'user.domain'}) {
                   3695:                         $ownitem = 1;
                   3696:                     }
                   3697:                 }
                   3698:                 $is_course = &Apache::lonnet::is_course($udom,$uname);
                   3699:                 if ($ownitem) { 
                   3700:                     $category = 'Your portfolio files';  
                   3701:                 } elsif ($is_course) {
                   3702:                     my $coursedesc;
                   3703:                     foreach my $course (keys(%setters)) {
                   3704:                         my %courseinfo =
                   3705:                              &Apache::lonnet::coursedescription($course);
                   3706:                         $coursedesc = $courseinfo{'description'};
                   3707:                     }
                   3708:                     $category = "Group files in the course '$coursedesc'";
                   3709:                 } else {
                   3710:                     $category = 'Portfolio files belonging to ';
                   3711:                     if ($env{'user.name'} eq 'public' && 
                   3712:                         $env{'user.domain'} eq 'public') {
                   3713:                         $category .= &plainname($uname,$udom);
                   3714:                     } else {
                   3715:                         $category .= &aboutmewrapper(&plainname($uname,$udom),$uname,$udom);  
                   3716:                     }
                   3717:                 }
                   3718:             } elsif ($activity eq 'groups') {
                   3719:                 $category = 'Groups in this course';
                   3720:             }
                   3721:             my $showstart = &Apache::lonlocal::locallocaltime($startblock);
                   3722:             my $showend = &Apache::lonlocal::locallocaltime($endblock);
                   3723:             $output = '<br />'.&mt('[_1] will be inaccessible between [_2] and [_3] because communication is being blocked.',$category,$showstart,$showend).'<br />';
                   3724:             if (!($activity eq 'port' && !($ownitem) && !($is_course))) { 
                   3725:                 $output .= &build_block_table($startblock,$endblock,\%setters);
                   3726:             }
                   3727:         }
                   3728:     }
                   3729:     if (wantarray) {
                   3730:         return ($blocked,$output);
                   3731:     } else {
                   3732:         return $blocked;
                   3733:     }
                   3734: }
                   3735: 
1.60      matthew  3736: ###############################################
                   3737: 
                   3738: =pod
                   3739: 
1.112     bowersj2 3740: =head1 Domain Template Functions
                   3741: 
                   3742: =over 4
                   3743: 
                   3744: =item * &determinedomain()
1.60      matthew  3745: 
                   3746: Inputs: $domain (usually will be undef)
                   3747: 
1.63      www      3748: Returns: Determines which domain should be used for designs
1.60      matthew  3749: 
                   3750: =cut
1.54      www      3751: 
1.60      matthew  3752: ###############################################
1.63      www      3753: sub determinedomain {
                   3754:     my $domain=shift;
1.531     albertel 3755:     if (! $domain) {
1.60      matthew  3756:         # Determine domain if we have not been given one
                   3757:         $domain = $Apache::lonnet::perlvar{'lonDefDomain'};
1.258     albertel 3758:         if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
                   3759:         if ($env{'request.role.domain'}) { 
                   3760:             $domain=$env{'request.role.domain'}; 
1.60      matthew  3761:         }
                   3762:     }
1.63      www      3763:     return $domain;
                   3764: }
                   3765: ###############################################
1.517     raeburn  3766: 
1.518     albertel 3767: sub devalidate_domconfig_cache {
                   3768:     my ($udom)=@_;
                   3769:     &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
                   3770: }
                   3771: 
                   3772: # ---------------------- Get domain configuration for a domain
                   3773: sub get_domainconf {
                   3774:     my ($udom) = @_;
                   3775:     my $cachetime=1800;
                   3776:     my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
                   3777:     if (defined($cached)) { return %{$result}; }
                   3778: 
                   3779:     my %domconfig = &Apache::lonnet::get_dom('configuration',
                   3780: 					     ['login','rolecolors'],$udom);
1.632     raeburn  3781:     my (%designhash,%legacy);
1.518     albertel 3782:     if (keys(%domconfig) > 0) {
                   3783:         if (ref($domconfig{'login'}) eq 'HASH') {
1.632     raeburn  3784:             if (keys(%{$domconfig{'login'}})) {
                   3785:                 foreach my $key (keys(%{$domconfig{'login'}})) {
                   3786:                     $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
                   3787:                 }
                   3788:             } else {
                   3789:                 $legacy{'login'} = 1;
1.518     albertel 3790:             }
1.632     raeburn  3791:         } else {
                   3792:             $legacy{'login'} = 1;
1.518     albertel 3793:         }
                   3794:         if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632     raeburn  3795:             if (keys(%{$domconfig{'rolecolors'}})) {
                   3796:                 foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
                   3797:                     if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
                   3798:                         foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
                   3799:                             $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
                   3800:                         }
1.518     albertel 3801:                     }
                   3802:                 }
1.632     raeburn  3803:             } else {
                   3804:                 $legacy{'rolecolors'} = 1;
1.518     albertel 3805:             }
1.632     raeburn  3806:         } else {
                   3807:             $legacy{'rolecolors'} = 1;
1.518     albertel 3808:         }
1.632     raeburn  3809:         if (keys(%legacy) > 0) {
                   3810:             my %legacyhash = &get_legacy_domconf($udom);
                   3811:             foreach my $item (keys(%legacyhash)) {
                   3812:                 if ($item =~ /^\Q$udom\E\.login/) {
                   3813:                     if ($legacy{'login'}) { 
                   3814:                         $designhash{$item} = $legacyhash{$item};
                   3815:                     }
                   3816:                 } else {
                   3817:                     if ($legacy{'rolecolors'}) {
                   3818:                         $designhash{$item} = $legacyhash{$item};
                   3819:                     }
1.518     albertel 3820:                 }
                   3821:             }
                   3822:         }
1.632     raeburn  3823:     } else {
                   3824:         %designhash = &get_legacy_domconf($udom); 
1.518     albertel 3825:     }
                   3826:     &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
                   3827: 				  $cachetime);
                   3828:     return %designhash;
                   3829: }
                   3830: 
1.632     raeburn  3831: sub get_legacy_domconf {
                   3832:     my ($udom) = @_;
                   3833:     my %legacyhash;
                   3834:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
                   3835:     my $designfile =  $designdir.'/'.$udom.'.tab';
                   3836:     if (-e $designfile) {
                   3837:         if ( open (my $fh,"<$designfile") ) {
                   3838:             while (my $line = <$fh>) {
                   3839:                 next if ($line =~ /^\#/);
                   3840:                 chomp($line);
                   3841:                 my ($key,$val)=(split(/\=/,$line));
                   3842:                 if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
                   3843:             }
                   3844:             close($fh);
                   3845:         }
                   3846:     }
                   3847:     if (-e '/home/httpd/html/adm/lonDomLogos/'.$udom.'.gif') {
                   3848:         $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
                   3849:     }
                   3850:     return %legacyhash;
                   3851: }
                   3852: 
1.63      www      3853: =pod
                   3854: 
1.112     bowersj2 3855: =item * &domainlogo()
1.63      www      3856: 
                   3857: Inputs: $domain (usually will be undef)
                   3858: 
                   3859: Returns: A link to a domain logo, if the domain logo exists.
                   3860: If the domain logo does not exist, a description of the domain.
                   3861: 
                   3862: =cut
1.112     bowersj2 3863: 
1.63      www      3864: ###############################################
                   3865: sub domainlogo {
1.517     raeburn  3866:     my $domain = &determinedomain(shift);
1.518     albertel 3867:     my %designhash = &get_domainconf($domain);    
1.517     raeburn  3868:     # See if there is a logo
                   3869:     if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519     raeburn  3870:         my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538     albertel 3871:         if ($imgsrc =~ m{^/(adm|res)/}) {
                   3872: 	    if ($imgsrc =~ m{^/res/}) {
                   3873: 		my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
                   3874: 		&Apache::lonnet::repcopy($local_name);
                   3875: 	    }
                   3876: 	   $imgsrc = &lonhttpdurl($imgsrc);
1.519     raeburn  3877:         } 
                   3878:         return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514     albertel 3879:     } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
                   3880:         return &Apache::lonnet::domain($domain,'description');
1.59      www      3881:     } else {
1.60      matthew  3882:         return '';
1.59      www      3883:     }
                   3884: }
1.63      www      3885: ##############################################
                   3886: 
                   3887: =pod
                   3888: 
1.112     bowersj2 3889: =item * &designparm()
1.63      www      3890: 
                   3891: Inputs: $which parameter; $domain (usually will be undef)
                   3892: 
                   3893: Returns: value of designparamter $which
                   3894: 
                   3895: =cut
1.112     bowersj2 3896: 
1.397     albertel 3897: 
1.400     albertel 3898: ##############################################
1.397     albertel 3899: sub designparm {
                   3900:     my ($which,$domain)=@_;
1.258     albertel 3901:     if ($env{'browser.blackwhite'} eq 'on') {
1.635     raeburn  3902: 	if ($which=~/\.(font|alink|vlink|link|textcol)$/) {
1.110     www      3903: 	    return '#000000';
                   3904: 	}
1.635     raeburn  3905: 	if ($which=~/\.(pgbg|sidebg|bgcol)$/) {
1.110     www      3906: 	    return '#FFFFFF';
                   3907: 	}
                   3908: 	if ($which=~/\.tabbg$/) {
                   3909: 	    return '#CCCCCC';
                   3910: 	}
                   3911:     }
1.397     albertel 3912:     if (exists($env{'environment.color.'.$which})) {
1.258     albertel 3913: 	return $env{'environment.color.'.$which};
1.96      www      3914:     }
1.63      www      3915:     $domain=&determinedomain($domain);
1.518     albertel 3916:     my %domdesign = &get_domainconf($domain);
1.520     raeburn  3917:     my $output;
1.517     raeburn  3918:     if ($domdesign{$domain.'.'.$which} ne '') {
1.520     raeburn  3919: 	$output = $domdesign{$domain.'.'.$which};
1.63      www      3920:     } else {
1.520     raeburn  3921:         $output = $defaultdesign{$which};
                   3922:     }
                   3923:     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635     raeburn  3924:         ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538     albertel 3925:         if ($output =~ m{^/(adm|res)/}) {
                   3926: 	    if ($output =~ m{^/res/}) {
                   3927: 		my $local_name = &Apache::lonnet::filelocation('',$output);
                   3928: 		&Apache::lonnet::repcopy($local_name);
                   3929: 	    }
1.520     raeburn  3930:             $output = &lonhttpdurl($output);
                   3931:         }
1.63      www      3932:     }
1.520     raeburn  3933:     return $output;
1.63      www      3934: }
1.59      www      3935: 
1.60      matthew  3936: ###############################################
                   3937: ###############################################
                   3938: 
                   3939: =pod
                   3940: 
1.112     bowersj2 3941: =back
                   3942: 
1.549     albertel 3943: =head1 HTML Helpers
1.112     bowersj2 3944: 
                   3945: =over 4
                   3946: 
                   3947: =item * &bodytag()
1.60      matthew  3948: 
                   3949: Returns a uniform header for LON-CAPA web pages.
                   3950: 
                   3951: Inputs: 
                   3952: 
1.112     bowersj2 3953: =over 4
                   3954: 
                   3955: =item * $title, A title to be displayed on the page.
                   3956: 
                   3957: =item * $function, the current role (can be undef).
                   3958: 
                   3959: =item * $addentries, extra parameters for the <body> tag.
                   3960: 
                   3961: =item * $bodyonly, if defined, only return the <body> tag.
                   3962: 
                   3963: =item * $domain, if defined, force a given domain.
                   3964: 
                   3965: =item * $forcereg, if page should register as content page (relevant for 
1.86      www      3966:             text interface only)
1.60      matthew  3967: 
1.326     albertel 3968: =item * $customtitle, alternate text to use instead of $title
                   3969:                       in the title box that appears, this text
                   3970:                       is not auto translated like the $title is
1.309     albertel 3971: 
                   3972: =item * $notopbar, if true, keep the 'what is this' info but remove the
                   3973:                    navigational links
1.317     albertel 3974: 
1.338     albertel 3975: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
                   3976: 
                   3977: =item * $notitle, if true keep the nav controls, but remove the title bar
                   3978: 
1.361     albertel 3979: =item * $no_inline_link, if true and in remote mode, don't show the 
                   3980:          'Switch To Inline Menu' link
                   3981: 
1.460     albertel 3982: =item * $args, optional argument valid values are
                   3983:             no_auto_mt_title -> prevents &mt()ing the title arg
1.562     albertel 3984:             inherit_jsmath -> when creating popup window in a page,
                   3985:                               should it have jsmath forced on by the
                   3986:                               current page
1.460     albertel 3987: 
1.112     bowersj2 3988: =back
                   3989: 
1.60      matthew  3990: Returns: A uniform header for LON-CAPA web pages.  
                   3991: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
                   3992: If $bodyonly is undef or zero, an html string containing a <body> tag and 
                   3993: other decorations will be returned.
                   3994: 
                   3995: =cut
                   3996: 
1.54      www      3997: sub bodytag {
1.309     albertel 3998:     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,$customtitle,
1.460     albertel 3999: 	$notopbar,$bgcolor,$notitle,$no_inline_link,$args)=@_;
1.339     albertel 4000: 
1.460     albertel 4001:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.339     albertel 4002: 
1.183     matthew  4003:     $function = &get_users_function() if (!$function);
1.339     albertel 4004:     my $img =    &designparm($function.'.img',$domain);
                   4005:     my $font =   &designparm($function.'.font',$domain);
                   4006:     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
                   4007: 
                   4008:     my %design = ( 'style'   => 'margin-top: 0px',
1.535     albertel 4009: 		   'bgcolor' => $pgbg,
1.339     albertel 4010: 		   'text'    => $font,
                   4011:                    'alink'   => &designparm($function.'.alink',$domain),
                   4012: 		   'vlink'   => &designparm($function.'.vlink',$domain),
                   4013: 		   'link'    => &designparm($function.'.link',$domain),);
1.438     albertel 4014:     @design{keys(%$addentries)} = @$addentries{keys(%$addentries)}; 
1.339     albertel 4015: 
1.63      www      4016:  # role and realm
1.378     raeburn  4017:     my ($role,$realm) = split(/\./,$env{'request.role'},2);
                   4018:     if ($role  eq 'ca') {
1.479     albertel 4019:         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500     albertel 4020:         $realm = &plainname($rname,$rdom);
1.378     raeburn  4021:     } 
1.55      www      4022: # realm
1.258     albertel 4023:     if ($env{'request.course.id'}) {
1.378     raeburn  4024:         if ($env{'request.role'} !~ /^cr/) {
                   4025:             $role = &Apache::lonnet::plaintext($role,&course_type());
                   4026:         }
1.359     albertel 4027: 	$realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378     raeburn  4028:     } else {
                   4029:         $role = &Apache::lonnet::plaintext($role);
1.54      www      4030:     }
1.433     albertel 4031: 
1.359     albertel 4032:     if (!$realm) { $realm='&nbsp;'; }
1.55      www      4033: # Set messages
1.60      matthew  4034:     my $messages=&domainlogo($domain);
1.330     albertel 4035: 
1.438     albertel 4036:     my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329     albertel 4037: 
1.101     www      4038: # construct main body tag
1.359     albertel 4039:     my $bodytag = "<body $extra_body_attr>".
1.562     albertel 4040: 	&Apache::lontexconvert::init_math_support($args->{'inherit_jsmath'});
1.252     albertel 4041: 
1.530     albertel 4042:     if ($bodyonly) {
1.60      matthew  4043:         return $bodytag;
1.258     albertel 4044:     } elsif ($env{'browser.interface'} eq 'textual') {
1.95      www      4045: # Accessibility
1.224     raeburn  4046:           
1.337     albertel 4047: 	$bodytag.=&Apache::lonmenu::menubuttons($forcereg,$forcereg);
1.338     albertel 4048: 	if (!$notitle) {
1.337     albertel 4049: 	    $bodytag.='<h1>LON-CAPA: '.$title.'</h1>';
                   4050: 	}
                   4051: 	return $bodytag;
1.359     albertel 4052:     }
                   4053: 
1.410     albertel 4054:     my $name = &plainname($env{'user.name'},$env{'user.domain'});
1.433     albertel 4055:     if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
                   4056: 	undef($role);
1.434     albertel 4057:     } else {
                   4058: 	$name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'});
1.433     albertel 4059:     }
1.359     albertel 4060:     
                   4061:     my $roleinfo=(<<ENDROLE);
                   4062: <td class="LC_title_bar_who">
                   4063: <div class="LC_title_bar_name">
1.410     albertel 4064:     $name
1.361     albertel 4065:     &nbsp;
1.359     albertel 4066: </div>
                   4067: <div class="LC_title_bar_role">
1.361     albertel 4068: $role&nbsp;
1.359     albertel 4069: </div>
                   4070: <div class="LC_title_bar_realm">
1.361     albertel 4071: $realm&nbsp;
1.359     albertel 4072: </div>
1.206     albertel 4073: </td>
                   4074: ENDROLE
1.235     raeburn  4075: 
1.359     albertel 4076:     my $titleinfo = '<span class="LC_title_bar_title">'.$title.'</span>';
                   4077:     if ($customtitle) {
                   4078:         $titleinfo = $customtitle;
                   4079:     }
                   4080:     #
                   4081:     # Extra info if you are the DC
                   4082:     my $dc_info = '';
                   4083:     if ($env{'user.adv'} && exists($env{'user.role.dc./'.
                   4084:                         $env{'course.'.$env{'request.course.id'}.
                   4085:                                  '.domain'}.'/'})) {
                   4086:         my $cid = $env{'request.course.id'};
                   4087:         $dc_info.= $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380     www      4088:         $dc_info =~ s/\s+$//;
1.359     albertel 4089:         $dc_info = '('.$dc_info.')';
                   4090:     }
                   4091: 
1.644     www      4092:     if (($env{'environment.remote'} eq 'off') || ($args->{'suppress_header_logos'})) {
1.359     albertel 4093:         # No Remote
1.258     albertel 4094: 	if ($env{'request.state'} eq 'construct') {
1.359     albertel 4095: 	    $forcereg=1;
                   4096: 	}
                   4097: 
                   4098: 	if (!$customtitle && $env{'request.state'} eq 'construct') {
                   4099: 	    # this is for resources; directories have customtitle, and crumbs
                   4100:             # and select recent are created in lonpubdir.pm  
1.229     albertel 4101: 	    my ($uname,$thisdisfn)=
1.258     albertel 4102: 		($env{'request.filename'} =~ m|^/home/([^/]+)/public_html/(.*)|);
1.229     albertel 4103: 	    my $formaction='/priv/'.$uname.'/'.$thisdisfn;
                   4104: 	    $formaction=~s/\/+/\//g;
                   4105: 
1.359     albertel 4106: 	    my $parentpath = '';
                   4107: 	    my $lastitem = '';
                   4108: 	    if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
                   4109: 		$parentpath = $1;
                   4110: 		$lastitem = $2;
                   4111: 	    } else {
                   4112: 		$lastitem = $thisdisfn;
                   4113: 	    }
                   4114: 	    $titleinfo = 
1.640     bisitz   4115: 		&Apache::loncommon::help_open_menu('','',3,'Authoring')
                   4116: 		.'<b>'.&mt('Construction Space').'</b>:&nbsp;'
                   4117: 		.'<form name="dirs" method="post" action="'.$formaction
1.359     albertel 4118: 		.'" target="_top"><tt><b>'
                   4119: 		.&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."<font size=\"+1\">$lastitem</font></b></tt><br />"
                   4120: 		.&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
                   4121: 		.'</form>'
                   4122: 		.&Apache::lonmenu::constspaceform();
1.235     raeburn  4123:         }
1.359     albertel 4124: 
1.337     albertel 4125:         my $titletable;
1.338     albertel 4126: 	if (!$notitle) {
1.337     albertel 4127: 	    $titletable =
1.359     albertel 4128: 		'<table id="LC_title_bar">'.
                   4129:                          "<tr><td> $titleinfo $dc_info</td>".$roleinfo.
                   4130: 			 '</tr></table>';
1.337     albertel 4131: 	}
1.359     albertel 4132: 	if ($notopbar) {
                   4133: 	    $bodytag .= $titletable;
                   4134: 	} else {
                   4135: 	    if ($env{'request.state'} eq 'construct') {
1.337     albertel 4136:                 $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg,
                   4137: 							  $titletable);
1.272     raeburn  4138:             } else {
1.336     albertel 4139:                 $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg).
1.359     albertel 4140: 		    $titletable;
1.272     raeburn  4141:             }
1.235     raeburn  4142:         }
                   4143:         return $bodytag;
1.94      www      4144:     }
1.95      www      4145: 
1.93      www      4146: #
1.95      www      4147: # Top frame rendering, Remote is up
1.93      www      4148: #
1.359     albertel 4149: 
1.517     raeburn  4150:     my $imgsrc = $img;
                   4151:     if ($img =~ /^\/adm/) {
1.575     albertel 4152:         $imgsrc = &lonhttpdurl($img);
1.517     raeburn  4153:     }
                   4154:     my $upperleft='<img src="'.$imgsrc.'" alt="'.$function.'" />';
1.359     albertel 4155: 
1.305     www      4156:     # Explicit link to get inline menu
1.361     albertel 4157:     my $menu= ($no_inline_link?''
                   4158: 	       :'<br /><a href="/adm/remote?action=collapse">'.&mt('Switch to Inline Menu Mode').'</a>');
1.245     matthew  4159:     #
1.338     albertel 4160:     if ($notitle) {
1.337     albertel 4161: 	return $bodytag;
                   4162:     }
1.94      www      4163:     return(<<ENDBODY);
1.60      matthew  4164: $bodytag
1.359     albertel 4165: <table id="LC_title_bar" class="LC_with_remote">
1.368     albertel 4166: <tr><td class="LC_title_bar_role_logo">$upperleft</td>
1.359     albertel 4167:     <td class="LC_title_bar_domain_logo">$messages&nbsp;</td>
1.54      www      4168: </tr>
1.359     albertel 4169: <tr><td>$titleinfo $dc_info $menu</td>
                   4170: $roleinfo
1.368     albertel 4171: </tr>
1.356     albertel 4172: </table>
1.54      www      4173: ENDBODY
1.182     matthew  4174: }
                   4175: 
1.330     albertel 4176: sub make_attr_string {
                   4177:     my ($register,$attr_ref) = @_;
                   4178: 
                   4179:     if ($attr_ref && !ref($attr_ref)) {
                   4180: 	die("addentries Must be a hash ref ".
                   4181: 	    join(':',caller(1))." ".
                   4182: 	    join(':',caller(0))." ");
                   4183:     }
                   4184: 
                   4185:     if ($register) {
1.339     albertel 4186: 	my ($on_load,$on_unload);
                   4187: 	foreach my $key (keys(%{$attr_ref})) {
                   4188: 	    if      (lc($key) eq 'onload') {
                   4189: 		$on_load.=$attr_ref->{$key}.';';
                   4190: 		delete($attr_ref->{$key});
                   4191: 
                   4192: 	    } elsif (lc($key) eq 'onunload') {
                   4193: 		$on_unload.=$attr_ref->{$key}.';';
                   4194: 		delete($attr_ref->{$key});
                   4195: 	    }
                   4196: 	}
                   4197: 	$attr_ref->{'onload'}  =
                   4198: 	    &Apache::lonmenu::loadevents().  $on_load;
                   4199: 	$attr_ref->{'onunload'}=
                   4200: 	    &Apache::lonmenu::unloadevents().$on_unload;
                   4201:     }
                   4202: 
                   4203: # Accessibility font enhance
                   4204:     if ($env{'browser.fontenhance'} eq 'on') {
                   4205: 	my $style;
                   4206: 	foreach my $key (keys(%{$attr_ref})) {
                   4207: 	    if (lc($key) eq 'style') {
                   4208: 		$style.=$attr_ref->{$key}.';';
                   4209: 		delete($attr_ref->{$key});
                   4210: 	    }
                   4211: 	}
                   4212: 	$attr_ref->{'style'}=$style.'; font-size: x-large;';
1.330     albertel 4213:     }
1.339     albertel 4214: 
                   4215:     if ($env{'browser.blackwhite'} eq 'on') {
                   4216: 	delete($attr_ref->{'font'});
                   4217: 	delete($attr_ref->{'link'});
                   4218: 	delete($attr_ref->{'alink'});
                   4219: 	delete($attr_ref->{'vlink'});
                   4220: 	delete($attr_ref->{'bgcolor'});
                   4221: 	delete($attr_ref->{'background'});
                   4222:     }
                   4223: 
1.330     albertel 4224:     my $attr_string;
                   4225:     foreach my $attr (keys(%$attr_ref)) {
                   4226: 	$attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
                   4227:     }
                   4228:     return $attr_string;
                   4229: }
                   4230: 
                   4231: 
1.182     matthew  4232: ###############################################
1.251     albertel 4233: ###############################################
                   4234: 
                   4235: =pod
                   4236: 
                   4237: =item * &endbodytag()
                   4238: 
                   4239: Returns a uniform footer for LON-CAPA web pages.
                   4240: 
1.635     raeburn  4241: Inputs: 1 - optional reference to an args hash
                   4242: If in the hash, key for noredirectlink has a value which evaluates to true,
                   4243: a 'Continue' link is not displayed if the page contains an
                   4244: internal redirect in the <head></head> section,
                   4245: i.e., $env{'internal.head.redirect'} exists   
1.251     albertel 4246: 
                   4247: =cut
                   4248: 
                   4249: sub endbodytag {
1.635     raeburn  4250:     my ($args) = @_;
1.251     albertel 4251:     my $endbodytag='</body>';
1.269     albertel 4252:     $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
1.315     albertel 4253:     if ( exists( $env{'internal.head.redirect'} ) ) {
1.635     raeburn  4254:         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
                   4255: 	    $endbodytag=
                   4256: 	        "<br /><a href=\"$env{'internal.head.redirect'}\">".
                   4257: 	        &mt('Continue').'</a>'.
                   4258: 	        $endbodytag;
                   4259:         }
1.315     albertel 4260:     }
1.251     albertel 4261:     return $endbodytag;
                   4262: }
                   4263: 
1.352     albertel 4264: =pod
                   4265: 
                   4266: =item * &standard_css()
                   4267: 
                   4268: Returns a style sheet
                   4269: 
                   4270: Inputs: (all optional)
                   4271:             domain         -> force to color decorate a page for a specific
                   4272:                                domain
                   4273:             function       -> force usage of a specific rolish color scheme
                   4274:             bgcolor        -> override the default page bgcolor
                   4275: 
                   4276: =cut
                   4277: 
1.343     albertel 4278: sub standard_css {
1.345     albertel 4279:     my ($function,$domain,$bgcolor) = @_;
1.352     albertel 4280:     $function  = &get_users_function() if (!$function);
                   4281:     my $img    = &designparm($function.'.img',   $domain);
                   4282:     my $tabbg  = &designparm($function.'.tabbg', $domain);
                   4283:     my $font   = &designparm($function.'.font',  $domain);
1.345     albertel 4284:     my $sidebg = &designparm($function.'.sidebg',$domain);
1.382     albertel 4285:     my $pgbg_or_bgcolor =
                   4286: 	         $bgcolor ||
1.352     albertel 4287: 	         &designparm($function.'.pgbg',  $domain);
1.382     albertel 4288:     my $pgbg   = &designparm($function.'.pgbg',  $domain);
1.352     albertel 4289:     my $alink  = &designparm($function.'.alink', $domain);
                   4290:     my $vlink  = &designparm($function.'.vlink', $domain);
                   4291:     my $link   = &designparm($function.'.link',  $domain);
                   4292: 
1.602     albertel 4293:     my $sans                 = 'Verdana,Arial,Helvetica,sans-serif';
1.395     albertel 4294:     my $mono                 = 'monospace';
1.352     albertel 4295:     my $data_table_head      = $tabbg;
                   4296:     my $data_table_light     = '#EEEEEE';
1.470     banghart 4297:     my $data_table_dark      = '#DDDDDD';
                   4298:     my $data_table_darker    = '#CCCCCC';
1.349     albertel 4299:     my $data_table_highlight = '#FFFF00';
1.352     albertel 4300:     my $mail_new             = '#FFBB77';
                   4301:     my $mail_new_hover       = '#DD9955';
                   4302:     my $mail_read            = '#BBBB77';
                   4303:     my $mail_read_hover      = '#999944';
                   4304:     my $mail_replied         = '#AAAA88';
                   4305:     my $mail_replied_hover   = '#888855';
                   4306:     my $mail_other           = '#99BBBB';
                   4307:     my $mail_other_hover     = '#669999';
1.391     albertel 4308:     my $table_header         = '#DDDDDD';
1.489     raeburn  4309:     my $feedback_link_bg     = '#BBBBBB';
1.392     albertel 4310: 
1.608     albertel 4311:     my $border = ($env{'browser.type'} eq 'explorer' ||
                   4312: 		  $env{'browser.type'} eq 'safari'     ) ? '0px 2px 0px 2px'
                   4313: 	                                                 : '0px 3px 0px 4px';
1.448     albertel 4314: 
1.523     albertel 4315: 
1.343     albertel 4316:     return <<END;
1.345     albertel 4317: h1, h2, h3, th { font-family: $sans }
1.343     albertel 4318: a:focus { color: red; background: yellow } 
1.510     albertel 4319: table.thinborder,
1.523     albertel 4320: 
1.510     albertel 4321: table.thinborder tr th {
                   4322:   border-style: solid;
                   4323:   border-width: 1px;
                   4324:   background: $tabbg;
                   4325: }
1.523     albertel 4326: table.thinborder tr td {
1.510     albertel 4327:   border-style: solid;
                   4328:   border-width: 1px
                   4329: }
1.426     albertel 4330: 
1.343     albertel 4331: form, .inline { display: inline; }
                   4332: .center { text-align: center; }
1.593     albertel 4333: .LC_filename {font-family: $mono; white-space:pre;}
1.350     albertel 4334: .LC_error {
                   4335:   color: red;
                   4336:   font-size: larger;
                   4337: }
1.457     albertel 4338: .LC_warning,
                   4339: .LC_diff_removed {
1.394     albertel 4340:   color: red;
                   4341: }
1.532     albertel 4342: 
                   4343: .LC_info,
1.457     albertel 4344: .LC_success,
                   4345: .LC_diff_added {
1.350     albertel 4346:   color: green;
                   4347: }
1.543     albertel 4348: .LC_unknown {
                   4349:   color: yellow;
                   4350: }
                   4351: 
1.440     albertel 4352: .LC_icon {
                   4353:   border: 0px;
                   4354: }
1.539     albertel 4355: .LC_indexer_icon {
                   4356:   border: 0px;
                   4357:   height: 22px;
                   4358: }
1.543     albertel 4359: .LC_docs_spacer {
                   4360:   width: 25px;
                   4361:   height: 1px;
                   4362:   border: 0px;
                   4363: }
1.346     albertel 4364: 
1.532     albertel 4365: .LC_internal_info {
                   4366:   color: #999;
                   4367: }
                   4368: 
1.458     albertel 4369: table.LC_pastsubmission {
                   4370:   border: 1px solid black;
                   4371:   margin: 2px;
                   4372: }
                   4373: 
1.606     albertel 4374: table#LC_top_nav, table#LC_menubuttons,table#LC_nav_location {
1.345     albertel 4375:   width: 100%;
                   4376:   background: $pgbg;
1.392     albertel 4377:   border: 2px;
1.402     albertel 4378:   border-collapse: separate;
1.403     albertel 4379:   padding: 0px;
1.345     albertel 4380: }
1.392     albertel 4381: 
1.606     albertel 4382: table#LC_title_bar, table.LC_breadcrumbs, 
1.393     albertel 4383: table#LC_title_bar.LC_with_remote {
1.359     albertel 4384:   width: 100%;
1.392     albertel 4385:   border-color: $pgbg;
                   4386:   border-style: solid;
                   4387:   border-width: $border;
                   4388: 
1.379     albertel 4389:   background: $pgbg;
                   4390:   font-family: $sans;
1.392     albertel 4391:   border-collapse: collapse;
1.403     albertel 4392:   padding: 0px;
1.359     albertel 4393: }
1.392     albertel 4394: 
1.409     albertel 4395: table.LC_docs_path {
                   4396:   width: 100%;
                   4397:   border: 0;
                   4398:   background: $pgbg;
                   4399:   font-family: $sans;
                   4400:   border-collapse: collapse;
                   4401:   padding: 0px;
                   4402: }
                   4403: 
1.359     albertel 4404: table#LC_title_bar td {
                   4405:   background: $tabbg;
                   4406: }
                   4407: table#LC_title_bar td.LC_title_bar_who {
                   4408:   background: $tabbg;
                   4409:   color: $font;
1.427     albertel 4410:   font: small $sans;
1.359     albertel 4411:   text-align: right;
                   4412: }
1.469     banghart 4413: span.LC_metadata {
                   4414:     font-family: $sans;
                   4415: }
1.359     albertel 4416: span.LC_title_bar_title {
1.416     albertel 4417:   font: bold x-large $sans;
1.359     albertel 4418: }
                   4419: table#LC_title_bar td.LC_title_bar_domain_logo {
                   4420:   background: $sidebg;
                   4421:   text-align: right;
1.368     albertel 4422:   padding: 0px;
                   4423: }
                   4424: table#LC_title_bar td.LC_title_bar_role_logo {
                   4425:   background: $sidebg;
                   4426:   padding: 0px;
1.359     albertel 4427: }
                   4428: 
1.346     albertel 4429: table#LC_menubuttons_mainmenu {
1.526     www      4430:   width: 100%;
1.346     albertel 4431:   border: 0px;
                   4432:   border-spacing: 1px;
1.372     albertel 4433:   padding: 0px 1px;
1.346     albertel 4434:   margin: 0px;
                   4435:   border-collapse: separate;
                   4436: }
                   4437: table#LC_menubuttons img, table#LC_menubuttons_mainmenu img {
                   4438:   border: 0px;
                   4439: }
1.345     albertel 4440: table#LC_top_nav td {
                   4441:   background: $tabbg;
1.392     albertel 4442:   border: 0px;
1.407     albertel 4443:   font-size: small;
1.345     albertel 4444: }
                   4445: table#LC_top_nav td a, div#LC_top_nav a {
                   4446:   color: $font;
                   4447:   font-family: $sans;
                   4448: }
1.364     albertel 4449: table#LC_top_nav td.LC_top_nav_logo {
                   4450:   background: $tabbg;
1.432     albertel 4451:   text-align: left;
1.408     albertel 4452:   white-space: nowrap;
1.432     albertel 4453:   width: 31px;
1.408     albertel 4454: }
                   4455: table#LC_top_nav td.LC_top_nav_logo img {
1.432     albertel 4456:   border: 0px;
1.408     albertel 4457:   vertical-align: bottom;
1.364     albertel 4458: }
1.432     albertel 4459: table#LC_top_nav td.LC_top_nav_exit,
                   4460: table#LC_top_nav td.LC_top_nav_help {
                   4461:   width: 2.0em;
                   4462: }
1.442     albertel 4463: table#LC_top_nav td.LC_top_nav_login {
                   4464:   width: 4.0em;
                   4465:   text-align: center;
                   4466: }
1.409     albertel 4467: table.LC_breadcrumbs td, table.LC_docs_path td  {
1.357     albertel 4468:   background: $tabbg;
                   4469:   color: $font;
                   4470:   font-family: $sans;
1.358     albertel 4471:   font-size: smaller;
1.357     albertel 4472: }
1.411     albertel 4473: table.LC_breadcrumbs td.LC_breadcrumbs_component,
1.409     albertel 4474: table.LC_docs_path td.LC_docs_path_component {
1.357     albertel 4475:   background: $tabbg;
                   4476:   color: $font;
                   4477:   font-family: $sans;
                   4478:   font-size: larger;
                   4479:   text-align: right;
                   4480: }
1.383     albertel 4481: td.LC_table_cell_checkbox {
                   4482:   text-align: center;
                   4483: }
                   4484: 
1.522     albertel 4485: table#LC_mainmenu td.LC_mainmenu_column {
                   4486:     vertical-align: top;
                   4487: }
                   4488: 
1.346     albertel 4489: .LC_menubuttons_inline_text {
                   4490:   color: $font;
                   4491:   font-family: $sans;
                   4492:   font-size: smaller;
                   4493: }
                   4494: 
1.526     www      4495: .LC_menubuttons_link {
                   4496:   text-decoration: none;
                   4497: }
                   4498: 
1.522     albertel 4499: .LC_menubuttons_category {
1.521     www      4500:   color: $font;
1.526     www      4501:   background: $pgbg;
1.521     www      4502:   font-family: $sans;
                   4503:   font-size: larger;
                   4504:   font-weight: bold;
                   4505: }
                   4506: 
1.346     albertel 4507: td.LC_menubuttons_text {
1.526     www      4508:   width: 90%;
1.346     albertel 4509:   color: $font;
                   4510:   font-family: $sans;
                   4511: }
1.526     www      4512: 
1.346     albertel 4513: td.LC_menubuttons_img {
                   4514: }
1.526     www      4515: 
1.346     albertel 4516: .LC_current_location {
                   4517:   font-family: $sans;
                   4518:   background: $tabbg;
                   4519: }
                   4520: .LC_new_mail {
                   4521:   font-family: $sans;
1.634     www      4522:   background: $tabbg;
1.346     albertel 4523:   font-weight: bold;
                   4524: }
1.347     albertel 4525: 
1.526     www      4526: .LC_rolesmenu_is {
                   4527:   font-family: $sans;
                   4528: }
                   4529: 
                   4530: .LC_rolesmenu_selected {
                   4531:   font-family: $sans;
                   4532: }
                   4533: 
                   4534: .LC_rolesmenu_future {
                   4535:   font-family: $sans;
                   4536: }
                   4537: 
                   4538: 
                   4539: .LC_rolesmenu_will {
                   4540:   font-family: $sans;
                   4541: }
                   4542: 
                   4543: .LC_rolesmenu_will_not {
                   4544:   font-family: $sans;
                   4545: }
                   4546: 
                   4547: .LC_rolesmenu_expired {
                   4548:   font-family: $sans;
                   4549: }
                   4550: 
                   4551: .LC_rolesinfo {
                   4552:   font-family: $sans;
                   4553: }
                   4554: 
1.527     www      4555: .LC_dropadd_labeltext {
                   4556:   font-family: $sans;
                   4557:   text-align: right;
                   4558: }
                   4559: 
                   4560: .LC_preferences_labeltext {
                   4561:   font-family: $sans;
                   4562:   text-align: right;
                   4563: }
                   4564: 
1.440     albertel 4565: table.LC_aboutme_port {
                   4566:   border: 0px;
                   4567:   border-collapse: collapse;
                   4568:   border-spacing: 0px;
                   4569: }
1.349     albertel 4570: table.LC_data_table, table.LC_mail_list {
1.347     albertel 4571:   border: 1px solid #000000;
1.402     albertel 4572:   border-collapse: separate;
1.426     albertel 4573:   border-spacing: 1px;
1.610     albertel 4574:   background: $pgbg;
1.347     albertel 4575: }
1.422     albertel 4576: .LC_data_table_dense {
                   4577:   font-size: small;
                   4578: }
1.507     raeburn  4579: table.LC_nested_outer {
                   4580:   border: 1px solid #000000;
1.589     raeburn  4581:   border-collapse: collapse;
1.507     raeburn  4582:   border-spacing: 0px;
                   4583:   width: 100%;
                   4584: }
                   4585: table.LC_nested {
                   4586:   border: 0px;
1.589     raeburn  4587:   border-collapse: collapse;
1.507     raeburn  4588:   border-spacing: 0px;
                   4589:   width: 100%;
                   4590: }
1.523     albertel 4591: table.LC_data_table tr th, table.LC_calendar tr th, table.LC_mail_list tr th,
                   4592: table.LC_prior_tries tr th {
1.349     albertel 4593:   font-weight: bold;
                   4594:   background-color: $data_table_head;
1.421     albertel 4595:   font-size: smaller;
1.347     albertel 4596: }
1.610     albertel 4597: table.LC_data_table tr.LC_odd_row > td, 
1.440     albertel 4598: table.LC_aboutme_port tr td {
1.349     albertel 4599:   background-color: $data_table_light;
1.425     albertel 4600:   padding: 2px;
1.347     albertel 4601: }
1.610     albertel 4602: table.LC_data_table tr.LC_even_row > td,
1.440     albertel 4603: table.LC_aboutme_port tr.LC_even_row td {
1.349     albertel 4604:   background-color: $data_table_dark;
1.347     albertel 4605: }
1.425     albertel 4606: table.LC_data_table tr.LC_data_table_highlight td {
                   4607:   background-color: $data_table_darker;
                   4608: }
1.639     raeburn  4609: table.LC_data_table tr td.LC_leftcol_header {
                   4610:   background-color: $data_table_head;
                   4611:   font-weight: bold;
                   4612: }
1.451     albertel 4613: table.LC_data_table tr.LC_empty_row td,
1.507     raeburn  4614: table.LC_nested tr.LC_empty_row td {
1.347     albertel 4615:   background-color: #FFFFFF;
1.421     albertel 4616:   font-weight: bold;
                   4617:   font-style: italic;
                   4618:   text-align: center;
                   4619:   padding: 8px;
1.347     albertel 4620: }
1.507     raeburn  4621: table.LC_nested tr.LC_empty_row td {
1.465     albertel 4622:   padding: 4ex
                   4623: }
1.507     raeburn  4624: table.LC_nested_outer tr th {
                   4625:   font-weight: bold;
                   4626:   background-color: $data_table_head;
                   4627:   font-size: smaller;
                   4628:   border-bottom: 1px solid #000000;
                   4629: }
                   4630: table.LC_nested_outer tr td.LC_subheader {
                   4631:   background-color: $data_table_head;
                   4632:   font-weight: bold;
                   4633:   font-size: small;
                   4634:   border-bottom: 1px solid #000000;
                   4635:   text-align: right;
1.451     albertel 4636: }
1.507     raeburn  4637: table.LC_nested tr.LC_info_row td {
1.451     albertel 4638:   background-color: #CCC;
                   4639:   font-weight: bold;
                   4640:   font-size: small;
1.507     raeburn  4641:   text-align: center;
                   4642: }
1.589     raeburn  4643: table.LC_nested tr.LC_info_row td.LC_left_item,
                   4644: table.LC_nested_outer tr th.LC_left_item {
1.507     raeburn  4645:   text-align: left;
1.451     albertel 4646: }
1.507     raeburn  4647: table.LC_nested td {
1.451     albertel 4648:   background-color: #FFF;
                   4649:   font-size: small;
1.507     raeburn  4650: }
                   4651: table.LC_nested_outer tr th.LC_right_item,
                   4652: table.LC_nested tr.LC_info_row td.LC_right_item,
                   4653: table.LC_nested tr.LC_odd_row td.LC_right_item,
                   4654: table.LC_nested tr td.LC_right_item {
1.451     albertel 4655:   text-align: right;
                   4656: }
                   4657: 
1.507     raeburn  4658: table.LC_nested tr.LC_odd_row td {
1.451     albertel 4659:   background-color: #EEE;
                   4660: }
                   4661: 
1.473     raeburn  4662: table.LC_createuser {
                   4663: }
                   4664: 
                   4665: table.LC_createuser tr.LC_section_row td {
                   4666:   font-size: smaller;
                   4667: }
                   4668: 
                   4669: table.LC_createuser tr.LC_info_row td  {
                   4670:   background-color: #CCC;
                   4671:   font-weight: bold;
                   4672:   text-align: center;
                   4673: }
                   4674: 
1.349     albertel 4675: table.LC_calendar {
                   4676:   border: 1px solid #000000;
                   4677:   border-collapse: collapse;
                   4678: }
                   4679: table.LC_calendar_pickdate {
                   4680:   font-size: xx-small;
                   4681: }
                   4682: table.LC_calendar tr td {
                   4683:   border: 1px solid #000000;
                   4684:   vertical-align: top;
                   4685: }
                   4686: table.LC_calendar tr td.LC_calendar_day_empty {
                   4687:   background-color: $data_table_dark;
                   4688: }
                   4689: table.LC_calendar tr td.LC_calendar_day_current {
                   4690:   background-color: $data_table_highlight;
                   4691: }
                   4692: 
                   4693: table.LC_mail_list tr.LC_mail_new {
                   4694:   background-color: $mail_new;
                   4695: }
                   4696: table.LC_mail_list tr.LC_mail_new:hover {
                   4697:   background-color: $mail_new_hover;
                   4698: }
                   4699: table.LC_mail_list tr.LC_mail_read {
                   4700:   background-color: $mail_read;
                   4701: }
                   4702: table.LC_mail_list tr.LC_mail_read:hover {
                   4703:   background-color: $mail_read_hover;
                   4704: }
                   4705: table.LC_mail_list tr.LC_mail_replied {
                   4706:   background-color: $mail_replied;
                   4707: }
                   4708: table.LC_mail_list tr.LC_mail_replied:hover {
                   4709:   background-color: $mail_replied_hover;
                   4710: }
                   4711: table.LC_mail_list tr.LC_mail_other {
                   4712:   background-color: $mail_other;
                   4713: }
                   4714: table.LC_mail_list tr.LC_mail_other:hover {
                   4715:   background-color: $mail_other_hover;
                   4716: }
1.494     raeburn  4717: table.LC_mail_list tr.LC_mail_even {
                   4718: }
                   4719: table.LC_mail_list tr.LC_mail_odd {
                   4720: }
                   4721: 
1.385     albertel 4722: 
1.386     albertel 4723: table#LC_portfolio_actions {
                   4724:   width: auto;
                   4725:   background: $pgbg;
                   4726:   border: 0px;
                   4727:   border-spacing: 2px 2px;
                   4728:   padding: 0px;
                   4729:   margin: 0px;
                   4730:   border-collapse: separate;
                   4731: }
                   4732: table#LC_portfolio_actions td.LC_label {
                   4733:   background: $tabbg;
                   4734:   text-align: right;
                   4735: }
                   4736: table#LC_portfolio_actions td.LC_value {
                   4737:   background: $tabbg;
                   4738: }
1.385     albertel 4739: 
1.391     albertel 4740: table#LC_cstr_controls {
                   4741:   width: 100%;
                   4742:   border-collapse: collapse;
                   4743: }
                   4744: table#LC_cstr_controls tr td {
                   4745:   border: 4px solid $pgbg;
                   4746:   padding: 4px;
                   4747:   text-align: center;
                   4748:   background: $tabbg;
                   4749: }
                   4750: table#LC_cstr_controls tr th {
                   4751:   border: 4px solid $pgbg;
                   4752:   background: $table_header;
                   4753:   text-align: center;
                   4754:   font-family: $sans;
                   4755:   font-size: smaller;
                   4756: }
                   4757: 
1.389     albertel 4758: table#LC_browser {
                   4759:  
                   4760: }
                   4761: table#LC_browser tr th {
1.391     albertel 4762:   background: $table_header;
1.389     albertel 4763: }
1.390     albertel 4764: table#LC_browser tr td {
                   4765:   padding: 2px;
                   4766: }
1.389     albertel 4767: table#LC_browser tr.LC_browser_file,
                   4768: table#LC_browser tr.LC_browser_file_published {
                   4769:   background: #CCFF88;
                   4770: }
                   4771: table#LC_browser tr.LC_browser_file_locked,
                   4772: table#LC_browser tr.LC_browser_file_unpublished {
                   4773:   background: #FFAA99;
1.387     albertel 4774: }
1.389     albertel 4775: table#LC_browser tr.LC_browser_file_obsolete {
                   4776:   background: #AAAAAA;
1.387     albertel 4777: }
1.455     albertel 4778: table#LC_browser tr.LC_browser_file_modified,
                   4779: table#LC_browser tr.LC_browser_file_metamodified {
1.389     albertel 4780:   background: #FFFF77;
1.387     albertel 4781: }
1.389     albertel 4782: table#LC_browser tr.LC_browser_folder {
                   4783:   background: #CCCCFF;
1.387     albertel 4784: }
1.388     albertel 4785: span.LC_current_location {
                   4786:   font-size: x-large;
                   4787:   background: $pgbg;
                   4788: }
1.387     albertel 4789: 
1.395     albertel 4790: span.LC_parm_menu_item {
                   4791:   font-size: larger;
                   4792:   font-family: $sans;
                   4793: }
                   4794: span.LC_parm_scope_all {
                   4795:   color: red;
                   4796: }
                   4797: span.LC_parm_scope_folder {
                   4798:   color: green;
                   4799: }
                   4800: span.LC_parm_scope_resource {
                   4801:   color: orange;
                   4802: }
                   4803: span.LC_parm_part {
                   4804:   color: blue;
                   4805: }
                   4806: span.LC_parm_folder, span.LC_parm_symb {
                   4807:   font-size: x-small;
                   4808:   font-family: $mono;
                   4809:   color: #AAAAAA;
                   4810: }
                   4811: 
1.396     albertel 4812: td.LC_parm_overview_level_menu, td.LC_parm_overview_map_menu,
                   4813: td.LC_parm_overview_parm_selectors, td.LC_parm_overview_parm_restrictions {
                   4814:   border: 1px solid black;
                   4815:   border-collapse: collapse;
                   4816: }
                   4817: table.LC_parm_overview_restrictions td {
                   4818:   border-width: 1px 4px 1px 4px;
                   4819:   border-style: solid;
                   4820:   border-color: $pgbg;
                   4821:   text-align: center;
                   4822: }
                   4823: table.LC_parm_overview_restrictions th {
                   4824:   background: $tabbg;
                   4825:   border-width: 1px 4px 1px 4px;
                   4826:   border-style: solid;
                   4827:   border-color: $pgbg;
                   4828: }
1.398     albertel 4829: table#LC_helpmenu {
                   4830:   border: 0px;
                   4831:   height: 55px;
                   4832:   border-spacing: 0px;
                   4833: }
                   4834: 
                   4835: table#LC_helpmenu fieldset legend {
                   4836:   font-size: larger;
                   4837:   font-weight: bold;
                   4838: }
1.397     albertel 4839: table#LC_helpmenu_links {
                   4840:   width: 100%;
                   4841:   border: 1px solid black;
                   4842:   background: $pgbg;
                   4843:   padding: 0px;
                   4844:   border-spacing: 1px;
                   4845: }
                   4846: table#LC_helpmenu_links tr td {
                   4847:   padding: 1px;
                   4848:   background: $tabbg;
1.399     albertel 4849:   text-align: center;
                   4850:   font-weight: bold;
1.397     albertel 4851: }
1.396     albertel 4852: 
1.397     albertel 4853: table#LC_helpmenu_links a:link, table#LC_helpmenu_links a:visited,
                   4854: table#LC_helpmenu_links a:active {
                   4855:   text-decoration: none;
                   4856:   color: $font;
                   4857: }
                   4858: table#LC_helpmenu_links a:hover {
                   4859:   text-decoration: underline;
                   4860:   color: $vlink;
                   4861: }
1.396     albertel 4862: 
1.417     albertel 4863: .LC_chrt_popup_exists {
                   4864:   border: 1px solid #339933;
                   4865:   margin: -1px;
                   4866: }
                   4867: .LC_chrt_popup_up {
                   4868:   border: 1px solid yellow;
                   4869:   margin: -1px;
                   4870: }
                   4871: .LC_chrt_popup {
                   4872:   border: 1px solid #8888FF;
                   4873:   background: #CCCCFF;
                   4874: }
1.421     albertel 4875: table.LC_pick_box {
                   4876:   border-collapse: separate;
                   4877:   background: white;
                   4878:   border: 1px solid black;
                   4879:   border-spacing: 1px;
                   4880: }
                   4881: table.LC_pick_box td.LC_pick_box_title {
                   4882:   background: $tabbg;
                   4883:   font-weight: bold;
                   4884:   text-align: right;
                   4885:   width: 184px;
                   4886:   padding: 8px;
                   4887: }
1.645     raeburn  4888: table.LC_pick_box td.LC_selfenroll_pick_box_title {
                   4889:   background: $tabbg;
                   4890:   font-weight: bold;
                   4891:   text-align: right;
                   4892:   width: 350px;
                   4893:   padding: 8px;
                   4894: }
                   4895: 
1.579     raeburn  4896: table.LC_pick_box td.LC_pick_box_value {
                   4897:   text-align: left;
                   4898:   padding: 8px;
                   4899: }
                   4900: table.LC_pick_box td.LC_pick_box_select {
                   4901:   text-align: left;
                   4902:   padding: 8px;
                   4903: }
1.424     albertel 4904: table.LC_pick_box td.LC_pick_box_separator {
1.421     albertel 4905:   padding: 0px;
                   4906:   height: 1px;
                   4907:   background: black;
                   4908: }
                   4909: table.LC_pick_box td.LC_pick_box_submit {
                   4910:   text-align: right;
                   4911: }
1.579     raeburn  4912: table.LC_pick_box td.LC_evenrow_value {
                   4913:   text-align: left;
                   4914:   padding: 8px;
                   4915:   background-color: $data_table_light;
                   4916: }
                   4917: table.LC_pick_box td.LC_oddrow_value {
                   4918:   text-align: left;
                   4919:   padding: 8px;
                   4920:   background-color: $data_table_light;
                   4921: }
                   4922: table.LC_helpform_receipt {
                   4923:   width: 620px;
                   4924:   border-collapse: separate;
                   4925:   background: white;
                   4926:   border: 1px solid black;
                   4927:   border-spacing: 1px;
                   4928: }
                   4929: table.LC_helpform_receipt td.LC_pick_box_title {
                   4930:   background: $tabbg;
                   4931:   font-weight: bold;
                   4932:   text-align: right;
                   4933:   width: 184px;
                   4934:   padding: 8px;
                   4935: }
                   4936: table.LC_helpform_receipt td.LC_evenrow_value {
                   4937:   text-align: left;
                   4938:   padding: 8px;
                   4939:   background-color: $data_table_light;
                   4940: }
                   4941: table.LC_helpform_receipt td.LC_oddrow_value {
                   4942:   text-align: left;
                   4943:   padding: 8px;
                   4944:   background-color: $data_table_light;
                   4945: }
                   4946: table.LC_helpform_receipt td.LC_pick_box_separator {
                   4947:   padding: 0px;
                   4948:   height: 1px;
                   4949:   background: black;
                   4950: }
                   4951: span.LC_helpform_receipt_cat {
                   4952:   font-weight: bold;
                   4953: }
1.424     albertel 4954: table.LC_group_priv_box {
                   4955:   background: white;
                   4956:   border: 1px solid black;
                   4957:   border-spacing: 1px;
                   4958: }
                   4959: table.LC_group_priv_box td.LC_pick_box_title {
                   4960:   background: $tabbg;
                   4961:   font-weight: bold;
                   4962:   text-align: right;
                   4963:   width: 184px;
                   4964: }
                   4965: table.LC_group_priv_box td.LC_groups_fixed {
                   4966:   background: $data_table_light;
                   4967:   text-align: center;
                   4968: }
                   4969: table.LC_group_priv_box td.LC_groups_optional {
                   4970:   background: $data_table_dark;
                   4971:   text-align: center;
                   4972: }
                   4973: table.LC_group_priv_box td.LC_groups_functionality {
                   4974:   background: $data_table_darker;
                   4975:   text-align: center;
                   4976:   font-weight: bold;
                   4977: }
                   4978: table.LC_group_priv td {
                   4979:   text-align: left;
                   4980:   padding: 0px;
                   4981: }
                   4982: 
1.421     albertel 4983: table.LC_notify_front_page {
                   4984:   background: white;
                   4985:   border: 1px solid black;
                   4986:   padding: 8px;
                   4987: }
                   4988: table.LC_notify_front_page td {
                   4989:   padding: 8px;
                   4990: }
1.424     albertel 4991: .LC_navbuttons {
                   4992:   margin: 2ex 0ex 2ex 0ex;
                   4993: }
1.423     albertel 4994: .LC_topic_bar {
                   4995:   font-family: $sans;
                   4996:   font-weight: bold;
                   4997:   width: 100%;
                   4998:   background: $tabbg;
                   4999:   vertical-align: middle;
                   5000:   margin: 2ex 0ex 2ex 0ex;
                   5001: }
                   5002: .LC_topic_bar span {
                   5003:   vertical-align: middle;
                   5004: }
                   5005: .LC_topic_bar img {
                   5006:   vertical-align: bottom;
                   5007: }
                   5008: table.LC_course_group_status {
                   5009:   margin: 20px;
                   5010: }
                   5011: table.LC_status_selector td {
                   5012:   vertical-align: top;
                   5013:   text-align: center;
1.424     albertel 5014:   padding: 4px;
                   5015: }
                   5016: table.LC_descriptive_input td.LC_description {
                   5017:   vertical-align: top;
                   5018:   text-align: right;
                   5019:   font-weight: bold;
1.423     albertel 5020: }
1.599     albertel 5021: div.LC_feedback_link {
1.616     albertel 5022:   clear: both;
1.599     albertel 5023:   background: white;
                   5024:   width: 100%;  
1.489     raeburn  5025: }
                   5026: span.LC_feedback_link {
1.599     albertel 5027:   background: $feedback_link_bg;
                   5028:   font-size: larger;
                   5029: }
                   5030: span.LC_message_link {
                   5031:   background: $feedback_link_bg;
                   5032:   font-size: larger;
                   5033:   position: absolute;
                   5034:   right: 1em;
1.489     raeburn  5035: }
1.421     albertel 5036: 
1.515     albertel 5037: table.LC_prior_tries {
1.524     albertel 5038:   border: 1px solid #000000;
                   5039:   border-collapse: separate;
                   5040:   border-spacing: 1px;
1.515     albertel 5041: }
1.523     albertel 5042: 
1.515     albertel 5043: table.LC_prior_tries td {
1.524     albertel 5044:   padding: 2px;
1.515     albertel 5045: }
1.523     albertel 5046: 
                   5047: .LC_answer_correct {
                   5048:   background: #AAFFAA;
                   5049:   color: black;
                   5050: }
                   5051: .LC_answer_charged_try {
                   5052:   background: #FFAAAA ! important;
                   5053:   color: black;
                   5054: }
                   5055: .LC_answer_not_charged_try, 
                   5056: .LC_answer_no_grade,
                   5057: .LC_answer_late {
                   5058:   background: #FFFFAA;
                   5059:   color: black;
                   5060: }
                   5061: .LC_answer_previous {
                   5062:   background: #AAAAFF;
                   5063:   color: black;
                   5064: }
                   5065: .LC_answer_no_message {
                   5066:   background: #FFFFFF;
                   5067:   color: black;
                   5068: }
                   5069: .LC_answer_unknown {
                   5070:   background: orange;
                   5071:   color: black;
                   5072: }
                   5073: 
                   5074: 
1.529     albertel 5075: span.LC_prior_numerical,
                   5076: span.LC_prior_string,
                   5077: span.LC_prior_custom,
                   5078: span.LC_prior_reaction,
                   5079: span.LC_prior_math {
1.523     albertel 5080:   font-family: monospace;
                   5081:   white-space: pre;
                   5082: }
                   5083: 
1.525     albertel 5084: span.LC_prior_string {
                   5085:   font-family: monospace;
                   5086:   white-space: pre;
                   5087: }
                   5088: 
1.523     albertel 5089: table.LC_prior_option {
                   5090:   width: 100%;
                   5091:   border-collapse: collapse;
                   5092: }
1.528     albertel 5093: table.LC_prior_rank, table.LC_prior_match {
                   5094:   border-collapse: collapse;
                   5095: }
                   5096: table.LC_prior_option tr td,
                   5097: table.LC_prior_rank tr td,
                   5098: table.LC_prior_match tr td {
1.524     albertel 5099:   border: 1px solid #000000;
1.515     albertel 5100: }
                   5101: 
1.519     raeburn  5102: span.LC_nobreak {
1.544     albertel 5103:   white-space: nowrap;
1.519     raeburn  5104: }
                   5105: 
1.576     raeburn  5106: span.LC_cusr_emph {
                   5107:   font-style: italic;
                   5108: }
                   5109: 
1.633     raeburn  5110: span.LC_cusr_subheading {
                   5111:   font-weight: normal;
                   5112:   font-size: 85%;
                   5113: }
                   5114: 
1.545     albertel 5115: table.LC_docs_documents {
                   5116:   background: #BBBBBB;
1.547     albertel 5117:   border-width: 0px;
1.545     albertel 5118:   border-collapse: collapse;
                   5119: }
                   5120: 
                   5121: table.LC_docs_documents td.LC_docs_document {
                   5122:   border: 2px solid black;
                   5123:   padding: 4px;
                   5124: }
                   5125: 
                   5126: .LC_docs_course_commands div {
                   5127:   float: left;
                   5128:   border: 4px solid #AAAAAA;
                   5129:   padding: 4px;
                   5130:   background: #DDDDCC;
                   5131: }
                   5132: 
                   5133: .LC_docs_entry_move {
                   5134:   border: 0px;
                   5135:   border-collapse: collapse;
1.544     albertel 5136: }
                   5137: 
1.545     albertel 5138: .LC_docs_entry_move td {
                   5139:   border: 2px solid #BBBBBB;
                   5140:   background: #DDDDDD;
                   5141: }
                   5142: 
                   5143: .LC_docs_editor td.LC_docs_entry_commands {
                   5144:   background: #DDDDDD;
                   5145:   font-size: x-small;
                   5146: }
1.544     albertel 5147: .LC_docs_copy {
1.545     albertel 5148:   color: #000099;
1.544     albertel 5149: }
                   5150: .LC_docs_cut {
1.545     albertel 5151:   color: #550044;
1.544     albertel 5152: }
                   5153: .LC_docs_rename {
1.545     albertel 5154:   color: #009900;
1.544     albertel 5155: }
                   5156: .LC_docs_remove {
1.545     albertel 5157:   color: #990000;
                   5158: }
                   5159: 
1.547     albertel 5160: .LC_docs_reinit_warn,
                   5161: .LC_docs_ext_edit {
                   5162:   font-size: x-small;
                   5163: }
                   5164: 
1.545     albertel 5165: .LC_docs_editor td.LC_docs_entry_title,
                   5166: .LC_docs_editor td.LC_docs_entry_icon {
                   5167:   background: #FFFFBB;
                   5168: }
                   5169: .LC_docs_editor td.LC_docs_entry_parameter {
                   5170:   background: #BBBBFF;
                   5171:   font-size: x-small;
                   5172:   white-space: nowrap;
                   5173: }
                   5174: 
                   5175: table.LC_docs_adddocs td,
                   5176: table.LC_docs_adddocs th {
                   5177:   border: 1px solid #BBBBBB;
                   5178:   padding: 4px;
                   5179:   background: #DDDDDD;
1.543     albertel 5180: }
                   5181: 
1.584     albertel 5182: table.LC_sty_begin {
                   5183:   background: #BBFFBB;
                   5184: }
                   5185: table.LC_sty_end {
                   5186:   background: #FFBBBB;
                   5187: }
                   5188: 
1.589     raeburn  5189: table.LC_double_column {
                   5190:   border-width: 0px;
                   5191:   border-collapse: collapse;
                   5192:   width: 100%;
                   5193:   padding: 2px;
                   5194: }
                   5195: 
                   5196: table.LC_double_column tr td.LC_left_col {
1.590     raeburn  5197:   top: 2px;
1.589     raeburn  5198:   left: 2px;
                   5199:   width: 47%;
                   5200:   vertical-align: top;
                   5201: }
                   5202: 
                   5203: table.LC_double_column tr td.LC_right_col {
                   5204:   top: 2px;
                   5205:   right: 2px; 
                   5206:   width: 47%;
                   5207:   vertical-align: top;
                   5208: }
                   5209: 
1.594     raeburn  5210: span.LC_role_level {
                   5211:   font-weight: bold;
                   5212: }
                   5213: 
1.591     raeburn  5214: div.LC_left_float {
                   5215:   float: left;
                   5216:   padding-right: 5%;
1.597     albertel 5217:   padding-bottom: 4px;
1.591     raeburn  5218: }
                   5219: 
                   5220: div.LC_clear_float_header {
1.597     albertel 5221:   padding-bottom: 2px;
1.591     raeburn  5222: }
                   5223: 
                   5224: div.LC_clear_float_footer {
1.597     albertel 5225:   padding-top: 10px;
1.591     raeburn  5226:   clear: both;
                   5227: }
                   5228: 
1.597     albertel 5229: 
1.601     albertel 5230: div.LC_grade_select_mode {
1.604     albertel 5231:   font-family: $sans;
1.601     albertel 5232: }
                   5233: div.LC_grade_select_mode div div {
                   5234:   margin: 5px;
                   5235: }
                   5236: div.LC_grade_select_mode_selector {
                   5237:   margin: 5px;
                   5238:   float: left;
                   5239: }
                   5240: div.LC_grade_select_mode_selector_header {
                   5241:   font: bold medium $sans;
                   5242: }
                   5243: div.LC_grade_select_mode_type {
                   5244:   clear: left;
                   5245: }
                   5246: 
1.597     albertel 5247: div.LC_grade_show_user {
                   5248:   margin-top: 20px;
                   5249:   border: 1px solid black;
                   5250: }
                   5251: div.LC_grade_user_name {
                   5252:   background: #DDDDEE;
                   5253:   border-bottom: 1px solid black;
                   5254:   font: bold large $sans;
                   5255: }
                   5256: div.LC_grade_show_user_odd_row div.LC_grade_user_name {
                   5257:   background: #DDEEDD;
                   5258: }
                   5259: 
                   5260: div.LC_grade_show_problem,
                   5261: div.LC_grade_submissions,
                   5262: div.LC_grade_message_center,
                   5263: div.LC_grade_info_links,
                   5264: div.LC_grade_assign {
                   5265:   margin: 5px;
                   5266:   width: 99%;
                   5267:   background: #FFFFFF;
                   5268: }
                   5269: div.LC_grade_show_problem_header,
                   5270: div.LC_grade_submissions_header,
                   5271: div.LC_grade_message_center_header,
                   5272: div.LC_grade_assign_header {
                   5273:   font: bold large $sans;
                   5274: }
                   5275: div.LC_grade_show_problem_problem,
                   5276: div.LC_grade_submissions_body,
                   5277: div.LC_grade_message_center_body,
                   5278: div.LC_grade_assign_body {
                   5279:   border: 1px solid black;
                   5280:   width: 99%;
                   5281:   background: #FFFFFF;
                   5282: }
1.598     albertel 5283: span.LC_grade_check_note {
                   5284:   font: normal medium $sans;
                   5285:   display: inline;
                   5286:   position: absolute;
                   5287:   right: 1em;
                   5288: }
1.597     albertel 5289: 
1.613     albertel 5290: table.LC_scantron_action {
                   5291:   width: 100%;
                   5292: }
                   5293: table.LC_scantron_action tr th {
                   5294:   font: normal bold $sans;
                   5295: }
1.600     albertel 5296: 
1.614     albertel 5297: div.LC_edit_problem_header, 
                   5298: div.LC_edit_problem_footer {
1.600     albertel 5299:   font: normal medium $sans;
1.602     albertel 5300:   margin: 2px;
1.600     albertel 5301: }
                   5302: div.LC_edit_problem_header,
1.602     albertel 5303: div.LC_edit_problem_header div,
1.614     albertel 5304: div.LC_edit_problem_footer,
                   5305: div.LC_edit_problem_footer div,
1.602     albertel 5306: div.LC_edit_problem_editxml_header,
                   5307: div.LC_edit_problem_editxml_header div {
1.600     albertel 5308:   margin-top: 5px;
                   5309: }
1.602     albertel 5310: div.LC_edit_problem_header_edit_row {
                   5311:   background: $tabbg;
                   5312:   padding: 3px;
                   5313:   margin-bottom: 5px;
                   5314: }
1.600     albertel 5315: div.LC_edit_problem_header_title {
1.602     albertel 5316:   font: larger bold $sans;
                   5317:   background: $tabbg;
                   5318:   padding: 3px;
                   5319: }
                   5320: table.LC_edit_problem_header_title {
                   5321:   font: larger bold $sans;
                   5322:   width: 100%;
                   5323:   border-color: $pgbg;
                   5324:   border-style: solid;
                   5325:   border-width: $border;
                   5326: 
1.600     albertel 5327:   background: $tabbg;
1.602     albertel 5328:   border-collapse: collapse;
                   5329:   padding: 0px
                   5330: }
                   5331: 
                   5332: div.LC_edit_problem_discards {
                   5333:   float: left;
                   5334:   padding-bottom: 5px;
                   5335: }
                   5336: div.LC_edit_problem_saves {
                   5337:   float: right;
                   5338:   padding-bottom: 5px;
1.600     albertel 5339: }
                   5340: hr.LC_edit_problem_divide {
1.602     albertel 5341:   clear: both;
1.600     albertel 5342:   color: $tabbg;
                   5343:   background-color: $tabbg;
                   5344:   height: 3px;
                   5345:   border: 0px;
                   5346: }
1.343     albertel 5347: END
                   5348: }
                   5349: 
1.306     albertel 5350: =pod
                   5351: 
                   5352: =item * &headtag()
                   5353: 
                   5354: Returns a uniform footer for LON-CAPA web pages.
                   5355: 
1.307     albertel 5356: Inputs: $title - optional title for the head
                   5357:         $head_extra - optional extra HTML to put inside the <head>
1.315     albertel 5358:         $args - optional arguments
1.319     albertel 5359:             force_register - if is true call registerurl so the remote is 
                   5360:                              informed
1.415     albertel 5361:             redirect       -> array ref of
                   5362:                                    1- seconds before redirect occurs
                   5363:                                    2- url to redirect to
                   5364:                                    3- whether the side effect should occur
1.315     albertel 5365:                            (side effect of setting 
                   5366:                                $env{'internal.head.redirect'} to the url 
                   5367:                                redirected too)
1.352     albertel 5368:             domain         -> force to color decorate a page for a specific
                   5369:                                domain
                   5370:             function       -> force usage of a specific rolish color scheme
                   5371:             bgcolor        -> override the default page bgcolor
1.460     albertel 5372:             no_auto_mt_title
                   5373:                            -> prevent &mt()ing the title arg
1.464     albertel 5374: 
1.306     albertel 5375: =cut
                   5376: 
                   5377: sub headtag {
1.313     albertel 5378:     my ($title,$head_extra,$args) = @_;
1.306     albertel 5379:     
1.363     albertel 5380:     my $function = $args->{'function'} || &get_users_function();
                   5381:     my $domain   = $args->{'domain'}   || &determinedomain();
                   5382:     my $bgcolor  = $args->{'bgcolor'}  || &designparm($function.'.pgbg',$domain);
1.418     albertel 5383:     my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458     albertel 5384: 		   $Apache::lonnet::perlvar{'lonVersion'},
1.531     albertel 5385: 		   #time(),
1.418     albertel 5386: 		   $env{'environment.color.timestamp'},
1.363     albertel 5387: 		   $function,$domain,$bgcolor);
                   5388: 
1.369     www      5389:     $url = '/adm/css/'.&escape($url).'.css';
1.363     albertel 5390: 
1.308     albertel 5391:     my $result =
                   5392: 	'<head>'.
1.461     albertel 5393: 	&font_settings();
1.319     albertel 5394: 
1.461     albertel 5395:     if (!$args->{'frameset'}) {
                   5396: 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
                   5397:     }
1.319     albertel 5398:     if ($args->{'force_register'}) {
                   5399: 	$result .= &Apache::lonmenu::registerurl(1);
                   5400:     }
1.436     albertel 5401:     if (!$args->{'no_nav_bar'} 
                   5402: 	&& !$args->{'only_body'}
                   5403: 	&& !$args->{'frameset'}) {
                   5404: 	$result .= &help_menu_js();
                   5405:     }
1.319     albertel 5406: 
1.314     albertel 5407:     if (ref($args->{'redirect'})) {
1.414     albertel 5408: 	my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315     albertel 5409: 	$url = &Apache::lonenc::check_encrypt($url);
1.414     albertel 5410: 	if (!$inhibit_continue) {
                   5411: 	    $env{'internal.head.redirect'} = $url;
                   5412: 	}
1.313     albertel 5413: 	$result.=<<ADDMETA
                   5414: <meta http-equiv="pragma" content="no-cache" />
1.344     albertel 5415: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313     albertel 5416: ADDMETA
                   5417:     }
1.306     albertel 5418:     if (!defined($title)) {
                   5419: 	$title = 'The LearningOnline Network with CAPA';
                   5420:     }
1.460     albertel 5421:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
                   5422:     $result .= '<title> LON-CAPA '.$title.'</title>'
1.414     albertel 5423: 	.'<link rel="stylesheet" type="text/css" href="'.$url.'" />'
                   5424: 	.$head_extra;
1.306     albertel 5425:     return $result;
                   5426: }
                   5427: 
                   5428: =pod
                   5429: 
1.340     albertel 5430: =item * &font_settings()
                   5431: 
                   5432: Returns neccessary <meta> to set the proper encoding
                   5433: 
                   5434: Inputs: none
                   5435: 
                   5436: =cut
                   5437: 
                   5438: sub font_settings {
                   5439:     my $headerstring='';
1.647     www      5440:     if (!$env{'browser.mathml'} && $env{'browser.unicode'}) {
1.340     albertel 5441: 	$headerstring.=
                   5442: 	    '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
                   5443:     }
                   5444:     return $headerstring;
                   5445: }
                   5446: 
1.341     albertel 5447: =pod
                   5448: 
                   5449: =item * &xml_begin()
                   5450: 
                   5451: Returns the needed doctype and <html>
                   5452: 
                   5453: Inputs: none
                   5454: 
                   5455: =cut
                   5456: 
                   5457: sub xml_begin {
                   5458:     my $output='';
                   5459: 
1.592     albertel 5460:     if ($env{'internal.start_page'}==1) {
                   5461: 	&Apache::lonhtmlcommon::init_htmlareafields();
                   5462:     }
1.342     albertel 5463: 
1.341     albertel 5464:     if ($env{'browser.mathml'}) {
                   5465: 	$output='<?xml version="1.0"?>'
                   5466:             #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
                   5467: #            .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
                   5468:             
                   5469: #	    .'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [<!ENTITY mathns "http://www.w3.org/1998/Math/MathML">] >'
                   5470: 	    .'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">'
                   5471:             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" ' 
                   5472: 	    .'xmlns="http://www.w3.org/1999/xhtml">';
                   5473:     } else {
                   5474: 	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>';
                   5475:     }
                   5476:     return $output;
                   5477: }
1.340     albertel 5478: 
                   5479: =pod
                   5480: 
1.306     albertel 5481: =item * &endheadtag()
                   5482: 
                   5483: Returns a uniform </head> for LON-CAPA web pages.
                   5484: 
                   5485: Inputs: none
                   5486: 
                   5487: =cut
                   5488: 
                   5489: sub endheadtag {
                   5490:     return '</head>';
                   5491: }
                   5492: 
                   5493: =pod
                   5494: 
                   5495: =item * &head()
                   5496: 
                   5497: Returns a uniform complete <head>..</head> section for LON-CAPA web pages.
                   5498: 
1.648     raeburn  5499: Inputs:
                   5500: 
                   5501: =over 4
                   5502: 
                   5503: $title - optional title for the page
                   5504: 
                   5505: $head_extra - optional extra HTML to put inside the <head>
                   5506: 
                   5507: =back
1.405     albertel 5508: 
1.306     albertel 5509: =cut
                   5510: 
                   5511: sub head {
1.325     albertel 5512:     my ($title,$head_extra,$args) = @_;
                   5513:     return &headtag($title,$head_extra,$args).&endheadtag();
1.306     albertel 5514: }
                   5515: 
                   5516: =pod
                   5517: 
                   5518: =item * &start_page()
                   5519: 
                   5520: Returns a complete <html> .. <body> section for LON-CAPA web pages.
                   5521: 
1.648     raeburn  5522: Inputs:
                   5523: 
                   5524: =over 4
                   5525: 
                   5526: $title - optional title for the page
                   5527: 
                   5528: $head_extra - optional extra HTML to incude inside the <head>
                   5529: 
                   5530: $args - additional optional args supported are:
                   5531: 
                   5532: =over 8
                   5533: 
                   5534:              only_body      -> is true will set &bodytag() onlybodytag
1.317     albertel 5535:                                     arg on
1.648     raeburn  5536:              no_nav_bar     -> is true will set &bodytag() notopbar arg on
                   5537:              add_entries    -> additional attributes to add to the  <body>
                   5538:              domain         -> force to color decorate a page for a 
1.317     albertel 5539:                                     specific domain
1.648     raeburn  5540:              function       -> force usage of a specific rolish color
1.317     albertel 5541:                                     scheme
1.648     raeburn  5542:              redirect       -> see &headtag()
                   5543:              bgcolor        -> override the default page bg color
                   5544:              js_ready       -> return a string ready for being used in 
1.317     albertel 5545:                                     a javascript writeln
1.648     raeburn  5546:              html_encode    -> return a string ready for being used in 
1.320     albertel 5547:                                     a html attribute
1.648     raeburn  5548:              force_register -> if is true will turn on the &bodytag()
1.317     albertel 5549:                                     $forcereg arg
1.648     raeburn  5550:              body_title     -> alternate text to use instead of $title
1.326     albertel 5551:                                     in the title box that appears, this text
                   5552:                                     is not auto translated like the $title is
1.648     raeburn  5553:              frameset       -> if true will start with a <frameset>
1.330     albertel 5554:                                     rather than <body>
1.648     raeburn  5555:              no_title       -> if true the title bar won't be shown
                   5556:              skip_phases    -> hash ref of 
1.338     albertel 5557:                                     head -> skip the <html><head> generation
                   5558:                                     body -> skip all <body> generation
1.648     raeburn  5559:              no_inline_link -> if true and in remote mode, don't show the 
1.361     albertel 5560:                                     'Switch To Inline Menu' link
1.648     raeburn  5561:              no_auto_mt_title -> prevent &mt()ing the title arg
                   5562:              inherit_jsmath -> when creating popup window in a page,
                   5563:                                     should it have jsmath forced on by the
                   5564:                                     current page
1.361     albertel 5565: 
1.648     raeburn  5566: =back
1.460     albertel 5567: 
1.648     raeburn  5568: =back
1.562     albertel 5569: 
1.306     albertel 5570: =cut
                   5571: 
                   5572: sub start_page {
1.309     albertel 5573:     my ($title,$head_extra,$args) = @_;
1.318     albertel 5574:     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.313     albertel 5575:     my %head_args;
1.352     albertel 5576:     foreach my $arg ('redirect','force_register','domain','function',
1.460     albertel 5577: 		     'bgcolor','frameset','no_nav_bar','only_body',
                   5578: 		     'no_auto_mt_title') {
1.319     albertel 5579: 	if (defined($args->{$arg})) {
1.324     raeburn  5580: 	    $head_args{$arg} = $args->{$arg};
1.319     albertel 5581: 	}
1.313     albertel 5582:     }
1.319     albertel 5583: 
1.315     albertel 5584:     $env{'internal.start_page'}++;
1.338     albertel 5585:     my $result;
                   5586:     if (! exists($args->{'skip_phases'}{'head'}) ) {
                   5587: 	$result.=
1.341     albertel 5588: 	    &xml_begin().
1.338     albertel 5589: 	    &headtag($title,$head_extra,\%head_args).&endheadtag();
                   5590:     }
                   5591:     
                   5592:     if (! exists($args->{'skip_phases'}{'body'}) ) {
                   5593: 	if ($args->{'frameset'}) {
                   5594: 	    my $attr_string = &make_attr_string($args->{'force_register'},
                   5595: 						$args->{'add_entries'});
                   5596: 	    $result .= "\n<frameset $attr_string>\n";
                   5597: 	} else {
                   5598: 	    $result .=
                   5599: 		&bodytag($title, 
                   5600: 			 $args->{'function'},       $args->{'add_entries'},
                   5601: 			 $args->{'only_body'},      $args->{'domain'},
                   5602: 			 $args->{'force_register'}, $args->{'body_title'},
                   5603: 			 $args->{'no_nav_bar'},     $args->{'bgcolor'},
1.460     albertel 5604: 			 $args->{'no_title'},       $args->{'no_inline_link'},
                   5605: 			 $args);
1.338     albertel 5606: 	}
1.330     albertel 5607:     }
1.338     albertel 5608: 
1.315     albertel 5609:     if ($args->{'js_ready'}) {
1.317     albertel 5610: 	$result = &js_ready($result);
1.315     albertel 5611:     }
1.320     albertel 5612:     if ($args->{'html_encode'}) {
                   5613: 	$result = &html_encode($result);
                   5614:     }
1.315     albertel 5615:     return $result;
1.306     albertel 5616: }
                   5617: 
1.330     albertel 5618: 
1.306     albertel 5619: =pod
                   5620: 
                   5621: =item * &head()
                   5622: 
                   5623: Returns a complete </body></html> section for LON-CAPA web pages.
                   5624: 
1.315     albertel 5625: Inputs:         $args - additional optional args supported are:
                   5626:                  js_ready     -> return a string ready for being used in 
                   5627:                                  a javascript writeln
1.320     albertel 5628:                  html_encode  -> return a string ready for being used in 
                   5629:                                  a html attribute
1.330     albertel 5630:                  frameset     -> if true will start with a <frameset>
                   5631:                                  rather than <body>
1.493     albertel 5632:                  dicsussion   -> if true will get discussion from
                   5633:                                   lonxml::xmlend
                   5634:                                  (you can pass the target and parser arguments
                   5635:                                   through optional 'target' and 'parser' args
                   5636:                                   to this routine)
1.306     albertel 5637: 
                   5638: =cut
                   5639: 
                   5640: sub end_page {
1.315     albertel 5641:     my ($args) = @_;
                   5642:     $env{'internal.end_page'}++;
1.330     albertel 5643:     my $result;
1.335     albertel 5644:     if ($args->{'discussion'}) {
                   5645: 	my ($target,$parser);
                   5646: 	if (ref($args->{'discussion'})) {
                   5647: 	    ($target,$parser) =($args->{'discussion'}{'target'},
                   5648: 				$args->{'discussion'}{'parser'});
                   5649: 	}
                   5650: 	$result .= &Apache::lonxml::xmlend($target,$parser);
                   5651:     }
                   5652: 
1.330     albertel 5653:     if ($args->{'frameset'}) {
                   5654: 	$result .= '</frameset>';
                   5655:     } else {
1.635     raeburn  5656: 	$result .= &endbodytag($args);
1.330     albertel 5657:     }
                   5658:     $result .= "\n</html>";
                   5659: 
1.315     albertel 5660:     if ($args->{'js_ready'}) {
1.317     albertel 5661: 	$result = &js_ready($result);
1.315     albertel 5662:     }
1.335     albertel 5663: 
1.320     albertel 5664:     if ($args->{'html_encode'}) {
                   5665: 	$result = &html_encode($result);
                   5666:     }
1.335     albertel 5667: 
1.315     albertel 5668:     return $result;
                   5669: }
                   5670: 
1.320     albertel 5671: sub html_encode {
                   5672:     my ($result) = @_;
                   5673: 
1.322     albertel 5674:     $result = &HTML::Entities::encode($result,'<>&"');
1.320     albertel 5675:     
                   5676:     return $result;
                   5677: }
1.317     albertel 5678: sub js_ready {
                   5679:     my ($result) = @_;
                   5680: 
1.323     albertel 5681:     $result =~ s/[\n\r]/ /xmsg;
                   5682:     $result =~ s/\\/\\\\/xmsg;
                   5683:     $result =~ s/'/\\'/xmsg;
1.372     albertel 5684:     $result =~ s{</}{<\\/}xmsg;
1.317     albertel 5685:     
                   5686:     return $result;
                   5687: }
                   5688: 
1.315     albertel 5689: sub validate_page {
                   5690:     if (  exists($env{'internal.start_page'})
1.316     albertel 5691: 	  &&     $env{'internal.start_page'} > 1) {
                   5692: 	&Apache::lonnet::logthis('start_page called multiple times '.
1.318     albertel 5693: 				 $env{'internal.start_page'}.' '.
1.316     albertel 5694: 				 $ENV{'request.filename'});
1.315     albertel 5695:     }
                   5696:     if (  exists($env{'internal.end_page'})
1.316     albertel 5697: 	  &&     $env{'internal.end_page'} > 1) {
                   5698: 	&Apache::lonnet::logthis('end_page called multiple times '.
1.318     albertel 5699: 				 $env{'internal.end_page'}.' '.
1.316     albertel 5700: 				 $env{'request.filename'});
1.315     albertel 5701:     }
                   5702:     if (     exists($env{'internal.start_page'})
                   5703: 	&& ! exists($env{'internal.end_page'})) {
1.316     albertel 5704: 	&Apache::lonnet::logthis('start_page called without end_page '.
                   5705: 				 $env{'request.filename'});
1.315     albertel 5706:     }
                   5707:     if (   ! exists($env{'internal.start_page'})
                   5708: 	&&   exists($env{'internal.end_page'})) {
1.316     albertel 5709: 	&Apache::lonnet::logthis('end_page called without start_page'.
                   5710: 				 $env{'request.filename'});
1.315     albertel 5711:     }
1.306     albertel 5712: }
1.315     albertel 5713: 
1.318     albertel 5714: sub simple_error_page {
                   5715:     my ($r,$title,$msg) = @_;
                   5716:     my $page =
                   5717: 	&Apache::loncommon::start_page($title).
                   5718: 	&mt($msg).
                   5719: 	&Apache::loncommon::end_page();
                   5720:     if (ref($r)) {
                   5721: 	$r->print($page);
1.327     albertel 5722: 	return;
1.318     albertel 5723:     }
                   5724:     return $page;
                   5725: }
1.347     albertel 5726: 
                   5727: {
1.610     albertel 5728:     my @row_count;
1.347     albertel 5729:     sub start_data_table {
1.422     albertel 5730: 	my ($add_class) = @_;
                   5731: 	my $css_class = (join(' ','LC_data_table',$add_class));
1.610     albertel 5732: 	unshift(@row_count,0);
1.422     albertel 5733: 	return '<table class="'.$css_class.'">'."\n";
1.347     albertel 5734:     }
                   5735: 
                   5736:     sub end_data_table {
1.610     albertel 5737: 	shift(@row_count);
1.389     albertel 5738: 	return '</table>'."\n";;
1.347     albertel 5739:     }
                   5740: 
                   5741:     sub start_data_table_row {
1.422     albertel 5742: 	my ($add_class) = @_;
1.610     albertel 5743: 	$row_count[0]++;
                   5744: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.428     albertel 5745: 	$css_class = (join(' ',$css_class,$add_class));
1.422     albertel 5746: 	return  '<tr class="'.$css_class.'">'."\n";;
1.347     albertel 5747:     }
1.471     banghart 5748:     
                   5749:     sub continue_data_table_row {
                   5750: 	my ($add_class) = @_;
1.610     albertel 5751: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.471     banghart 5752: 	$css_class = (join(' ',$css_class,$add_class));
                   5753: 	return  '<tr class="'.$css_class.'">'."\n";;
                   5754:     }
1.347     albertel 5755: 
                   5756:     sub end_data_table_row {
1.389     albertel 5757: 	return '</tr>'."\n";;
1.347     albertel 5758:     }
1.367     www      5759: 
1.421     albertel 5760:     sub start_data_table_empty_row {
1.610     albertel 5761: 	$row_count[0]++;
1.421     albertel 5762: 	return  '<tr class="LC_empty_row" >'."\n";;
                   5763:     }
                   5764: 
                   5765:     sub end_data_table_empty_row {
                   5766: 	return '</tr>'."\n";;
                   5767:     }
                   5768: 
1.367     www      5769:     sub start_data_table_header_row {
1.389     albertel 5770: 	return  '<tr class="LC_header_row">'."\n";;
1.367     www      5771:     }
                   5772: 
                   5773:     sub end_data_table_header_row {
1.389     albertel 5774: 	return '</tr>'."\n";;
1.367     www      5775:     }
1.347     albertel 5776: }
                   5777: 
1.548     albertel 5778: =pod
                   5779: 
                   5780: =item * &inhibit_menu_check($arg)
                   5781: 
                   5782: Checks for a inhibitmenu state and generates output to preserve it
                   5783: 
                   5784: Inputs:         $arg - can be any of
                   5785:                      - undef - in which case the return value is a string 
                   5786:                                to add  into arguments list of a uri
                   5787:                      - 'input' - in which case the return value is a HTML
                   5788:                                  <form> <input> field of type hidden to
                   5789:                                  preserve the value
                   5790:                      - a url - in which case the return value is the url with
                   5791:                                the neccesary cgi args added to preserve the
                   5792:                                inhibitmenu state
                   5793:                      - a ref to a url - no return value, but the string is
                   5794:                                         updated to include the neccessary cgi
                   5795:                                         args to preserve the inhibitmenu state
                   5796: 
                   5797: =cut
                   5798: 
                   5799: sub inhibit_menu_check {
                   5800:     my ($arg) = @_;
                   5801:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   5802:     if ($arg eq 'input') {
                   5803: 	if ($env{'form.inhibitmenu'}) {
                   5804: 	    return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
                   5805: 	} else {
                   5806: 	    return
                   5807: 	}
                   5808:     }
                   5809:     if ($env{'form.inhibitmenu'}) {
                   5810: 	if (ref($arg)) {
                   5811: 	    $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   5812: 	} elsif ($arg eq '') {
                   5813: 	    $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
                   5814: 	} else {
                   5815: 	    $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   5816: 	}
                   5817:     }
                   5818:     if (!ref($arg)) {
                   5819: 	return $arg;
                   5820:     }
                   5821: }
                   5822: 
1.251     albertel 5823: ###############################################
1.182     matthew  5824: 
                   5825: =pod
                   5826: 
1.549     albertel 5827: =back
                   5828: 
                   5829: =head1 User Information Routines
                   5830: 
                   5831: =over 4
                   5832: 
1.405     albertel 5833: =item * &get_users_function()
1.182     matthew  5834: 
                   5835: Used by &bodytag to determine the current users primary role.
                   5836: Returns either 'student','coordinator','admin', or 'author'.
                   5837: 
                   5838: =cut
                   5839: 
                   5840: ###############################################
                   5841: sub get_users_function {
                   5842:     my $function = 'student';
1.258     albertel 5843:     if ($env{'request.role'}=~/^(cc|in|ta|ep)/) {
1.182     matthew  5844:         $function='coordinator';
                   5845:     }
1.258     albertel 5846:     if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182     matthew  5847:         $function='admin';
                   5848:     }
1.258     albertel 5849:     if (($env{'request.role'}=~/^(au|ca)/) ||
1.182     matthew  5850:         ($ENV{'REQUEST_URI'}=~/^(\/priv|\~)/)) {
                   5851:         $function='author';
                   5852:     }
                   5853:     return $function;
1.54      www      5854: }
1.99      www      5855: 
                   5856: ###############################################
                   5857: 
1.233     raeburn  5858: =pod
                   5859: 
1.542     raeburn  5860: =item * &check_user_status()
1.274     raeburn  5861: 
                   5862: Determines current status of supplied role for a
                   5863: specific user. Roles can be active, previous or future.
                   5864: 
                   5865: Inputs: 
                   5866: user's domain, user's username, course's domain,
1.375     raeburn  5867: course's number, optional section ID.
1.274     raeburn  5868: 
                   5869: Outputs:
                   5870: role status: active, previous or future. 
                   5871: 
                   5872: =cut
                   5873: 
                   5874: sub check_user_status {
1.412     raeburn  5875:     my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.274     raeburn  5876:     my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
                   5877:     my @uroles = keys %userinfo;
                   5878:     my $srchstr;
                   5879:     my $active_chk = 'none';
1.412     raeburn  5880:     my $now = time;
1.274     raeburn  5881:     if (@uroles > 0) {
1.412     raeburn  5882:         if (($role eq 'cc') || ($sec eq '') || (!defined($sec))) {
1.274     raeburn  5883:             $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
                   5884:         } else {
1.412     raeburn  5885:             $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
                   5886:         }
                   5887:         if (grep/^\Q$srchstr\E$/,@uroles) {
1.274     raeburn  5888:             my $role_end = 0;
                   5889:             my $role_start = 0;
                   5890:             $active_chk = 'active';
1.412     raeburn  5891:             if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
                   5892:                 $role_end = $1;
                   5893:                 if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
                   5894:                     $role_start = $1;
1.274     raeburn  5895:                 }
                   5896:             }
                   5897:             if ($role_start > 0) {
1.412     raeburn  5898:                 if ($now < $role_start) {
1.274     raeburn  5899:                     $active_chk = 'future';
                   5900:                 }
                   5901:             }
                   5902:             if ($role_end > 0) {
1.412     raeburn  5903:                 if ($now > $role_end) {
1.274     raeburn  5904:                     $active_chk = 'previous';
                   5905:                 }
                   5906:             }
                   5907:         }
                   5908:     }
                   5909:     return $active_chk;
                   5910: }
                   5911: 
                   5912: ###############################################
                   5913: 
                   5914: =pod
                   5915: 
1.405     albertel 5916: =item * &get_sections()
1.233     raeburn  5917: 
                   5918: Determines all the sections for a course including
                   5919: sections with students and sections containing other roles.
1.419     raeburn  5920: Incoming parameters: 
                   5921: 
                   5922: 1. domain
                   5923: 2. course number 
                   5924: 3. reference to array containing roles for which sections should 
                   5925: be gathered (optional).
                   5926: 4. reference to array containing status types for which sections 
                   5927: should be gathered (optional).
                   5928: 
                   5929: If the third argument is undefined, sections are gathered for any role. 
                   5930: If the fourth argument is undefined, sections are gathered for any status.
                   5931: Permissible values are 'active' or 'future' or 'previous'.
1.233     raeburn  5932:  
1.374     raeburn  5933: Returns section hash (keys are section IDs, values are
                   5934: number of users in each section), subject to the
1.419     raeburn  5935: optional roles filter, optional status filter 
1.233     raeburn  5936: 
                   5937: =cut
                   5938: 
                   5939: ###############################################
                   5940: sub get_sections {
1.419     raeburn  5941:     my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366     albertel 5942:     if (!defined($cdom) || !defined($cnum)) {
                   5943:         my $cid =  $env{'request.course.id'};
                   5944: 
                   5945: 	return if (!defined($cid));
                   5946: 
                   5947:         $cdom = $env{'course.'.$cid.'.domain'};
                   5948:         $cnum = $env{'course.'.$cid.'.num'};
                   5949:     }
                   5950: 
                   5951:     my %sectioncount;
1.419     raeburn  5952:     my $now = time;
1.240     albertel 5953: 
1.366     albertel 5954:     if (!defined($possible_roles) || (grep(/^st$/,@$possible_roles))) {
1.276     albertel 5955: 	my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240     albertel 5956: 	my $sec_index = &Apache::loncoursedata::CL_SECTION();
                   5957: 	my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419     raeburn  5958:         my $start_index = &Apache::loncoursedata::CL_START();
                   5959:         my $end_index = &Apache::loncoursedata::CL_END();
                   5960:         my $status;
1.366     albertel 5961: 	while (my ($student,$data) = each(%$classlist)) {
1.419     raeburn  5962: 	    my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
                   5963: 				                     $data->[$status_index],
                   5964:                                                      $data->[$start_index],
                   5965:                                                      $data->[$end_index]);
                   5966:             if ($stu_status eq 'Active') {
                   5967:                 $status = 'active';
                   5968:             } elsif ($end < $now) {
                   5969:                 $status = 'previous';
                   5970:             } elsif ($start > $now) {
                   5971:                 $status = 'future';
                   5972:             } 
                   5973: 	    if ($section ne '-1' && $section !~ /^\s*$/) {
                   5974:                 if ((!defined($possible_status)) || (($status ne '') && 
                   5975:                     (grep/^\Q$status\E$/,@{$possible_status}))) { 
                   5976: 		    $sectioncount{$section}++;
                   5977:                 }
1.240     albertel 5978: 	    }
                   5979: 	}
                   5980:     }
                   5981:     my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   5982:     foreach my $user (sort(keys(%courseroles))) {
                   5983: 	if ($user !~ /^(\w{2})/) { next; }
                   5984: 	my ($role) = ($user =~ /^(\w{2})/);
                   5985: 	if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419     raeburn  5986: 	my ($section,$status);
1.240     albertel 5987: 	if ($role eq 'cr' &&
                   5988: 	    $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
                   5989: 	    $section=$1;
                   5990: 	}
                   5991: 	if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
                   5992: 	if (!defined($section) || $section eq '-1') { next; }
1.419     raeburn  5993:         my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
                   5994:         if ($end == -1 && $start == -1) {
                   5995:             next; #deleted role
                   5996:         }
                   5997:         if (!defined($possible_status)) { 
                   5998:             $sectioncount{$section}++;
                   5999:         } else {
                   6000:             if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
                   6001:                 $status = 'active';
                   6002:             } elsif ($end < $now) {
                   6003:                 $status = 'future';
                   6004:             } elsif ($start > $now) {
                   6005:                 $status = 'previous';
                   6006:             }
                   6007:             if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
                   6008:                 $sectioncount{$section}++;
                   6009:             }
                   6010:         }
1.233     raeburn  6011:     }
1.366     albertel 6012:     return %sectioncount;
1.233     raeburn  6013: }
                   6014: 
1.274     raeburn  6015: ###############################################
1.294     raeburn  6016: 
                   6017: =pod
1.405     albertel 6018: 
                   6019: =item * &get_course_users()
                   6020: 
1.275     raeburn  6021: Retrieves usernames:domains for users in the specified course
                   6022: with specific role(s), and access status. 
                   6023: 
                   6024: Incoming parameters:
1.277     albertel 6025: 1. course domain
                   6026: 2. course number
                   6027: 3. access status: users must have - either active, 
1.275     raeburn  6028: previous, future, or all.
1.277     albertel 6029: 4. reference to array of permissible roles
1.288     raeburn  6030: 5. reference to array of section restrictions (optional)
                   6031: 6. reference to results object (hash of hashes).
                   6032: 7. reference to optional userdata hash
1.609     raeburn  6033: 8. reference to optional statushash
1.630     raeburn  6034: 9. flag if privileged users (except those set to unhide in
                   6035:    course settings) should be excluded    
1.609     raeburn  6036: Keys of top level results hash are roles.
1.275     raeburn  6037: Keys of inner hashes are username:domain, with 
                   6038: values set to access type.
1.288     raeburn  6039: Optional userdata hash returns an array with arguments in the 
                   6040: same order as loncoursedata::get_classlist() for student data.
                   6041: 
1.609     raeburn  6042: Optional statushash returns
                   6043: 
1.288     raeburn  6044: Entries for end, start, section and status are blank because
                   6045: of the possibility of multiple values for non-student roles.
                   6046: 
1.275     raeburn  6047: =cut
1.405     albertel 6048: 
1.275     raeburn  6049: ###############################################
1.405     albertel 6050: 
1.275     raeburn  6051: sub get_course_users {
1.630     raeburn  6052:     my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288     raeburn  6053:     my %idx = ();
1.419     raeburn  6054:     my %seclists;
1.288     raeburn  6055: 
                   6056:     $idx{udom} = &Apache::loncoursedata::CL_SDOM();
                   6057:     $idx{uname} =  &Apache::loncoursedata::CL_SNAME();
                   6058:     $idx{end} = &Apache::loncoursedata::CL_END();
                   6059:     $idx{start} = &Apache::loncoursedata::CL_START();
                   6060:     $idx{id} = &Apache::loncoursedata::CL_ID();
                   6061:     $idx{section} = &Apache::loncoursedata::CL_SECTION();
                   6062:     $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
                   6063:     $idx{status} = &Apache::loncoursedata::CL_STATUS();
                   6064: 
1.290     albertel 6065:     if (grep(/^st$/,@{$roles})) {
1.276     albertel 6066:         my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278     raeburn  6067:         my $now = time;
1.277     albertel 6068:         foreach my $student (keys(%{$classlist})) {
1.288     raeburn  6069:             my $match = 0;
1.412     raeburn  6070:             my $secmatch = 0;
1.419     raeburn  6071:             my $section = $$classlist{$student}[$idx{section}];
1.609     raeburn  6072:             my $status = $$classlist{$student}[$idx{status}];
1.419     raeburn  6073:             if ($section eq '') {
                   6074:                 $section = 'none';
                   6075:             }
1.291     albertel 6076:             if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 6077:                 if (grep(/^all$/,@{$sections})) {
1.412     raeburn  6078:                     $secmatch = 1;
                   6079:                 } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420     albertel 6080:                     if (grep(/^none$/,@{$sections})) {
1.412     raeburn  6081:                         $secmatch = 1;
                   6082:                     }
                   6083:                 } else {  
1.419     raeburn  6084: 		    if (grep(/^\Q$section\E$/,@{$sections})) {
1.412     raeburn  6085: 		        $secmatch = 1;
                   6086:                     }
1.290     albertel 6087: 		}
1.412     raeburn  6088:                 if (!$secmatch) {
                   6089:                     next;
                   6090:                 }
1.419     raeburn  6091:             }
1.275     raeburn  6092:             if (defined($$types{'active'})) {
1.288     raeburn  6093:                 if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275     raeburn  6094:                     push(@{$$users{st}{$student}},'active');
1.288     raeburn  6095:                     $match = 1;
1.275     raeburn  6096:                 }
                   6097:             }
                   6098:             if (defined($$types{'previous'})) {
1.609     raeburn  6099:                 if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275     raeburn  6100:                     push(@{$$users{st}{$student}},'previous');
1.288     raeburn  6101:                     $match = 1;
1.275     raeburn  6102:                 }
                   6103:             }
                   6104:             if (defined($$types{'future'})) {
1.609     raeburn  6105:                 if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275     raeburn  6106:                     push(@{$$users{st}{$student}},'future');
1.288     raeburn  6107:                     $match = 1;
1.275     raeburn  6108:                 }
                   6109:             }
1.609     raeburn  6110:             if ($match) {
                   6111:                 push(@{$seclists{$student}},$section);
                   6112:                 if (ref($userdata) eq 'HASH') {
                   6113:                     $$userdata{$student} = $$classlist{$student};
                   6114:                 }
                   6115:                 if (ref($statushash) eq 'HASH') {
                   6116:                     $statushash->{$student}{'st'}{$section} = $status;
                   6117:                 }
1.288     raeburn  6118:             }
1.275     raeburn  6119:         }
                   6120:     }
1.412     raeburn  6121:     if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439     raeburn  6122:         my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   6123:         my $now = time;
1.609     raeburn  6124:         my %displaystatus = ( previous => 'Expired',
                   6125:                               active   => 'Active',
                   6126:                               future   => 'Future',
                   6127:                             );
1.630     raeburn  6128:         my %nothide;
                   6129:         if ($hidepriv) {
                   6130:             my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
                   6131:             foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
                   6132:                 if ($user !~ /:/) {
                   6133:                     $nothide{join(':',split(/[\@]/,$user))}=1;
                   6134:                 } else {
                   6135:                     $nothide{$user} = 1;
                   6136:                 }
                   6137:             }
                   6138:         }
1.439     raeburn  6139:         foreach my $person (sort(keys(%coursepersonnel))) {
1.288     raeburn  6140:             my $match = 0;
1.412     raeburn  6141:             my $secmatch = 0;
1.439     raeburn  6142:             my $status;
1.412     raeburn  6143:             my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275     raeburn  6144:             $user =~ s/:$//;
1.439     raeburn  6145:             my ($end,$start) = split(/:/,$coursepersonnel{$person});
                   6146:             if ($end == -1 || $start == -1) {
                   6147:                 next;
                   6148:             }
                   6149:             if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
                   6150:                 (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412     raeburn  6151:                 my ($uname,$udom) = split(/:/,$user);
                   6152:                 if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 6153:                     if (grep(/^all$/,@{$sections})) {
1.412     raeburn  6154:                         $secmatch = 1;
                   6155:                     } elsif ($usec eq '') {
1.420     albertel 6156:                         if (grep(/^none$/,@{$sections})) {
1.412     raeburn  6157:                             $secmatch = 1;
                   6158:                         }
                   6159:                     } else {
                   6160:                         if (grep(/^\Q$usec\E$/,@{$sections})) {
                   6161:                             $secmatch = 1;
                   6162:                         }
                   6163:                     }
                   6164:                     if (!$secmatch) {
                   6165:                         next;
                   6166:                     }
1.288     raeburn  6167:                 }
1.419     raeburn  6168:                 if ($usec eq '') {
                   6169:                     $usec = 'none';
                   6170:                 }
1.275     raeburn  6171:                 if ($uname ne '' && $udom ne '') {
1.630     raeburn  6172:                     if ($hidepriv) {
                   6173:                         if ((&Apache::lonnet::privileged($uname,$udom)) &&
                   6174:                             (!$nothide{$uname.':'.$udom})) {
                   6175:                             next;
                   6176:                         }
                   6177:                     }
1.503     raeburn  6178:                     if ($end > 0 && $end < $now) {
1.439     raeburn  6179:                         $status = 'previous';
                   6180:                     } elsif ($start > $now) {
                   6181:                         $status = 'future';
                   6182:                     } else {
                   6183:                         $status = 'active';
                   6184:                     }
1.277     albertel 6185:                     foreach my $type (keys(%{$types})) { 
1.275     raeburn  6186:                         if ($status eq $type) {
1.420     albertel 6187:                             if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419     raeburn  6188:                                 push(@{$$users{$role}{$user}},$type);
                   6189:                             }
1.288     raeburn  6190:                             $match = 1;
                   6191:                         }
                   6192:                     }
1.419     raeburn  6193:                     if (($match) && (ref($userdata) eq 'HASH')) {
                   6194:                         if (!exists($$userdata{$uname.':'.$udom})) {
                   6195: 			    &get_user_info($udom,$uname,\%idx,$userdata);
                   6196:                         }
1.420     albertel 6197:                         if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419     raeburn  6198:                             push(@{$seclists{$uname.':'.$udom}},$usec);
                   6199:                         }
1.609     raeburn  6200:                         if (ref($statushash) eq 'HASH') {
                   6201:                             $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
                   6202:                         }
1.275     raeburn  6203:                     }
                   6204:                 }
                   6205:             }
                   6206:         }
1.290     albertel 6207:         if (grep(/^ow$/,@{$roles})) {
1.279     raeburn  6208:             if ((defined($cdom)) && (defined($cnum))) {
                   6209:                 my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
                   6210:                 if ( defined($csettings{'internal.courseowner'}) ) {
                   6211:                     my $owner = $csettings{'internal.courseowner'};
1.609     raeburn  6212:                     next if ($owner eq '');
                   6213:                     my ($ownername,$ownerdom);
                   6214:                     if ($owner =~ /^([^:]+):([^:]+)$/) {
                   6215:                         $ownername = $1;
                   6216:                         $ownerdom = $2;
                   6217:                     } else {
                   6218:                         $ownername = $owner;
                   6219:                         $ownerdom = $cdom;
                   6220:                         $owner = $ownername.':'.$ownerdom;
1.439     raeburn  6221:                     }
                   6222:                     @{$$users{'ow'}{$owner}} = 'any';
1.290     albertel 6223:                     if (defined($userdata) && 
1.609     raeburn  6224: 			!exists($$userdata{$owner})) {
                   6225: 			&get_user_info($ownerdom,$ownername,\%idx,$userdata);
                   6226:                         if (!grep(/^none$/,@{$seclists{$owner}})) {
                   6227:                             push(@{$seclists{$owner}},'none');
                   6228:                         }
                   6229:                         if (ref($statushash) eq 'HASH') {
                   6230:                             $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419     raeburn  6231:                         }
1.290     albertel 6232: 		    }
1.279     raeburn  6233:                 }
                   6234:             }
                   6235:         }
1.419     raeburn  6236:         foreach my $user (keys(%seclists)) {
                   6237:             @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
                   6238:             $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
                   6239:         }
1.275     raeburn  6240:     }
                   6241:     return;
                   6242: }
                   6243: 
1.288     raeburn  6244: sub get_user_info {
                   6245:     my ($udom,$uname,$idx,$userdata) = @_;
1.289     albertel 6246:     $$userdata{$uname.':'.$udom}[$$idx{fullname}] = 
                   6247: 	&plainname($uname,$udom,'lastname');
1.291     albertel 6248:     $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297     raeburn  6249:     $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609     raeburn  6250:     my %idhash =  &Apache::lonnet::idrget($udom,($uname));
                   6251:     $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname}; 
1.288     raeburn  6252:     return;
                   6253: }
1.275     raeburn  6254: 
1.472     raeburn  6255: ###############################################
                   6256: 
                   6257: =pod
                   6258: 
                   6259: =item * &get_user_quota()
                   6260: 
                   6261: Retrieves quota assigned for storage of portfolio files for a user  
                   6262: 
                   6263: Incoming parameters:
                   6264: 1. user's username
                   6265: 2. user's domain
                   6266: 
                   6267: Returns:
1.536     raeburn  6268: 1. Disk quota (in Mb) assigned to student.
                   6269: 2. (Optional) Type of setting: custom or default
                   6270:    (individually assigned or default for user's 
                   6271:    institutional status).
                   6272: 3. (Optional) - User's institutional status (e.g., faculty, staff
                   6273:    or student - types as defined in localenroll::inst_usertypes 
                   6274:    for user's domain, which determines default quota for user.
                   6275: 4. (Optional) - Default quota which would apply to the user.
1.472     raeburn  6276: 
                   6277: If a value has been stored in the user's environment, 
1.536     raeburn  6278: it will return that, otherwise it returns the maximal default
                   6279: defined for the user's instituional status(es) in the domain.
1.472     raeburn  6280: 
                   6281: =cut
                   6282: 
                   6283: ###############################################
                   6284: 
                   6285: 
                   6286: sub get_user_quota {
                   6287:     my ($uname,$udom) = @_;
1.536     raeburn  6288:     my ($quota,$quotatype,$settingstatus,$defquota);
1.472     raeburn  6289:     if (!defined($udom)) {
                   6290:         $udom = $env{'user.domain'};
                   6291:     }
                   6292:     if (!defined($uname)) {
                   6293:         $uname = $env{'user.name'};
                   6294:     }
                   6295:     if (($udom eq '' || $uname eq '') ||
                   6296:         ($udom eq 'public') && ($uname eq 'public')) {
                   6297:         $quota = 0;
1.536     raeburn  6298:         $quotatype = 'default';
                   6299:         $defquota = 0; 
1.472     raeburn  6300:     } else {
1.536     raeburn  6301:         my $inststatus;
1.472     raeburn  6302:         if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
                   6303:             $quota = $env{'environment.portfolioquota'};
1.536     raeburn  6304:             $inststatus = $env{'environment.inststatus'};
1.472     raeburn  6305:         } else {
1.536     raeburn  6306:             my %userenv = 
                   6307:                 &Apache::lonnet::get('environment',['portfolioquota',
                   6308:                                      'inststatus'],$udom,$uname);
1.472     raeburn  6309:             my ($tmp) = keys(%userenv);
                   6310:             if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   6311:                 $quota = $userenv{'portfolioquota'};
1.536     raeburn  6312:                 $inststatus = $userenv{'inststatus'};
1.472     raeburn  6313:             } else {
                   6314:                 undef(%userenv);
                   6315:             }
                   6316:         }
1.536     raeburn  6317:         ($defquota,$settingstatus) = &default_quota($udom,$inststatus);
1.472     raeburn  6318:         if ($quota eq '') {
1.536     raeburn  6319:             $quota = $defquota;
                   6320:             $quotatype = 'default';
                   6321:         } else {
                   6322:             $quotatype = 'custom';
1.472     raeburn  6323:         }
                   6324:     }
1.536     raeburn  6325:     if (wantarray) {
                   6326:         return ($quota,$quotatype,$settingstatus,$defquota);
                   6327:     } else {
                   6328:         return $quota;
                   6329:     }
1.472     raeburn  6330: }
                   6331: 
                   6332: ###############################################
                   6333: 
                   6334: =pod
                   6335: 
                   6336: =item * &default_quota()
                   6337: 
1.536     raeburn  6338: Retrieves default quota assigned for storage of user portfolio files,
                   6339: given an (optional) user's institutional status.
1.472     raeburn  6340: 
                   6341: Incoming parameters:
                   6342: 1. domain
1.536     raeburn  6343: 2. (Optional) institutional status(es).  This is a : separated list of 
                   6344:    status types (e.g., faculty, staff, student etc.)
                   6345:    which apply to the user for whom the default is being retrieved.
                   6346:    If the institutional status string in undefined, the domain
                   6347:    default quota will be returned. 
1.472     raeburn  6348: 
                   6349: Returns:
                   6350: 1. Default disk quota (in Mb) for user portfolios in the domain.
1.536     raeburn  6351: 2. (Optional) institutional type which determined the value of the
                   6352:    default quota.
1.472     raeburn  6353: 
                   6354: If a value has been stored in the domain's configuration db,
                   6355: it will return that, otherwise it returns 20 (for backwards 
                   6356: compatibility with domains which have not set up a configuration
                   6357: db file; the original statically defined portfolio quota was 20 Mb). 
                   6358: 
1.536     raeburn  6359: If the user's status includes multiple types (e.g., staff and student),
                   6360: the largest default quota which applies to the user determines the
                   6361: default quota returned.
                   6362: 
1.472     raeburn  6363: =cut
                   6364: 
                   6365: ###############################################
                   6366: 
                   6367: 
                   6368: sub default_quota {
1.536     raeburn  6369:     my ($udom,$inststatus) = @_;
                   6370:     my ($defquota,$settingstatus);
                   6371:     my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622     raeburn  6372:                                             ['quotas'],$udom);
                   6373:     if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536     raeburn  6374:         if ($inststatus ne '') {
                   6375:             my @statuses = split(/:/,$inststatus);
                   6376:             foreach my $item (@statuses) {
1.622     raeburn  6377:                 if ($quotahash{'quotas'}{$item} ne '') {
1.536     raeburn  6378:                     if ($defquota eq '') {
1.622     raeburn  6379:                         $defquota = $quotahash{'quotas'}{$item};
1.536     raeburn  6380:                         $settingstatus = $item;
1.622     raeburn  6381:                     } elsif ($quotahash{'quotas'}{$item} > $defquota) {
                   6382:                         $defquota = $quotahash{'quotas'}{$item};
1.536     raeburn  6383:                         $settingstatus = $item;
                   6384:                     }
                   6385:                 }
                   6386:             }
                   6387:         }
                   6388:         if ($defquota eq '') {
1.622     raeburn  6389:             $defquota = $quotahash{'quotas'}{'default'};
1.536     raeburn  6390:             $settingstatus = 'default';
                   6391:         }
                   6392:     } else {
                   6393:         $settingstatus = 'default';
                   6394:         $defquota = 20;
                   6395:     }
                   6396:     if (wantarray) {
                   6397:         return ($defquota,$settingstatus);
1.472     raeburn  6398:     } else {
1.536     raeburn  6399:         return $defquota;
1.472     raeburn  6400:     }
                   6401: }
                   6402: 
1.384     raeburn  6403: sub get_secgrprole_info {
                   6404:     my ($cdom,$cnum,$needroles,$type)  = @_;
                   6405:     my %sections_count = &get_sections($cdom,$cnum);
                   6406:     my @sections =  (sort {$a <=> $b} keys(%sections_count));
                   6407:     my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
                   6408:     my @groups = sort(keys(%curr_groups));
                   6409:     my $allroles = [];
                   6410:     my $rolehash;
                   6411:     my $accesshash = {
                   6412:                      active => 'Currently has access',
                   6413:                      future => 'Will have future access',
                   6414:                      previous => 'Previously had access',
                   6415:                   };
                   6416:     if ($needroles) {
                   6417:         $rolehash = {'all' => 'all'};
1.385     albertel 6418:         my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   6419: 	if (&Apache::lonnet::error(%user_roles)) {
                   6420: 	    undef(%user_roles);
                   6421: 	}
                   6422:         foreach my $item (keys(%user_roles)) {
1.384     raeburn  6423:             my ($role)=split(/\:/,$item,2);
                   6424:             if ($role eq 'cr') { next; }
                   6425:             if ($role =~ /^cr/) {
                   6426:                 $$rolehash{$role} = (split('/',$role))[3];
                   6427:             } else {
                   6428:                 $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
                   6429:             }
                   6430:         }
                   6431:         foreach my $key (sort(keys(%{$rolehash}))) {
                   6432:             push(@{$allroles},$key);
                   6433:         }
                   6434:         push (@{$allroles},'st');
                   6435:         $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
                   6436:     }
                   6437:     return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
                   6438: }
                   6439: 
1.555     raeburn  6440: sub user_picker {
1.627     raeburn  6441:     my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype) = @_;
1.555     raeburn  6442:     my $currdom = $dom;
                   6443:     my %curr_selected = (
                   6444:                         srchin => 'dom',
1.580     raeburn  6445:                         srchby => 'lastname',
1.555     raeburn  6446:                       );
                   6447:     my $srchterm;
1.625     raeburn  6448:     if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555     raeburn  6449:         if ($srch->{'srchby'} ne '') {
                   6450:             $curr_selected{'srchby'} = $srch->{'srchby'};
                   6451:         }
                   6452:         if ($srch->{'srchin'} ne '') {
                   6453:             $curr_selected{'srchin'} = $srch->{'srchin'};
                   6454:         }
                   6455:         if ($srch->{'srchtype'} ne '') {
                   6456:             $curr_selected{'srchtype'} = $srch->{'srchtype'};
                   6457:         }
                   6458:         if ($srch->{'srchdomain'} ne '') {
                   6459:             $currdom = $srch->{'srchdomain'};
                   6460:         }
                   6461:         $srchterm = $srch->{'srchterm'};
                   6462:     }
                   6463:     my %lt=&Apache::lonlocal::texthash(
1.573     raeburn  6464:                     'usr'       => 'Search criteria',
1.563     raeburn  6465:                     'doma'      => 'Domain/institution to search',
1.558     albertel 6466:                     'uname'     => 'username',
                   6467:                     'lastname'  => 'last name',
1.555     raeburn  6468:                     'lastfirst' => 'last name, first name',
1.558     albertel 6469:                     'crs'       => 'in this course',
1.576     raeburn  6470:                     'dom'       => 'in selected LON-CAPA domain', 
1.558     albertel 6471:                     'alc'       => 'all LON-CAPA',
1.573     raeburn  6472:                     'instd'     => 'in institutional directory for selected domain',
1.558     albertel 6473:                     'exact'     => 'is',
                   6474:                     'contains'  => 'contains',
1.569     raeburn  6475:                     'begins'    => 'begins with',
1.571     raeburn  6476:                     'youm'      => "You must include some text to search for.",
                   6477:                     'thte'      => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
                   6478:                     'thet'      => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
                   6479:                     'yomc'      => "You must choose a domain when using an institutional directory search.",
                   6480:                     'ymcd'      => "You must choose a domain when using a domain search.",
                   6481:                     'whus'      => "When using searching by last,first you must include a comma as separator between last name and first name.",
                   6482:                     'whse'      => "When searching by last,first you must include at least one character in the first name.",
                   6483:                      'thfo'     => "The following need to be corrected before the search can be run:",
1.555     raeburn  6484:                                        );
1.563     raeburn  6485:     my $domform = &select_dom_form($currdom,'srchdomain',1,1);
                   6486:     my $srchinsel = ' <select name="srchin">';
1.555     raeburn  6487: 
                   6488:     my @srchins = ('crs','dom','alc','instd');
                   6489: 
                   6490:     foreach my $option (@srchins) {
                   6491:         # FIXME 'alc' option unavailable until 
                   6492:         #       loncreateuser::print_user_query_page()
                   6493:         #       has been completed.
                   6494:         next if ($option eq 'alc');
                   6495:         next if ($option eq 'crs' && !$env{'request.course.id'});
1.563     raeburn  6496:         if ($curr_selected{'srchin'} eq $option) {
                   6497:             $srchinsel .= ' 
                   6498:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   6499:         } else {
                   6500:             $srchinsel .= '
                   6501:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   6502:         }
1.555     raeburn  6503:     }
1.563     raeburn  6504:     $srchinsel .= "\n  </select>\n";
1.555     raeburn  6505: 
                   6506:     my $srchbysel =  ' <select name="srchby">';
1.580     raeburn  6507:     foreach my $option ('lastname','lastfirst','uname') {
1.555     raeburn  6508:         if ($curr_selected{'srchby'} eq $option) {
                   6509:             $srchbysel .= '
                   6510:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   6511:         } else {
                   6512:             $srchbysel .= '
                   6513:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   6514:          }
                   6515:     }
                   6516:     $srchbysel .= "\n  </select>\n";
                   6517: 
                   6518:     my $srchtypesel = ' <select name="srchtype">';
1.580     raeburn  6519:     foreach my $option ('begins','contains','exact') {
1.555     raeburn  6520:         if ($curr_selected{'srchtype'} eq $option) {
                   6521:             $srchtypesel .= '
                   6522:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   6523:         } else {
                   6524:             $srchtypesel .= '
                   6525:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   6526:         }
                   6527:     }
                   6528:     $srchtypesel .= "\n  </select>\n";
                   6529: 
1.558     albertel 6530:     my ($newuserscript,$new_user_create);
1.556     raeburn  6531: 
                   6532:     if ($forcenewuser) {
1.576     raeburn  6533:         if (ref($srch) eq 'HASH') {
                   6534:             if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $env{'request.role.domain'}) {
1.627     raeburn  6535:                 if ($cancreate) {
                   6536:                     $new_user_create = '<p> <input type="submit" name="forcenew" value="'.&HTML::Entities::encode(&mt('Make new user "[_1]"',$srchterm),'<>&"').'" onclick="javascript:setSearch(\'1\','.$caller.');" /> </p>';
                   6537:                 } else {
                   6538:                     my $helplink = ' href="javascript:helpMenu('."'display'".')"';
                   6539:                     my %usertypetext = (
                   6540:                         official   => 'institutional',
                   6541:                         unofficial => 'non-institutional',
                   6542:                     );
                   6543:                     $new_user_create = '<br /><span class="LC_warning">'.&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.").' '.&mt('Contact the <a[_1]>helpdesk</a> for assistance.',$helplink).'</span><br /><br />';
                   6544:                 }
1.576     raeburn  6545:             }
                   6546:         }
                   6547: 
1.556     raeburn  6548:         $newuserscript = <<"ENDSCRIPT";
                   6549: 
1.570     raeburn  6550: function setSearch(createnew,callingForm) {
1.556     raeburn  6551:     if (createnew == 1) {
1.570     raeburn  6552:         for (var i=0; i<callingForm.srchby.length; i++) {
                   6553:             if (callingForm.srchby.options[i].value == 'uname') {
                   6554:                 callingForm.srchby.selectedIndex = i;
1.556     raeburn  6555:             }
                   6556:         }
1.570     raeburn  6557:         for (var i=0; i<callingForm.srchin.length; i++) {
                   6558:             if ( callingForm.srchin.options[i].value == 'dom') {
                   6559: 		callingForm.srchin.selectedIndex = i;
1.556     raeburn  6560:             }
                   6561:         }
1.570     raeburn  6562:         for (var i=0; i<callingForm.srchtype.length; i++) {
                   6563:             if (callingForm.srchtype.options[i].value == 'exact') {
                   6564:                 callingForm.srchtype.selectedIndex = i;
1.556     raeburn  6565:             }
                   6566:         }
1.570     raeburn  6567:         for (var i=0; i<callingForm.srchdomain.length; i++) {
                   6568:             if (callingForm.srchdomain.options[i].value == '$env{'request.role.domain'}') {
                   6569:                 callingForm.srchdomain.selectedIndex = i;
1.556     raeburn  6570:             }
                   6571:         }
                   6572:     }
                   6573: }
                   6574: ENDSCRIPT
1.558     albertel 6575: 
1.556     raeburn  6576:     }
                   6577: 
1.555     raeburn  6578:     my $output = <<"END_BLOCK";
1.556     raeburn  6579: <script type="text/javascript">
1.570     raeburn  6580: function validateEntry(callingForm) {
1.558     albertel 6581: 
1.556     raeburn  6582:     var checkok = 1;
1.558     albertel 6583:     var srchin;
1.570     raeburn  6584:     for (var i=0; i<callingForm.srchin.length; i++) {
                   6585: 	if ( callingForm.srchin[i].checked ) {
                   6586: 	    srchin = callingForm.srchin[i].value;
1.558     albertel 6587: 	}
                   6588:     }
                   6589: 
1.570     raeburn  6590:     var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
                   6591:     var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
                   6592:     var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
                   6593:     var srchterm =  callingForm.srchterm.value;
                   6594:     var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556     raeburn  6595:     var msg = "";
                   6596: 
                   6597:     if (srchterm == "") {
                   6598:         checkok = 0;
1.571     raeburn  6599:         msg += "$lt{'youm'}\\n";
1.556     raeburn  6600:     }
                   6601: 
1.569     raeburn  6602:     if (srchtype== 'begins') {
                   6603:         if (srchterm.length < 2) {
                   6604:             checkok = 0;
1.571     raeburn  6605:             msg += "$lt{'thte'}\\n";
1.569     raeburn  6606:         }
                   6607:     }
                   6608: 
1.556     raeburn  6609:     if (srchtype== 'contains') {
                   6610:         if (srchterm.length < 3) {
                   6611:             checkok = 0;
1.571     raeburn  6612:             msg += "$lt{'thet'}\\n";
1.556     raeburn  6613:         }
                   6614:     }
                   6615:     if (srchin == 'instd') {
                   6616:         if (srchdomain == '') {
                   6617:             checkok = 0;
1.571     raeburn  6618:             msg += "$lt{'yomc'}\\n";
1.556     raeburn  6619:         }
                   6620:     }
                   6621:     if (srchin == 'dom') {
                   6622:         if (srchdomain == '') {
                   6623:             checkok = 0;
1.571     raeburn  6624:             msg += "$lt{'ymcd'}\\n";
1.556     raeburn  6625:         }
                   6626:     }
                   6627:     if (srchby == 'lastfirst') {
                   6628:         if (srchterm.indexOf(",") == -1) {
                   6629:             checkok = 0;
1.571     raeburn  6630:             msg += "$lt{'whus'}\\n";
1.556     raeburn  6631:         }
                   6632:         if (srchterm.indexOf(",") == srchterm.length -1) {
                   6633:             checkok = 0;
1.571     raeburn  6634:             msg += "$lt{'whse'}\\n";
1.556     raeburn  6635:         }
                   6636:     }
                   6637:     if (checkok == 0) {
1.571     raeburn  6638:         alert("$lt{'thfo'}\\n"+msg);
1.556     raeburn  6639:         return;
                   6640:     }
                   6641:     if (checkok == 1) {
1.570     raeburn  6642:         callingForm.submit();
1.556     raeburn  6643:     }
                   6644: }
                   6645: 
                   6646: $newuserscript
                   6647: 
                   6648: </script>
1.558     albertel 6649: 
                   6650: $new_user_create
                   6651: 
1.555     raeburn  6652: <table>
1.558     albertel 6653:  <tr>
1.573     raeburn  6654:   <td>$lt{'doma'}:</td>
                   6655:   <td>$domform</td>
                   6656:   </td>
                   6657:  </tr>
                   6658:  <tr>
                   6659:   <td>$lt{'usr'}:</td>
1.563     raeburn  6660:   <td>$srchbysel
                   6661:       $srchtypesel 
                   6662:       <input type="text" size="15" name="srchterm" value="$srchterm" />
1.564     albertel 6663:       $srchinsel 
1.563     raeburn  6664:   </td>
                   6665:  </tr>
1.555     raeburn  6666: </table>
                   6667: <br />
                   6668: END_BLOCK
1.558     albertel 6669: 
1.555     raeburn  6670:     return $output;
                   6671: }
                   6672: 
1.612     raeburn  6673: sub user_rule_check {
1.615     raeburn  6674:     my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.612     raeburn  6675:     my $response;
                   6676:     if (ref($usershash) eq 'HASH') {
                   6677:         foreach my $user (keys(%{$usershash})) {
                   6678:             my ($uname,$udom) = split(/:/,$user);
                   6679:             next if ($udom eq '' || $uname eq '');
1.615     raeburn  6680:             my ($id,$newuser);
1.612     raeburn  6681:             if (ref($usershash->{$user}) eq 'HASH') {
1.615     raeburn  6682:                 $newuser = $usershash->{$user}->{'newuser'};
1.612     raeburn  6683:                 $id = $usershash->{$user}->{'id'};
                   6684:             }
                   6685:             my $inst_response;
                   6686:             if (ref($checks) eq 'HASH') {
                   6687:                 if (defined($checks->{'username'})) {
1.615     raeburn  6688:                     ($inst_response,%{$inst_results->{$user}}) = 
1.612     raeburn  6689:                         &Apache::lonnet::get_instuser($udom,$uname);
                   6690:                 } elsif (defined($checks->{'id'})) {
1.615     raeburn  6691:                     ($inst_response,%{$inst_results->{$user}}) =
1.612     raeburn  6692:                         &Apache::lonnet::get_instuser($udom,undef,$id);
                   6693:                 }
1.615     raeburn  6694:             } else {
                   6695:                 ($inst_response,%{$inst_results->{$user}}) =
                   6696:                     &Apache::lonnet::get_instuser($udom,$uname);
                   6697:                 return;
1.612     raeburn  6698:             }
1.615     raeburn  6699:             if (!$got_rules->{$udom}) {
1.612     raeburn  6700:                 my %domconfig = &Apache::lonnet::get_dom('configuration',
                   6701:                                                   ['usercreation'],$udom);
                   6702:                 if (ref($domconfig{'usercreation'}) eq 'HASH') {
1.615     raeburn  6703:                     foreach my $item ('username','id') {
1.612     raeburn  6704:                         if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
                   6705:                             $$curr_rules{$udom}{$item} = 
                   6706:                                 $domconfig{'usercreation'}{$item.'_rule'};
1.585     raeburn  6707:                         }
                   6708:                     }
                   6709:                 }
1.615     raeburn  6710:                 $got_rules->{$udom} = 1;  
1.585     raeburn  6711:             }
1.612     raeburn  6712:             foreach my $item (keys(%{$checks})) {
                   6713:                 if (ref($$curr_rules{$udom}) eq 'HASH') {
                   6714:                     if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
                   6715:                         if (@{$$curr_rules{$udom}{$item}} > 0) {
                   6716:                             my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,$$curr_rules{$udom}{$item});
                   6717:                             foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
                   6718:                                 if ($rule_check{$rule}) {
                   6719:                                     $$rulematch{$user}{$item} = $rule;
                   6720:                                     if ($inst_response eq 'ok') {
1.615     raeburn  6721:                                         if (ref($inst_results) eq 'HASH') {
                   6722:                                             if (ref($inst_results->{$user}) eq 'HASH') {
                   6723:                                                 if (keys(%{$inst_results->{$user}}) == 0) {
                   6724:                                                     $$alerts{$item}{$udom}{$uname} = 1;
                   6725:                                                 }
1.612     raeburn  6726:                                             }
                   6727:                                         }
1.615     raeburn  6728:                                     }
                   6729:                                     last;
1.585     raeburn  6730:                                 }
                   6731:                             }
                   6732:                         }
                   6733:                     }
                   6734:                 }
                   6735:             }
                   6736:         }
                   6737:     }
1.612     raeburn  6738:     return;
                   6739: }
                   6740: 
                   6741: sub user_rule_formats {
                   6742:     my ($domain,$domdesc,$curr_rules,$check) = @_;
                   6743:     my %text = ( 
                   6744:                  'username' => 'Usernames',
                   6745:                  'id'       => 'IDs',
                   6746:                );
                   6747:     my $output;
                   6748:     my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
                   6749:     if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
                   6750:         if (@{$ruleorder} > 0) {
                   6751:             $output = '<br />'.&mt("$text{$check} with the following format(s) may <span class=\"LC_cusr_emph\">only</span> be used for verified users at [_1]:",$domdesc).' <ul>';
                   6752:             foreach my $rule (@{$ruleorder}) {
                   6753:                 if (ref($curr_rules) eq 'ARRAY') {
                   6754:                     if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
                   6755:                         if (ref($rules->{$rule}) eq 'HASH') {
                   6756:                             $output .= '<li>'.$rules->{$rule}{'name'}.': '.
                   6757:                                         $rules->{$rule}{'desc'}.'</li>';
                   6758:                         }
                   6759:                     }
                   6760:                 }
                   6761:             }
                   6762:             $output .= '</ul>';
                   6763:         }
                   6764:     }
                   6765:     return $output;
                   6766: }
                   6767: 
                   6768: sub instrule_disallow_msg {
1.615     raeburn  6769:     my ($checkitem,$domdesc,$count,$mode) = @_;
1.612     raeburn  6770:     my $response;
                   6771:     my %text = (
                   6772:                   item   => 'username',
                   6773:                   items  => 'usernames',
                   6774:                   match  => 'matches',
                   6775:                   do     => 'does',
                   6776:                   action => 'a username',
                   6777:                   one    => 'one',
                   6778:                );
                   6779:     if ($count > 1) {
                   6780:         $text{'item'} = 'usernames';
                   6781:         $text{'match'} ='match';
                   6782:         $text{'do'} = 'do';
                   6783:         $text{'action'} = 'usernames',
                   6784:         $text{'one'} = 'ones';
                   6785:     }
                   6786:     if ($checkitem eq 'id') {
                   6787:         $text{'items'} = 'IDs';
                   6788:         $text{'item'} = 'ID';
                   6789:         $text{'action'} = 'an ID';
1.615     raeburn  6790:         if ($count > 1) {
                   6791:             $text{'item'} = 'IDs';
                   6792:             $text{'action'} = 'IDs';
                   6793:         }
1.612     raeburn  6794:     }
                   6795:     $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for <span class=\"LC_cusr_emph\">[_1]</span>, but the $text{'item'} $text{'do'} not exist in the institutional directory.",$domdesc).'<br />';
1.615     raeburn  6796:     if ($mode eq 'upload') {
                   6797:         if ($checkitem eq 'username') {
                   6798:             $response .= &mt("You will need to modify your upload file so it will include $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
                   6799:         } elsif ($checkitem eq 'id') {
                   6800:             $response .= &mt("Either upload a file which includes $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or when associating fields with data columns, omit an association for the ID/Student Number field.");
                   6801:         }
                   6802:     } else {
                   6803:         if ($checkitem eq 'username') {
                   6804:             $response .= &mt("You must choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
                   6805:         } elsif ($checkitem eq 'id') {
                   6806:             $response .= &mt("You must either choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or leave the ID field blank.");
                   6807:         }
1.612     raeburn  6808:     }
                   6809:     return $response;
1.585     raeburn  6810: }
                   6811: 
1.624     raeburn  6812: sub personal_data_fieldtitles {
                   6813:     my %fieldtitles = &Apache::lonlocal::texthash (
                   6814:                         id => 'Student/Employee ID',
                   6815:                         permanentemail => 'E-mail address',
                   6816:                         lastname => 'Last Name',
                   6817:                         firstname => 'First Name',
                   6818:                         middlename => 'Middle Name',
                   6819:                         generation => 'Generation',
                   6820:                         gen => 'Generation',
                   6821:                    );
                   6822:     return %fieldtitles;
                   6823: }
                   6824: 
1.642     raeburn  6825: sub sorted_inst_types {
                   6826:     my ($dom) = @_;
                   6827:     my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
                   6828:     my $othertitle = &mt('All users');
                   6829:     if ($env{'request.course.id'}) {
                   6830:         $othertitle  = 'any';
                   6831:     }
                   6832:     my @types;
                   6833:     if (ref($order) eq 'ARRAY') {
                   6834:         @types = @{$order};
                   6835:     }
                   6836:     if (@types == 0) {
                   6837:         if (ref($usertypes) eq 'HASH') {
                   6838:             @types = sort(keys(%{$usertypes}));
                   6839:         }
                   6840:     }
                   6841:     if (keys(%{$usertypes}) > 0) {
                   6842:         $othertitle = &mt('Other users');
                   6843:         if ($env{'request.course.id'}) {
                   6844:             $othertitle = 'other';
                   6845:         }
                   6846:     }
                   6847:     return ($othertitle,$usertypes,\@types);
                   6848: }
                   6849: 
1.645     raeburn  6850: sub get_institutional_codes {
                   6851:     my ($settings,$allcourses,$LC_code) = @_;
                   6852: # Get complete list of course sections to update
                   6853:     my @currsections = ();
                   6854:     my @currxlists = ();
                   6855:     my $coursecode = $$settings{'internal.coursecode'};
                   6856: 
                   6857:     if ($$settings{'internal.sectionnums'} ne '') {
                   6858:         @currsections = split(/,/,$$settings{'internal.sectionnums'});
                   6859:     }
                   6860: 
                   6861:     if ($$settings{'internal.crosslistings'} ne '') {
                   6862:         @currxlists = split(/,/,$$settings{'internal.crosslistings'});
                   6863:     }
                   6864: 
                   6865:     if (@currxlists > 0) {
                   6866:         foreach (@currxlists) {
                   6867:             if (m/^([^:]+):(\w*)$/) {
                   6868:                 unless (grep/^$1$/,@{$allcourses}) {
                   6869:                     push @{$allcourses},$1;
                   6870:                     $$LC_code{$1} = $2;
                   6871:                 }
                   6872:             }
                   6873:         }
                   6874:     }
                   6875:  
                   6876:     if (@currsections > 0) {
                   6877:         foreach (@currsections) {
                   6878:             if (m/^(\w+):(\w*)$/) {
                   6879:                 my $sec = $coursecode.$1;
                   6880:                 my $lc_sec = $2;
                   6881:                 unless (grep/^$sec$/,@{$allcourses}) {
                   6882:                     push @{$allcourses},$sec;
                   6883:                     $$LC_code{$sec} = $lc_sec;
                   6884:                 }
                   6885:             }
                   6886:         }
                   6887:     }
                   6888:     return;
                   6889: }
                   6890: 
1.112     bowersj2 6891: =pod
                   6892: 
1.549     albertel 6893: =back
                   6894: 
                   6895: =head1 HTTP Helpers
                   6896: 
                   6897: =over 4
                   6898: 
1.648     raeburn  6899: =item * &get_unprocessed_cgi($query,$possible_names)
1.112     bowersj2 6900: 
1.258     albertel 6901: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112     bowersj2 6902: $query.  The parameters listed in $possible_names (an array reference),
1.258     albertel 6903: will be set in $env{'form.name'} if they do not already exist.
1.112     bowersj2 6904: 
                   6905: Typically called with $ENV{'QUERY_STRING'} as the first parameter.  
                   6906: $possible_names is an ref to an array of form element names.  As an example:
                   6907: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258     albertel 6908: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112     bowersj2 6909: 
                   6910: =cut
1.1       albertel 6911: 
1.6       albertel 6912: sub get_unprocessed_cgi {
1.25      albertel 6913:   my ($query,$possible_names)= @_;
1.26      matthew  6914:   # $Apache::lonxml::debug=1;
1.356     albertel 6915:   foreach my $pair (split(/&/,$query)) {
                   6916:     my ($name, $value) = split(/=/,$pair);
1.369     www      6917:     $name = &unescape($name);
1.25      albertel 6918:     if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
                   6919:       $value =~ tr/+/ /;
                   6920:       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258     albertel 6921:       unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25      albertel 6922:     }
1.16      harris41 6923:   }
1.6       albertel 6924: }
                   6925: 
1.112     bowersj2 6926: =pod
                   6927: 
1.648     raeburn  6928: =item * &cacheheader() 
1.112     bowersj2 6929: 
                   6930: returns cache-controlling header code
                   6931: 
                   6932: =cut
                   6933: 
1.7       albertel 6934: sub cacheheader {
1.258     albertel 6935:     unless ($env{'request.method'} eq 'GET') { return ''; }
1.216     albertel 6936:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
                   6937:     my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7       albertel 6938:                 <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
                   6939:                 <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216     albertel 6940:     return $output;
1.7       albertel 6941: }
                   6942: 
1.112     bowersj2 6943: =pod
                   6944: 
1.648     raeburn  6945: =item * &no_cache($r) 
1.112     bowersj2 6946: 
                   6947: specifies header code to not have cache
                   6948: 
                   6949: =cut
                   6950: 
1.9       albertel 6951: sub no_cache {
1.216     albertel 6952:     my ($r) = @_;
                   6953:     if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258     albertel 6954: 	$env{'request.method'} ne 'GET') { return ''; }
1.216     albertel 6955:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
                   6956:     $r->no_cache(1);
                   6957:     $r->header_out("Expires" => $date);
                   6958:     $r->header_out("Pragma" => "no-cache");
1.123     www      6959: }
                   6960: 
                   6961: sub content_type {
1.181     albertel 6962:     my ($r,$type,$charset) = @_;
1.299     foxr     6963:     if ($r) {
                   6964: 	#  Note that printout.pl calls this with undef for $r.
                   6965: 	&no_cache($r);
                   6966:     }
1.258     albertel 6967:     if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181     albertel 6968:     unless ($charset) {
                   6969: 	$charset=&Apache::lonlocal::current_encoding;
                   6970:     }
                   6971:     if ($charset) { $type.='; charset='.$charset; }
                   6972:     if ($r) {
                   6973: 	$r->content_type($type);
                   6974:     } else {
                   6975: 	print("Content-type: $type\n\n");
                   6976:     }
1.9       albertel 6977: }
1.25      albertel 6978: 
1.112     bowersj2 6979: =pod
                   6980: 
1.648     raeburn  6981: =item * &add_to_env($name,$value) 
1.112     bowersj2 6982: 
1.258     albertel 6983: adds $name to the %env hash with value
1.112     bowersj2 6984: $value, if $name already exists, the entry is converted to an array
                   6985: reference and $value is added to the array.
                   6986: 
                   6987: =cut
                   6988: 
1.25      albertel 6989: sub add_to_env {
                   6990:   my ($name,$value)=@_;
1.258     albertel 6991:   if (defined($env{$name})) {
                   6992:     if (ref($env{$name})) {
1.25      albertel 6993:       #already have multiple values
1.258     albertel 6994:       push(@{ $env{$name} },$value);
1.25      albertel 6995:     } else {
                   6996:       #first time seeing multiple values, convert hash entry to an arrayref
1.258     albertel 6997:       my $first=$env{$name};
                   6998:       undef($env{$name});
                   6999:       push(@{ $env{$name} },$first,$value);
1.25      albertel 7000:     }
                   7001:   } else {
1.258     albertel 7002:     $env{$name}=$value;
1.25      albertel 7003:   }
1.31      albertel 7004: }
1.149     albertel 7005: 
                   7006: =pod
                   7007: 
1.648     raeburn  7008: =item * &get_env_multiple($name) 
1.149     albertel 7009: 
1.258     albertel 7010: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149     albertel 7011: values may be defined and end up as an array ref.
                   7012: 
                   7013: returns an array of values
                   7014: 
                   7015: =cut
                   7016: 
                   7017: sub get_env_multiple {
                   7018:     my ($name) = @_;
                   7019:     my @values;
1.258     albertel 7020:     if (defined($env{$name})) {
1.149     albertel 7021:         # exists is it an array
1.258     albertel 7022:         if (ref($env{$name})) {
                   7023:             @values=@{ $env{$name} };
1.149     albertel 7024:         } else {
1.258     albertel 7025:             $values[0]=$env{$name};
1.149     albertel 7026:         }
                   7027:     }
                   7028:     return(@values);
                   7029: }
                   7030: 
1.31      albertel 7031: 
1.41      ng       7032: =pod
1.45      matthew  7033: 
1.464     albertel 7034: =back
1.41      ng       7035: 
1.112     bowersj2 7036: =head1 CSV Upload/Handling functions
1.38      albertel 7037: 
1.41      ng       7038: =over 4
                   7039: 
1.648     raeburn  7040: =item * &upfile_store($r)
1.41      ng       7041: 
                   7042: Store uploaded file, $r should be the HTTP Request object,
1.258     albertel 7043: needs $env{'form.upfile'}
1.41      ng       7044: returns $datatoken to be put into hidden field
                   7045: 
                   7046: =cut
1.31      albertel 7047: 
                   7048: sub upfile_store {
                   7049:     my $r=shift;
1.258     albertel 7050:     $env{'form.upfile'}=~s/\r/\n/gs;
                   7051:     $env{'form.upfile'}=~s/\f/\n/gs;
                   7052:     $env{'form.upfile'}=~s/\n+/\n/gs;
                   7053:     $env{'form.upfile'}=~s/\n+$//gs;
1.31      albertel 7054: 
1.258     albertel 7055:     my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
                   7056: 	'_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31      albertel 7057:     {
1.158     raeburn  7058:         my $datafile = $r->dir_config('lonDaemons').
                   7059:                            '/tmp/'.$datatoken.'.tmp';
                   7060:         if ( open(my $fh,">$datafile") ) {
1.258     albertel 7061:             print $fh $env{'form.upfile'};
1.158     raeburn  7062:             close($fh);
                   7063:         }
1.31      albertel 7064:     }
                   7065:     return $datatoken;
                   7066: }
                   7067: 
1.56      matthew  7068: =pod
                   7069: 
1.648     raeburn  7070: =item * &load_tmp_file($r)
1.41      ng       7071: 
                   7072: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258     albertel 7073: needs $env{'form.datatoken'},
                   7074: sets $env{'form.upfile'} to the contents of the file
1.41      ng       7075: 
                   7076: =cut
1.31      albertel 7077: 
                   7078: sub load_tmp_file {
                   7079:     my $r=shift;
                   7080:     my @studentdata=();
                   7081:     {
1.158     raeburn  7082:         my $studentfile = $r->dir_config('lonDaemons').
1.258     albertel 7083:                               '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158     raeburn  7084:         if ( open(my $fh,"<$studentfile") ) {
                   7085:             @studentdata=<$fh>;
                   7086:             close($fh);
                   7087:         }
1.31      albertel 7088:     }
1.258     albertel 7089:     $env{'form.upfile'}=join('',@studentdata);
1.31      albertel 7090: }
                   7091: 
1.56      matthew  7092: =pod
                   7093: 
1.648     raeburn  7094: =item * &upfile_record_sep()
1.41      ng       7095: 
                   7096: Separate uploaded file into records
                   7097: returns array of records,
1.258     albertel 7098: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41      ng       7099: 
                   7100: =cut
1.31      albertel 7101: 
                   7102: sub upfile_record_sep {
1.258     albertel 7103:     if ($env{'form.upfiletype'} eq 'xml') {
1.31      albertel 7104:     } else {
1.248     albertel 7105: 	my @records;
1.258     albertel 7106: 	foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248     albertel 7107: 	    if ($line=~/^\s*$/) { next; }
                   7108: 	    push(@records,$line);
                   7109: 	}
                   7110: 	return @records;
1.31      albertel 7111:     }
                   7112: }
                   7113: 
1.56      matthew  7114: =pod
                   7115: 
1.648     raeburn  7116: =item * &record_sep($record)
1.41      ng       7117: 
1.258     albertel 7118: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41      ng       7119: 
                   7120: =cut
                   7121: 
1.263     www      7122: sub takeleft {
                   7123:     my $index=shift;
                   7124:     return substr('0000'.$index,-4,4);
                   7125: }
                   7126: 
1.31      albertel 7127: sub record_sep {
                   7128:     my $record=shift;
                   7129:     my %components=();
1.258     albertel 7130:     if ($env{'form.upfiletype'} eq 'xml') {
                   7131:     } elsif ($env{'form.upfiletype'} eq 'space') {
1.31      albertel 7132:         my $i=0;
1.356     albertel 7133:         foreach my $field (split(/\s+/,$record)) {
1.31      albertel 7134:             $field=~s/^(\"|\')//;
                   7135:             $field=~s/(\"|\')$//;
1.263     www      7136:             $components{&takeleft($i)}=$field;
1.31      albertel 7137:             $i++;
                   7138:         }
1.258     albertel 7139:     } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31      albertel 7140:         my $i=0;
1.356     albertel 7141:         foreach my $field (split(/\t/,$record)) {
1.31      albertel 7142:             $field=~s/^(\"|\')//;
                   7143:             $field=~s/(\"|\')$//;
1.263     www      7144:             $components{&takeleft($i)}=$field;
1.31      albertel 7145:             $i++;
                   7146:         }
                   7147:     } else {
1.561     www      7148:         my $separator=',';
1.480     banghart 7149:         if ($env{'form.upfiletype'} eq 'semisv') {
1.561     www      7150:             $separator=';';
1.480     banghart 7151:         }
1.31      albertel 7152:         my $i=0;
1.561     www      7153: # the character we are looking for to indicate the end of a quote or a record 
                   7154:         my $looking_for=$separator;
                   7155: # do not add the characters to the fields
                   7156:         my $ignore=0;
                   7157: # we just encountered a separator (or the beginning of the record)
                   7158:         my $just_found_separator=1;
                   7159: # store the field we are working on here
                   7160:         my $field='';
                   7161: # work our way through all characters in record
                   7162:         foreach my $character ($record=~/(.)/g) {
                   7163:             if ($character eq $looking_for) {
                   7164:                if ($character ne $separator) {
                   7165: # Found the end of a quote, again looking for separator
                   7166:                   $looking_for=$separator;
                   7167:                   $ignore=1;
                   7168:                } else {
                   7169: # Found a separator, store away what we got
                   7170:                   $components{&takeleft($i)}=$field;
                   7171: 	          $i++;
                   7172:                   $just_found_separator=1;
                   7173:                   $ignore=0;
                   7174:                   $field='';
                   7175:                }
                   7176:                next;
                   7177:             }
                   7178: # single or double quotation marks after a separator indicate beginning of a quote
                   7179: # we are now looking for the end of the quote and need to ignore separators
                   7180:             if ((($character eq '"') || ($character eq "'")) && ($just_found_separator))  {
                   7181:                $looking_for=$character;
                   7182:                next;
                   7183:             }
                   7184: # ignore would be true after we reached the end of a quote
                   7185:             if ($ignore) { next; }
                   7186:             if (($just_found_separator) && ($character=~/\s/)) { next; }
                   7187:             $field.=$character;
                   7188:             $just_found_separator=0; 
1.31      albertel 7189:         }
1.561     www      7190: # catch the very last entry, since we never encountered the separator
                   7191:         $components{&takeleft($i)}=$field;
1.31      albertel 7192:     }
                   7193:     return %components;
                   7194: }
                   7195: 
1.144     matthew  7196: ######################################################
                   7197: ######################################################
                   7198: 
1.56      matthew  7199: =pod
                   7200: 
1.648     raeburn  7201: =item * &upfile_select_html()
1.41      ng       7202: 
1.144     matthew  7203: Return HTML code to select a file from the users machine and specify 
                   7204: the file type.
1.41      ng       7205: 
                   7206: =cut
                   7207: 
1.144     matthew  7208: ######################################################
                   7209: ######################################################
1.31      albertel 7210: sub upfile_select_html {
1.144     matthew  7211:     my %Types = (
                   7212:                  csv   => &mt('CSV (comma separated values, spreadsheet)'),
1.480     banghart 7213:                  semisv => &mt('Semicolon separated values'),
1.144     matthew  7214:                  space => &mt('Space separated'),
                   7215:                  tab   => &mt('Tabulator separated'),
                   7216: #                 xml   => &mt('HTML/XML'),
                   7217:                  );
                   7218:     my $Str = '<input type="file" name="upfile" size="50" />'.
                   7219:         '<br />Type: <select name="upfiletype">';
                   7220:     foreach my $type (sort(keys(%Types))) {
                   7221:         $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
                   7222:     }
                   7223:     $Str .= "</select>\n";
                   7224:     return $Str;
1.31      albertel 7225: }
                   7226: 
1.301     albertel 7227: sub get_samples {
                   7228:     my ($records,$toget) = @_;
                   7229:     my @samples=({});
                   7230:     my $got=0;
                   7231:     foreach my $rec (@$records) {
                   7232: 	my %temp = &record_sep($rec);
                   7233: 	if (! grep(/\S/, values(%temp))) { next; }
                   7234: 	if (%temp) {
                   7235: 	    $samples[$got]=\%temp;
                   7236: 	    $got++;
                   7237: 	    if ($got == $toget) { last; }
                   7238: 	}
                   7239:     }
                   7240:     return \@samples;
                   7241: }
                   7242: 
1.144     matthew  7243: ######################################################
                   7244: ######################################################
                   7245: 
1.56      matthew  7246: =pod
                   7247: 
1.648     raeburn  7248: =item * &csv_print_samples($r,$records)
1.41      ng       7249: 
                   7250: Prints a table of sample values from each column uploaded $r is an
                   7251: Apache Request ref, $records is an arrayref from
                   7252: &Apache::loncommon::upfile_record_sep
                   7253: 
                   7254: =cut
                   7255: 
1.144     matthew  7256: ######################################################
                   7257: ######################################################
1.31      albertel 7258: sub csv_print_samples {
                   7259:     my ($r,$records) = @_;
1.301     albertel 7260:     my $samples = &get_samples($records,3);
                   7261: 
1.594     raeburn  7262:     $r->print(&mt('Samples').'<br />'.&start_data_table().
                   7263:               &start_data_table_header_row());
1.356     albertel 7264:     foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { 
                   7265:         $r->print('<th>'.&mt('Column&nbsp;[_1]',($sample+1)).'</th>'); }
1.594     raeburn  7266:     $r->print(&end_data_table_header_row());
1.301     albertel 7267:     foreach my $hash (@$samples) {
1.594     raeburn  7268: 	$r->print(&start_data_table_row());
1.356     albertel 7269: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31      albertel 7270: 	    $r->print('<td>');
1.356     albertel 7271: 	    if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31      albertel 7272: 	    $r->print('</td>');
                   7273: 	}
1.594     raeburn  7274: 	$r->print(&end_data_table_row());
1.31      albertel 7275:     }
1.594     raeburn  7276:     $r->print(&end_data_table().'<br />'."\n");
1.31      albertel 7277: }
                   7278: 
1.144     matthew  7279: ######################################################
                   7280: ######################################################
                   7281: 
1.56      matthew  7282: =pod
                   7283: 
1.648     raeburn  7284: =item * &csv_print_select_table($r,$records,$d)
1.41      ng       7285: 
                   7286: Prints a table to create associations between values and table columns.
1.144     matthew  7287: 
1.41      ng       7288: $r is an Apache Request ref,
                   7289: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174     matthew  7290: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41      ng       7291: 
                   7292: =cut
                   7293: 
1.144     matthew  7294: ######################################################
                   7295: ######################################################
1.31      albertel 7296: sub csv_print_select_table {
                   7297:     my ($r,$records,$d) = @_;
1.301     albertel 7298:     my $i=0;
                   7299:     my $samples = &get_samples($records,1);
1.144     matthew  7300:     $r->print(&mt('Associate columns with student attributes.')."\n".
1.594     raeburn  7301: 	      &start_data_table().&start_data_table_header_row().
1.144     matthew  7302:               '<th>'.&mt('Attribute').'</th>'.
1.594     raeburn  7303:               '<th>'.&mt('Column').'</th>'.
                   7304:               &end_data_table_header_row()."\n");
1.356     albertel 7305:     foreach my $array_ref (@$d) {
                   7306: 	my ($value,$display,$defaultcol)=@{ $array_ref };
1.594     raeburn  7307: 	$r->print(&start_data_table_row().'<tr><td>'.$display.'</td>');
1.31      albertel 7308: 
                   7309: 	$r->print('<td><select name=f'.$i.
1.32      matthew  7310: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 7311: 	$r->print('<option value="none"></option>');
1.356     albertel 7312: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
                   7313: 	    $r->print('<option value="'.$sample.'"'.
                   7314:                       ($sample eq $defaultcol ? ' selected="selected" ' : '').
                   7315:                       '>Column '.($sample+1).'</option>');
1.31      albertel 7316: 	}
1.594     raeburn  7317: 	$r->print('</select></td>'.&end_data_table_row()."\n");
1.31      albertel 7318: 	$i++;
                   7319:     }
1.594     raeburn  7320:     $r->print(&end_data_table());
1.31      albertel 7321:     $i--;
                   7322:     return $i;
                   7323: }
1.56      matthew  7324: 
1.144     matthew  7325: ######################################################
                   7326: ######################################################
                   7327: 
1.56      matthew  7328: =pod
1.31      albertel 7329: 
1.648     raeburn  7330: =item * &csv_samples_select_table($r,$records,$d)
1.41      ng       7331: 
                   7332: Prints a table of sample values from the upload and can make associate samples to internal names.
                   7333: 
                   7334: $r is an Apache Request ref,
                   7335: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
                   7336: $d is an array of 2 element arrays (internal name, displayed name)
                   7337: 
                   7338: =cut
                   7339: 
1.144     matthew  7340: ######################################################
                   7341: ######################################################
1.31      albertel 7342: sub csv_samples_select_table {
                   7343:     my ($r,$records,$d) = @_;
                   7344:     my $i=0;
1.144     matthew  7345:     #
1.301     albertel 7346:     my $samples = &get_samples($records,3);
1.594     raeburn  7347:     $r->print(&start_data_table().
                   7348:               &start_data_table_header_row().'<th>'.
                   7349:               &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
                   7350:               &end_data_table_header_row());
1.301     albertel 7351: 
                   7352:     foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594     raeburn  7353: 	$r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32      matthew  7354: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.301     albertel 7355: 	foreach my $option (@$d) {
                   7356: 	    my ($value,$display,$defaultcol)=@{ $option };
1.174     matthew  7357: 	    $r->print('<option value="'.$value.'"'.
1.253     albertel 7358:                       ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174     matthew  7359:                       $display.'</option>');
1.31      albertel 7360: 	}
                   7361: 	$r->print('</select></td><td>');
1.301     albertel 7362: 	foreach my $line (0..2) {
                   7363: 	    if (defined($samples->[$line]{$key})) { 
                   7364: 		$r->print($samples->[$line]{$key}."<br />\n"); 
                   7365: 	    }
                   7366: 	}
1.594     raeburn  7367: 	$r->print('</td>'.&end_data_table_row());
1.31      albertel 7368: 	$i++;
                   7369:     }
1.594     raeburn  7370:     $r->print(&end_data_table());
1.31      albertel 7371:     $i--;
                   7372:     return($i);
1.115     matthew  7373: }
                   7374: 
1.144     matthew  7375: ######################################################
                   7376: ######################################################
                   7377: 
1.115     matthew  7378: =pod
                   7379: 
1.648     raeburn  7380: =item * &clean_excel_name($name)
1.115     matthew  7381: 
                   7382: Returns a replacement for $name which does not contain any illegal characters.
                   7383: 
                   7384: =cut
                   7385: 
1.144     matthew  7386: ######################################################
                   7387: ######################################################
1.115     matthew  7388: sub clean_excel_name {
                   7389:     my ($name) = @_;
                   7390:     $name =~ s/[:\*\?\/\\]//g;
                   7391:     if (length($name) > 31) {
                   7392:         $name = substr($name,0,31);
                   7393:     }
                   7394:     return $name;
1.25      albertel 7395: }
1.84      albertel 7396: 
1.85      albertel 7397: =pod
                   7398: 
1.648     raeburn  7399: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85      albertel 7400: 
                   7401: Returns either 1 or undef
                   7402: 
                   7403: 1 if the part is to be hidden, undef if it is to be shown
                   7404: 
                   7405: Arguments are:
                   7406: 
                   7407: $id the id of the part to be checked
                   7408: $symb, optional the symb of the resource to check
                   7409: $udom, optional the domain of the user to check for
                   7410: $uname, optional the username of the user to check for
                   7411: 
                   7412: =cut
1.84      albertel 7413: 
                   7414: sub check_if_partid_hidden {
                   7415:     my ($id,$symb,$udom,$uname) = @_;
1.133     albertel 7416:     my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84      albertel 7417: 					 $symb,$udom,$uname);
1.141     albertel 7418:     my $truth=1;
                   7419:     #if the string starts with !, then the list is the list to show not hide
                   7420:     if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84      albertel 7421:     my @hiddenlist=split(/,/,$hiddenparts);
                   7422:     foreach my $checkid (@hiddenlist) {
1.141     albertel 7423: 	if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84      albertel 7424:     }
1.141     albertel 7425:     return !$truth;
1.84      albertel 7426: }
1.127     matthew  7427: 
1.138     matthew  7428: 
                   7429: ############################################################
                   7430: ############################################################
                   7431: 
                   7432: =pod
                   7433: 
1.157     matthew  7434: =back 
                   7435: 
1.138     matthew  7436: =head1 cgi-bin script and graphing routines
                   7437: 
1.157     matthew  7438: =over 4
                   7439: 
1.648     raeburn  7440: =item * &get_cgi_id()
1.138     matthew  7441: 
                   7442: Inputs: none
                   7443: 
                   7444: Returns an id which can be used to pass environment variables
                   7445: to various cgi-bin scripts.  These environment variables will
                   7446: be removed from the users environment after a given time by
                   7447: the routine &Apache::lonnet::transfer_profile_to_env.
                   7448: 
                   7449: =cut
                   7450: 
                   7451: ############################################################
                   7452: ############################################################
1.152     albertel 7453: my $uniq=0;
1.136     matthew  7454: sub get_cgi_id {
1.154     albertel 7455:     $uniq=($uniq+1)%100000;
1.280     albertel 7456:     return (time.'_'.$$.'_'.$uniq);
1.136     matthew  7457: }
                   7458: 
1.127     matthew  7459: ############################################################
                   7460: ############################################################
                   7461: 
                   7462: =pod
                   7463: 
1.648     raeburn  7464: =item * &DrawBarGraph()
1.127     matthew  7465: 
1.138     matthew  7466: Facilitates the plotting of data in a (stacked) bar graph.
                   7467: Puts plot definition data into the users environment in order for 
                   7468: graph.png to plot it.  Returns an <img> tag for the plot.
                   7469: The bars on the plot are labeled '1','2',...,'n'.
                   7470: 
                   7471: Inputs:
                   7472: 
                   7473: =over 4
                   7474: 
                   7475: =item $Title: string, the title of the plot
                   7476: 
                   7477: =item $xlabel: string, text describing the X-axis of the plot
                   7478: 
                   7479: =item $ylabel: string, text describing the Y-axis of the plot
                   7480: 
                   7481: =item $Max: scalar, the maximum Y value to use in the plot
                   7482: If $Max is < any data point, the graph will not be rendered.
                   7483: 
1.140     matthew  7484: =item $colors: array ref holding the colors to be used for the data sets when
1.138     matthew  7485: they are plotted.  If undefined, default values will be used.
                   7486: 
1.178     matthew  7487: =item $labels: array ref holding the labels to use on the x-axis for the bars.
                   7488: 
1.138     matthew  7489: =item @Values: An array of array references.  Each array reference holds data
                   7490: to be plotted in a stacked bar chart.
                   7491: 
1.239     matthew  7492: =item If the final element of @Values is a hash reference the key/value
                   7493: pairs will be added to the graph definition.
                   7494: 
1.138     matthew  7495: =back
                   7496: 
                   7497: Returns:
                   7498: 
                   7499: An <img> tag which references graph.png and the appropriate identifying
                   7500: information for the plot.
                   7501: 
1.127     matthew  7502: =cut
                   7503: 
                   7504: ############################################################
                   7505: ############################################################
1.134     matthew  7506: sub DrawBarGraph {
1.178     matthew  7507:     my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134     matthew  7508:     #
                   7509:     if (! defined($colors)) {
                   7510:         $colors = ['#33ff00', 
                   7511:                   '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
                   7512:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                   7513:                   ]; 
                   7514:     }
1.228     matthew  7515:     my $extra_settings = {};
                   7516:     if (ref($Values[-1]) eq 'HASH') {
                   7517:         $extra_settings = pop(@Values);
                   7518:     }
1.127     matthew  7519:     #
1.136     matthew  7520:     my $identifier = &get_cgi_id();
                   7521:     my $id = 'cgi.'.$identifier;        
1.129     matthew  7522:     if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127     matthew  7523:         return '';
                   7524:     }
1.225     matthew  7525:     #
                   7526:     my @Labels;
                   7527:     if (defined($labels)) {
                   7528:         @Labels = @$labels;
                   7529:     } else {
                   7530:         for (my $i=0;$i<@{$Values[0]};$i++) {
                   7531:             push (@Labels,$i+1);
                   7532:         }
                   7533:     }
                   7534:     #
1.129     matthew  7535:     my $NumBars = scalar(@{$Values[0]});
1.225     matthew  7536:     if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129     matthew  7537:     my %ValuesHash;
                   7538:     my $NumSets=1;
                   7539:     foreach my $array (@Values) {
                   7540:         next if (! ref($array));
1.136     matthew  7541:         $ValuesHash{$id.'.data.'.$NumSets++} = 
1.132     matthew  7542:             join(',',@$array);
1.129     matthew  7543:     }
1.127     matthew  7544:     #
1.136     matthew  7545:     my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225     matthew  7546:     if ($NumBars < 3) {
                   7547:         $width = 120+$NumBars*32;
1.220     matthew  7548:         $xskip = 1;
1.225     matthew  7549:         $bar_width = 30;
                   7550:     } elsif ($NumBars < 5) {
                   7551:         $width = 120+$NumBars*20;
                   7552:         $xskip = 1;
                   7553:         $bar_width = 20;
1.220     matthew  7554:     } elsif ($NumBars < 10) {
1.136     matthew  7555:         $width = 120+$NumBars*15;
                   7556:         $xskip = 1;
                   7557:         $bar_width = 15;
                   7558:     } elsif ($NumBars <= 25) {
                   7559:         $width = 120+$NumBars*11;
                   7560:         $xskip = 5;
                   7561:         $bar_width = 8;
                   7562:     } elsif ($NumBars <= 50) {
                   7563:         $width = 120+$NumBars*8;
                   7564:         $xskip = 5;
                   7565:         $bar_width = 4;
                   7566:     } else {
                   7567:         $width = 120+$NumBars*8;
                   7568:         $xskip = 5;
                   7569:         $bar_width = 4;
                   7570:     }
                   7571:     #
1.137     matthew  7572:     $Max = 1 if ($Max < 1);
                   7573:     if ( int($Max) < $Max ) {
                   7574:         $Max++;
                   7575:         $Max = int($Max);
                   7576:     }
1.127     matthew  7577:     $Title  = '' if (! defined($Title));
                   7578:     $xlabel = '' if (! defined($xlabel));
                   7579:     $ylabel = '' if (! defined($ylabel));
1.369     www      7580:     $ValuesHash{$id.'.title'}    = &escape($Title);
                   7581:     $ValuesHash{$id.'.xlabel'}   = &escape($xlabel);
                   7582:     $ValuesHash{$id.'.ylabel'}   = &escape($ylabel);
1.137     matthew  7583:     $ValuesHash{$id.'.y_max_value'} = $Max;
1.136     matthew  7584:     $ValuesHash{$id.'.NumBars'}  = $NumBars;
                   7585:     $ValuesHash{$id.'.NumSets'}  = $NumSets;
                   7586:     $ValuesHash{$id.'.PlotType'} = 'bar';
                   7587:     $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   7588:     $ValuesHash{$id.'.height'}   = $height;
                   7589:     $ValuesHash{$id.'.width'}    = $width;
                   7590:     $ValuesHash{$id.'.xskip'}    = $xskip;
                   7591:     $ValuesHash{$id.'.bar_width'} = $bar_width;
                   7592:     $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127     matthew  7593:     #
1.228     matthew  7594:     # Deal with other parameters
                   7595:     while (my ($key,$value) = each(%$extra_settings)) {
                   7596:         $ValuesHash{$id.'.'.$key} = $value;
                   7597:     }
                   7598:     #
1.646     raeburn  7599:     &Apache::lonnet::appenv(\%ValuesHash);
1.137     matthew  7600:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   7601: }
                   7602: 
                   7603: ############################################################
                   7604: ############################################################
                   7605: 
                   7606: =pod
                   7607: 
1.648     raeburn  7608: =item * &DrawXYGraph()
1.137     matthew  7609: 
1.138     matthew  7610: Facilitates the plotting of data in an XY graph.
                   7611: Puts plot definition data into the users environment in order for 
                   7612: graph.png to plot it.  Returns an <img> tag for the plot.
                   7613: 
                   7614: Inputs:
                   7615: 
                   7616: =over 4
                   7617: 
                   7618: =item $Title: string, the title of the plot
                   7619: 
                   7620: =item $xlabel: string, text describing the X-axis of the plot
                   7621: 
                   7622: =item $ylabel: string, text describing the Y-axis of the plot
                   7623: 
                   7624: =item $Max: scalar, the maximum Y value to use in the plot
                   7625: If $Max is < any data point, the graph will not be rendered.
                   7626: 
                   7627: =item $colors: Array ref containing the hex color codes for the data to be 
                   7628: plotted in.  If undefined, default values will be used.
                   7629: 
                   7630: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   7631: 
                   7632: =item $Ydata: Array ref containing Array refs.  
1.185     www      7633: Each of the contained arrays will be plotted as a separate curve.
1.138     matthew  7634: 
                   7635: =item %Values: hash indicating or overriding any default values which are 
                   7636: passed to graph.png.  
                   7637: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   7638: 
                   7639: =back
                   7640: 
                   7641: Returns:
                   7642: 
                   7643: An <img> tag which references graph.png and the appropriate identifying
                   7644: information for the plot.
                   7645: 
1.137     matthew  7646: =cut
                   7647: 
                   7648: ############################################################
                   7649: ############################################################
                   7650: sub DrawXYGraph {
                   7651:     my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
                   7652:     #
                   7653:     # Create the identifier for the graph
                   7654:     my $identifier = &get_cgi_id();
                   7655:     my $id = 'cgi.'.$identifier;
                   7656:     #
                   7657:     $Title  = '' if (! defined($Title));
                   7658:     $xlabel = '' if (! defined($xlabel));
                   7659:     $ylabel = '' if (! defined($ylabel));
                   7660:     my %ValuesHash = 
                   7661:         (
1.369     www      7662:          $id.'.title'  => &escape($Title),
                   7663:          $id.'.xlabel' => &escape($xlabel),
                   7664:          $id.'.ylabel' => &escape($ylabel),
1.137     matthew  7665:          $id.'.y_max_value'=> $Max,
                   7666:          $id.'.labels'     => join(',',@$Xlabels),
                   7667:          $id.'.PlotType'   => 'XY',
                   7668:          );
                   7669:     #
                   7670:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   7671:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   7672:     }
                   7673:     #
                   7674:     if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
                   7675:         return '';
                   7676:     }
                   7677:     my $NumSets=1;
1.138     matthew  7678:     foreach my $array (@{$Ydata}){
1.137     matthew  7679:         next if (! ref($array));
                   7680:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
                   7681:     }
1.138     matthew  7682:     $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137     matthew  7683:     #
                   7684:     # Deal with other parameters
                   7685:     while (my ($key,$value) = each(%Values)) {
                   7686:         $ValuesHash{$id.'.'.$key} = $value;
1.127     matthew  7687:     }
                   7688:     #
1.646     raeburn  7689:     &Apache::lonnet::appenv(\%ValuesHash);
1.136     matthew  7690:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   7691: }
                   7692: 
                   7693: ############################################################
                   7694: ############################################################
                   7695: 
                   7696: =pod
                   7697: 
1.648     raeburn  7698: =item * &DrawXYYGraph()
1.138     matthew  7699: 
                   7700: Facilitates the plotting of data in an XY graph with two Y axes.
                   7701: Puts plot definition data into the users environment in order for 
                   7702: graph.png to plot it.  Returns an <img> tag for the plot.
                   7703: 
                   7704: Inputs:
                   7705: 
                   7706: =over 4
                   7707: 
                   7708: =item $Title: string, the title of the plot
                   7709: 
                   7710: =item $xlabel: string, text describing the X-axis of the plot
                   7711: 
                   7712: =item $ylabel: string, text describing the Y-axis of the plot
                   7713: 
                   7714: =item $colors: Array ref containing the hex color codes for the data to be 
                   7715: plotted in.  If undefined, default values will be used.
                   7716: 
                   7717: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   7718: 
                   7719: =item $Ydata1: The first data set
                   7720: 
                   7721: =item $Min1: The minimum value of the left Y-axis
                   7722: 
                   7723: =item $Max1: The maximum value of the left Y-axis
                   7724: 
                   7725: =item $Ydata2: The second data set
                   7726: 
                   7727: =item $Min2: The minimum value of the right Y-axis
                   7728: 
                   7729: =item $Max2: The maximum value of the left Y-axis
                   7730: 
                   7731: =item %Values: hash indicating or overriding any default values which are 
                   7732: passed to graph.png.  
                   7733: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   7734: 
                   7735: =back
                   7736: 
                   7737: Returns:
                   7738: 
                   7739: An <img> tag which references graph.png and the appropriate identifying
                   7740: information for the plot.
1.136     matthew  7741: 
                   7742: =cut
                   7743: 
                   7744: ############################################################
                   7745: ############################################################
1.137     matthew  7746: sub DrawXYYGraph {
                   7747:     my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
                   7748:                                         $Ydata2,$Min2,$Max2,%Values)=@_;
1.136     matthew  7749:     #
                   7750:     # Create the identifier for the graph
                   7751:     my $identifier = &get_cgi_id();
                   7752:     my $id = 'cgi.'.$identifier;
                   7753:     #
                   7754:     $Title  = '' if (! defined($Title));
                   7755:     $xlabel = '' if (! defined($xlabel));
                   7756:     $ylabel = '' if (! defined($ylabel));
                   7757:     my %ValuesHash = 
                   7758:         (
1.369     www      7759:          $id.'.title'  => &escape($Title),
                   7760:          $id.'.xlabel' => &escape($xlabel),
                   7761:          $id.'.ylabel' => &escape($ylabel),
1.136     matthew  7762:          $id.'.labels' => join(',',@$Xlabels),
                   7763:          $id.'.PlotType' => 'XY',
                   7764:          $id.'.NumSets' => 2,
1.137     matthew  7765:          $id.'.two_axes' => 1,
                   7766:          $id.'.y1_max_value' => $Max1,
                   7767:          $id.'.y1_min_value' => $Min1,
                   7768:          $id.'.y2_max_value' => $Max2,
                   7769:          $id.'.y2_min_value' => $Min2,
1.136     matthew  7770:          );
                   7771:     #
1.137     matthew  7772:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   7773:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   7774:     }
                   7775:     #
                   7776:     if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
                   7777:         ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136     matthew  7778:         return '';
                   7779:     }
                   7780:     my $NumSets=1;
1.137     matthew  7781:     foreach my $array ($Ydata1,$Ydata2){
1.136     matthew  7782:         next if (! ref($array));
                   7783:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137     matthew  7784:     }
                   7785:     #
                   7786:     # Deal with other parameters
                   7787:     while (my ($key,$value) = each(%Values)) {
                   7788:         $ValuesHash{$id.'.'.$key} = $value;
1.136     matthew  7789:     }
                   7790:     #
1.646     raeburn  7791:     &Apache::lonnet::appenv(\%ValuesHash);
1.130     albertel 7792:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139     matthew  7793: }
                   7794: 
                   7795: ############################################################
                   7796: ############################################################
                   7797: 
                   7798: =pod
                   7799: 
1.157     matthew  7800: =back 
                   7801: 
1.139     matthew  7802: =head1 Statistics helper routines?  
                   7803: 
                   7804: Bad place for them but what the hell.
                   7805: 
1.157     matthew  7806: =over 4
                   7807: 
1.648     raeburn  7808: =item * &chartlink()
1.139     matthew  7809: 
                   7810: Returns a link to the chart for a specific student.  
                   7811: 
                   7812: Inputs:
                   7813: 
                   7814: =over 4
                   7815: 
                   7816: =item $linktext: The text of the link
                   7817: 
                   7818: =item $sname: The students username
                   7819: 
                   7820: =item $sdomain: The students domain
                   7821: 
                   7822: =back
                   7823: 
1.157     matthew  7824: =back
                   7825: 
1.139     matthew  7826: =cut
                   7827: 
                   7828: ############################################################
                   7829: ############################################################
                   7830: sub chartlink {
                   7831:     my ($linktext, $sname, $sdomain) = @_;
                   7832:     my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369     www      7833:         '&amp;SelectedStudent='.&escape($sname.':'.$sdomain).
1.219     albertel 7834:         '&amp;chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139     matthew  7835:        '">'.$linktext.'</a>';
1.153     matthew  7836: }
                   7837: 
                   7838: #######################################################
                   7839: #######################################################
                   7840: 
                   7841: =pod
                   7842: 
                   7843: =head1 Course Environment Routines
1.157     matthew  7844: 
                   7845: =over 4
1.153     matthew  7846: 
1.648     raeburn  7847: =item * &restore_course_settings()
1.153     matthew  7848: 
1.648     raeburn  7849: =item * &store_course_settings()
1.153     matthew  7850: 
                   7851: Restores/Store indicated form parameters from the course environment.
                   7852: Will not overwrite existing values of the form parameters.
                   7853: 
                   7854: Inputs: 
                   7855: a scalar describing the data (e.g. 'chart', 'problem_analysis')
                   7856: 
                   7857: a hash ref describing the data to be stored.  For example:
                   7858:    
                   7859: %Save_Parameters = ('Status' => 'scalar',
                   7860:     'chartoutputmode' => 'scalar',
                   7861:     'chartoutputdata' => 'scalar',
                   7862:     'Section' => 'array',
1.373     raeburn  7863:     'Group' => 'array',
1.153     matthew  7864:     'StudentData' => 'array',
                   7865:     'Maps' => 'array');
                   7866: 
                   7867: Returns: both routines return nothing
                   7868: 
1.631     raeburn  7869: =back
                   7870: 
1.153     matthew  7871: =cut
                   7872: 
                   7873: #######################################################
                   7874: #######################################################
                   7875: sub store_course_settings {
1.496     albertel 7876:     return &store_settings($env{'request.course.id'},@_);
                   7877: }
                   7878: 
                   7879: sub store_settings {
1.153     matthew  7880:     # save to the environment
                   7881:     # appenv the same items, just to be safe
1.300     albertel 7882:     my $udom  = $env{'user.domain'};
                   7883:     my $uname = $env{'user.name'};
1.496     albertel 7884:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  7885:     my %SaveHash;
                   7886:     my %AppHash;
                   7887:     while (my ($setting,$type) = each(%$Settings)) {
1.496     albertel 7888:         my $basename = join('.','internal',$context,$prefix,$setting);
1.300     albertel 7889:         my $envname = 'environment.'.$basename;
1.258     albertel 7890:         if (exists($env{'form.'.$setting})) {
1.153     matthew  7891:             # Save this value away
                   7892:             if ($type eq 'scalar' &&
1.258     albertel 7893:                 (! exists($env{$envname}) || 
                   7894:                  $env{$envname} ne $env{'form.'.$setting})) {
                   7895:                 $SaveHash{$basename} = $env{'form.'.$setting};
                   7896:                 $AppHash{$envname}   = $env{'form.'.$setting};
1.153     matthew  7897:             } elsif ($type eq 'array') {
                   7898:                 my $stored_form;
1.258     albertel 7899:                 if (ref($env{'form.'.$setting})) {
1.153     matthew  7900:                     $stored_form = join(',',
                   7901:                                         map {
1.369     www      7902:                                             &escape($_);
1.258     albertel 7903:                                         } sort(@{$env{'form.'.$setting}}));
1.153     matthew  7904:                 } else {
                   7905:                     $stored_form = 
1.369     www      7906:                         &escape($env{'form.'.$setting});
1.153     matthew  7907:                 }
                   7908:                 # Determine if the array contents are the same.
1.258     albertel 7909:                 if ($stored_form ne $env{$envname}) {
1.153     matthew  7910:                     $SaveHash{$basename} = $stored_form;
                   7911:                     $AppHash{$envname}   = $stored_form;
                   7912:                 }
                   7913:             }
                   7914:         }
                   7915:     }
                   7916:     my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300     albertel 7917:                                           $udom,$uname);
1.153     matthew  7918:     if ($put_result !~ /^(ok|delayed)/) {
                   7919:         &Apache::lonnet::logthis('unable to save form parameters, '.
                   7920:                                  'got error:'.$put_result);
                   7921:     }
                   7922:     # Make sure these settings stick around in this session, too
1.646     raeburn  7923:     &Apache::lonnet::appenv(\%AppHash);
1.153     matthew  7924:     return;
                   7925: }
                   7926: 
                   7927: sub restore_course_settings {
1.499     albertel 7928:     return &restore_settings($env{'request.course.id'},@_);
1.496     albertel 7929: }
                   7930: 
                   7931: sub restore_settings {
                   7932:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  7933:     while (my ($setting,$type) = each(%$Settings)) {
1.258     albertel 7934:         next if (exists($env{'form.'.$setting}));
1.496     albertel 7935:         my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153     matthew  7936:             '.'.$setting;
1.258     albertel 7937:         if (exists($env{$envname})) {
1.153     matthew  7938:             if ($type eq 'scalar') {
1.258     albertel 7939:                 $env{'form.'.$setting} = $env{$envname};
1.153     matthew  7940:             } elsif ($type eq 'array') {
1.258     albertel 7941:                 $env{'form.'.$setting} = [ 
1.153     matthew  7942:                                            map { 
1.369     www      7943:                                                &unescape($_); 
1.258     albertel 7944:                                            } split(',',$env{$envname})
1.153     matthew  7945:                                            ];
                   7946:             }
                   7947:         }
                   7948:     }
1.127     matthew  7949: }
                   7950: 
1.618     raeburn  7951: #######################################################
                   7952: #######################################################
                   7953: 
                   7954: =pod
                   7955: 
                   7956: =head1 Domain E-mail Routines  
                   7957: 
                   7958: =over 4
                   7959: 
1.648     raeburn  7960: =item * &build_recipient_list()
1.618     raeburn  7961: 
                   7962: Build recipient lists for three types of e-mail:
                   7963: (a) Error Reports, (b) Package Updates, (c) Help requests, generated by
1.619     raeburn  7964: lonerrorhandler.pm, CHECKRPMS and lonsupportreq.pm respectively.
1.618     raeburn  7965: 
                   7966: Inputs:
1.619     raeburn  7967: defmail (scalar - email address of default recipient), 
1.618     raeburn  7968: mailing type (scalar - errormail, packagesmail, or helpdeskmail), 
1.619     raeburn  7969: defdom (domain for which to retrieve configuration settings),
                   7970: origmail (scalar - email address of recipient from loncapa.conf, 
                   7971: i.e., predates configuration by DC via domainprefs.pm 
1.618     raeburn  7972: 
1.655     raeburn  7973: Returns: comma separated list of addresses to which to send e-mail.
                   7974: 
                   7975: =back
1.618     raeburn  7976: 
                   7977: =cut
                   7978: 
                   7979: ############################################################
                   7980: ############################################################
                   7981: sub build_recipient_list {
1.619     raeburn  7982:     my ($defmail,$mailing,$defdom,$origmail) = @_;
1.618     raeburn  7983:     my @recipients;
                   7984:     my $otheremails;
                   7985:     my %domconfig =
                   7986:          &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
                   7987:     if (ref($domconfig{'contacts'}) eq 'HASH') {
                   7988:         if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
                   7989:             my @contacts = ('adminemail','supportemail');
                   7990:             foreach my $item (@contacts) {
                   7991:                 if ($domconfig{'contacts'}{$mailing}{$item}) {
1.619     raeburn  7992:                     my $addr = $domconfig{'contacts'}{$item}; 
                   7993:                     if (!grep(/^\Q$addr\E$/,@recipients)) {
                   7994:                         push(@recipients,$addr);
                   7995:                     }
1.618     raeburn  7996:                 }
                   7997:                 $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
                   7998:             }
                   7999:         }
1.619     raeburn  8000:     } elsif ($origmail ne '') {
                   8001:         push(@recipients,$origmail);
1.618     raeburn  8002:     }
                   8003:     if ($defmail ne '') {
                   8004:         push(@recipients,$defmail);
                   8005:     }
                   8006:     if ($otheremails) {
1.619     raeburn  8007:         my @others;
                   8008:         if ($otheremails =~ /,/) {
                   8009:             @others = split(/,/,$otheremails);
1.618     raeburn  8010:         } else {
1.619     raeburn  8011:             push(@others,$otheremails);
                   8012:         }
                   8013:         foreach my $addr (@others) {
                   8014:             if (!grep(/^\Q$addr\E$/,@recipients)) {
                   8015:                 push(@recipients,$addr);
                   8016:             }
1.618     raeburn  8017:         }
                   8018:     }
1.619     raeburn  8019:     my $recipientlist = join(',',@recipients); 
1.618     raeburn  8020:     return $recipientlist;
                   8021: }
                   8022: 
1.127     matthew  8023: ############################################################
                   8024: ############################################################
1.154     albertel 8025: 
1.655     raeburn  8026: =pod
                   8027: 
                   8028: =head1 Course Catalog Routines
                   8029: 
                   8030: =over 4
                   8031: 
                   8032: =item * &gather_categories()
                   8033: 
                   8034: Converts category definitions - keys of categories hash stored in  
                   8035: coursecategories in configuration.db on the primary library server in a 
                   8036: domain - to an array.  Also generates javascript and idx hash used to 
                   8037: generate Domain Coordinator interface for editing Course Categories.
                   8038: 
                   8039: Inputs:
                   8040: categories (reference to hash of category definitions).
                   8041: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   8042:       categories and subcategories).
                   8043: idx (reference to hash of counters used in Domain Coordinator interface for 
                   8044:       editing Course Categories).
                   8045: jsarray (reference to array of categories used to create Javascript arrays for
                   8046:          Domain Coordinator interface for editing Course Categories).
                   8047: 
                   8048: Returns: nothing
                   8049: 
                   8050: Side effects: populates cats, idx and jsarray. 
                   8051: 
                   8052: =cut
                   8053: 
                   8054: sub gather_categories {
                   8055:     my ($categories,$cats,$idx,$jsarray) = @_;
                   8056:     my %counters;
                   8057:     my $num = 0;
                   8058:     foreach my $item (keys(%{$categories})) {
                   8059:         my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
                   8060:         if ($container eq '' && $depth == 0) {
                   8061:             $cats->[$depth][$categories->{$item}] = $cat;
                   8062:         } else {
                   8063:             $cats->[$depth]{$container}[$categories->{$item}] = $cat;
                   8064:         }
                   8065:         my ($escitem,$tail) = split(/:/,$item,2);
                   8066:         if ($counters{$tail} eq '') {
                   8067:             $counters{$tail} = $num;
                   8068:             $num ++;
                   8069:         }
                   8070:         if (ref($idx) eq 'HASH') {
                   8071:             $idx->{$item} = $counters{$tail};
                   8072:         }
                   8073:         if (ref($jsarray) eq 'ARRAY') {
                   8074:             push(@{$jsarray->[$counters{$tail}]},$item);
                   8075:         }
                   8076:     }
                   8077:     return;
                   8078: }
                   8079: 
                   8080: =pod
                   8081: 
                   8082: =item * &extract_categories()
                   8083: 
                   8084: Used to generate breadcrumb trails for course categories.
                   8085: 
                   8086: Inputs:
                   8087: categories (reference to hash of category definitions).
                   8088: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   8089:       categories and subcategories).
                   8090: trails (reference to array of breacrumb trails for each category).
                   8091: allitems (reference to hash - key is category key 
                   8092:          (format: escaped(name):escaped(parent category):depth in hierarchy).
                   8093: idx (reference to hash of counters used in Domain Coordinator interface for
                   8094:       editing Course Categories).
                   8095: jsarray (reference to array of categories used to create Javascript arrays for
                   8096:          Domain Coordinator interface for editing Course Categories).
                   8097: 
                   8098: Returns: nothing
                   8099: 
                   8100: Side effects: populates trails and allitems hash references.
                   8101: 
                   8102: =cut
                   8103: 
                   8104: sub extract_categories {
                   8105:     my ($categories,$cats,$trails,$allitems,$idx,$jsarray) = @_;
                   8106:     if (ref($categories) eq 'HASH') {
                   8107:         &gather_categories($categories,$cats,$idx,$jsarray);
                   8108:         if (ref($cats->[0]) eq 'ARRAY') {
                   8109:             for (my $i=0; $i<@{$cats->[0]}; $i++) {
                   8110:                 my $name = $cats->[0][$i];
                   8111:                 my $item = &escape($name).'::0';
                   8112:                 my $trailstr;
                   8113:                 if ($name eq 'instcode') {
                   8114:                     $trailstr = &mt('Official courses (with institutional codes)');
                   8115:                 } else {
                   8116:                     $trailstr = $name;
                   8117:                 }
                   8118:                 if ($allitems->{$item} eq '') {
                   8119:                     push(@{$trails},$trailstr);
                   8120:                     $allitems->{$item} = scalar(@{$trails})-1;
                   8121:                 }
                   8122:                 my @parents = ($name);
                   8123:                 if (ref($cats->[1]{$name}) eq 'ARRAY') {
                   8124:                     for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
                   8125:                         my $category = $cats->[1]{$name}[$j];
                   8126:                         &recurse_categories($cats,2,$category,$trails,$allitems,\@parents);
                   8127:                     }
                   8128:                 }
                   8129:             }
                   8130:         }
                   8131:     }
                   8132:     return;
                   8133: }
                   8134: 
                   8135: =pod
                   8136: 
                   8137: =item *&recurse_categories()
                   8138: 
                   8139: Recursively used to generate breadcrumb trails for course categories.
                   8140: 
                   8141: Inputs:
                   8142: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   8143:       categories and subcategories).
                   8144: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
                   8145: category (current course category, for which breadcrumb trail is being generated).   
                   8146: trails (reference to array of breacrumb trails for each category).
                   8147: allitems (reference to hash - key is category key
                   8148:          (format: escaped(name):escaped(parent category):depth in hierarchy).
                   8149: parents (array containing containers directories for current category, 
                   8150:          back to top level). 
                   8151: 
                   8152: Returns: nothing
                   8153: 
                   8154: Side effects: populates trails and allitems hash references
                   8155: 
                   8156: =back
                   8157: 
                   8158: =cut
                   8159: 
                   8160: sub recurse_categories {
                   8161:     my ($cats,$depth,$category,$trails,$allitems,$parents) = @_;
                   8162:     my $shallower = $depth - 1;
                   8163:     if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
                   8164:         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
                   8165:             my $name = $cats->[$depth]{$category}[$k];
                   8166:             my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   8167:             my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   8168:             if ($allitems->{$item} eq '') {
                   8169:                 push(@{$trails},$trailstr);
                   8170:                 $allitems->{$item} = scalar(@{$trails})-1;
                   8171:             }
                   8172:             my $deeper = $depth+1;
                   8173:             push(@{$parents},$category);
                   8174:             &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents);
                   8175:             pop(@{$parents});
                   8176:         }
                   8177:     } else {
                   8178:         my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   8179:         my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   8180:         if ($allitems->{$item} eq '') {
                   8181:             push(@{$trails},$trailstr);
                   8182:             $allitems->{$item} = scalar(@{$trails})-1;
                   8183:         }
                   8184:     }
                   8185:     return;
                   8186: }
                   8187: 
                   8188: ############################################################
                   8189: ############################################################
                   8190: 
                   8191: 
1.443     albertel 8192: sub commit_customrole {
                   8193:     my ($udom,$uname,$url,$three,$four,$five,$start,$end) = @_;
1.630     raeburn  8194:     my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443     albertel 8195:                          ($start?', '.&mt('starting').' '.localtime($start):'').
                   8196:                          ($end?', ending '.localtime($end):'').': <b>'.
                   8197:               &Apache::lonnet::assigncustomrole(
                   8198:                  $udom,$uname,$url,$three,$four,$five,$end,$start).
                   8199:                  '</b><br />';
                   8200:     return $output;
                   8201: }
                   8202: 
                   8203: sub commit_standardrole {
1.541     raeburn  8204:     my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
                   8205:     my ($output,$logmsg,$linefeed);
                   8206:     if ($context eq 'auto') {
                   8207:         $linefeed = "\n";
                   8208:     } else {
                   8209:         $linefeed = "<br />\n";
                   8210:     }  
1.443     albertel 8211:     if ($three eq 'st') {
1.541     raeburn  8212:         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
                   8213:                                          $one,$two,$sec,$context);
                   8214:         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
1.626     raeburn  8215:             ($result eq 'unknown_course') || ($result eq 'refused')) {
                   8216:             $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
1.443     albertel 8217:         } else {
1.541     raeburn  8218:             $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443     albertel 8219:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  8220:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
                   8221:             if ($context eq 'auto') {
                   8222:                 $output .= $result.$linefeed.&mt('Add to classlist').': ok';
                   8223:             } else {
                   8224:                $output .= '<b>'.$result.'</b>'.$linefeed.
                   8225:                &mt('Add to classlist').': <b>ok</b>';
                   8226:             }
                   8227:             $output .= $linefeed;
1.443     albertel 8228:         }
                   8229:     } else {
                   8230:         $output = &mt('Assigning').' '.$three.' in '.$url.
                   8231:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  8232:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652     raeburn  8233:         my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541     raeburn  8234:         if ($context eq 'auto') {
                   8235:             $output .= $result.$linefeed;
                   8236:         } else {
                   8237:             $output .= '<b>'.$result.'</b>'.$linefeed;
                   8238:         }
1.443     albertel 8239:     }
                   8240:     return $output;
                   8241: }
                   8242: 
                   8243: sub commit_studentrole {
1.541     raeburn  8244:     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
1.626     raeburn  8245:     my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541     raeburn  8246:     if ($context eq 'auto') {
                   8247:         $linefeed = "\n";
                   8248:     } else {
                   8249:         $linefeed = '<br />'."\n";
                   8250:     }
1.443     albertel 8251:     if (defined($one) && defined($two)) {
                   8252:         my $cid=$one.'_'.$two;
                   8253:         my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
                   8254:         my $secchange = 0;
                   8255:         my $expire_role_result;
                   8256:         my $modify_section_result;
1.628     raeburn  8257:         if ($oldsec ne '-1') { 
                   8258:             if ($oldsec ne $sec) {
1.443     albertel 8259:                 $secchange = 1;
1.628     raeburn  8260:                 my $now = time;
1.443     albertel 8261:                 my $uurl='/'.$cid;
                   8262:                 $uurl=~s/\_/\//g;
                   8263:                 if ($oldsec) {
                   8264:                     $uurl.='/'.$oldsec;
                   8265:                 }
1.626     raeburn  8266:                 $oldsecurl = $uurl;
1.628     raeburn  8267:                 $expire_role_result = 
1.652     raeburn  8268:                     &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628     raeburn  8269:                 if ($env{'request.course.sec'} ne '') { 
                   8270:                     if ($expire_role_result eq 'refused') {
                   8271:                         my @roles = ('st');
                   8272:                         my @statuses = ('previous');
                   8273:                         my @roledoms = ($one);
                   8274:                         my $withsec = 1;
                   8275:                         my %roleshash = 
                   8276:                             &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
                   8277:                                               \@statuses,\@roles,\@roledoms,$withsec);
                   8278:                         if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
                   8279:                             my ($oldstart,$oldend) = 
                   8280:                                 split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
                   8281:                             if ($oldend > 0 && $oldend <= $now) {
                   8282:                                 $expire_role_result = 'ok';
                   8283:                             }
                   8284:                         }
                   8285:                     }
                   8286:                 }
1.443     albertel 8287:                 $result = $expire_role_result;
                   8288:             }
                   8289:         }
                   8290:         if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.652     raeburn  8291:             $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid,'',$context);
1.443     albertel 8292:             if ($modify_section_result =~ /^ok/) {
                   8293:                 if ($secchange == 1) {
1.628     raeburn  8294:                     if ($sec eq '') {
                   8295:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
                   8296:                     } else {
                   8297:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
                   8298:                     }
1.443     albertel 8299:                 } elsif ($oldsec eq '-1') {
1.628     raeburn  8300:                     if ($sec eq '') {
                   8301:                         $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
                   8302:                     } else {
                   8303:                         $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   8304:                     }
1.443     albertel 8305:                 } else {
1.628     raeburn  8306:                     if ($sec eq '') {
                   8307:                         $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
                   8308:                     } else {
                   8309:                         $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   8310:                     }
1.443     albertel 8311:                 }
                   8312:             } else {
1.628     raeburn  8313:                 if ($secchange) {       
                   8314:                     $$logmsg .= &mt('Error when attempting section change for [_1] from old section "[_2]" to new section: "[_3]" in course [_4] -error:',$uname,$oldsec,$sec,$cid).' '.$modify_section_result.$linefeed;
                   8315:                 } else {
                   8316:                     $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
                   8317:                 }
1.443     albertel 8318:             }
                   8319:             $result = $modify_section_result;
                   8320:         } elsif ($secchange == 1) {
1.628     raeburn  8321:             if ($oldsec eq '') {
                   8322:                 $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_3] -error: ',$uname,$cid).' '.$expire_role_result.$linefeed;
                   8323:             } else {
                   8324:                 $$logmsg .= &mt('Error when attempting to expire existing role for [_1] in section [_2] in course [_3] -error: ',$uname,$oldsec,$cid).' '.$expire_role_result.$linefeed;
                   8325:             }
1.626     raeburn  8326:             if ($expire_role_result eq 'refused') {
                   8327:                 my $newsecurl = '/'.$cid;
                   8328:                 $newsecurl =~ s/\_/\//g;
                   8329:                 if ($sec ne '') {
                   8330:                     $newsecurl.='/'.$sec;
                   8331:                 }
                   8332:                 if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
                   8333:                     if ($sec eq '') {
                   8334:                         $$logmsg .= &mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments unaffiliated with any section.',$sec).$linefeed;
                   8335:                     } else {
                   8336:                         $$logmsg .= &mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments in other sections.',$sec).$linefeed;
                   8337:                     }
                   8338:                 }
                   8339:             }
1.443     albertel 8340:         }
                   8341:     } else {
1.626     raeburn  8342:         $$logmsg .= &mt('Incomplete course id defined.').$linefeed.&mt('Addition of user [_1] from domain [_2] to course [_3], section [_4] not completed.',$uname,$udom,$one.'_'.$two,$sec).$linefeed;
1.443     albertel 8343:         $result = "error: incomplete course id\n";
                   8344:     }
                   8345:     return $result;
                   8346: }
                   8347: 
                   8348: ############################################################
                   8349: ############################################################
                   8350: 
1.566     albertel 8351: sub check_clone {
1.578     raeburn  8352:     my ($args,$linefeed) = @_;
1.566     albertel 8353:     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
                   8354:     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
                   8355:     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
                   8356:     my $clonemsg;
                   8357:     my $can_clone = 0;
                   8358: 
                   8359:     if ($clonehome eq 'no_host') {
1.578     raeburn  8360:         $clonemsg = &mt('No new course created.').$linefeed.&mt('A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});     
1.566     albertel 8361:     } else {
                   8362: 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.568     albertel 8363: 	if ($env{'request.role.domain'} eq $args->{'clonedomain'}) {
1.566     albertel 8364: 	    $can_clone = 1;
                   8365: 	} else {
                   8366: 	    my %clonehash = &Apache::lonnet::get('environment',['cloners'],
                   8367: 						 $args->{'clonedomain'},$args->{'clonecourse'});
                   8368: 	    my @cloners = split(/,/,$clonehash{'cloners'});
1.578     raeburn  8369:             if (grep(/^\*$/,@cloners)) {
                   8370:                 $can_clone = 1;
                   8371:             } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
                   8372:                 $can_clone = 1;
                   8373:             } else {
                   8374: 	        my %roleshash =
                   8375: 		    &Apache::lonnet::get_my_roles($args->{'ccuname'},
                   8376: 					 $args->{'ccdomain'},
                   8377:                                          'userroles',['active'],['cc'],
                   8378: 					 [$args->{'clonedomain'}]);
                   8379: 	        if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':cc'}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
                   8380: 		    $can_clone = 1;
                   8381: 	        } else {
                   8382:                     $clonemsg = &mt('No new course created.').$linefeed.&mt('The new course could not be cloned from the existing course because the new course owner ([_1]) does not have cloning rights in the existing course ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
                   8383: 	        }
1.566     albertel 8384: 	    }
1.578     raeburn  8385:         }
1.566     albertel 8386:     }
                   8387:     return ($can_clone, $clonemsg, $cloneid, $clonehome);
                   8388: }
                   8389: 
1.444     albertel 8390: sub construct_course {
1.541     raeburn  8391:     my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context) = @_;
1.444     albertel 8392:     my $outcome;
1.541     raeburn  8393:     my $linefeed =  '<br />'."\n";
                   8394:     if ($context eq 'auto') {
                   8395:         $linefeed = "\n";
                   8396:     }
1.566     albertel 8397: 
                   8398: #
                   8399: # Are we cloning?
                   8400: #
                   8401:     my ($can_clone, $clonemsg, $cloneid, $clonehome);
                   8402:     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578     raeburn  8403: 	($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566     albertel 8404: 	if ($context ne 'auto') {
1.578     raeburn  8405:             if ($clonemsg ne '') {
                   8406: 	        $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
                   8407:             }
1.566     albertel 8408: 	}
                   8409: 	$outcome .= $clonemsg.$linefeed;
                   8410: 
                   8411:         if (!$can_clone) {
                   8412: 	    return (0,$outcome);
                   8413: 	}
                   8414:     }
                   8415: 
1.444     albertel 8416: #
                   8417: # Open course
                   8418: #
                   8419:     my $crstype = lc($args->{'crstype'});
                   8420:     my %cenv=();
                   8421:     $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
                   8422:                                              $args->{'cdescr'},
                   8423:                                              $args->{'curl'},
                   8424:                                              $args->{'course_home'},
                   8425:                                              $args->{'nonstandard'},
                   8426:                                              $args->{'crscode'},
                   8427:                                              $args->{'ccuname'}.':'.
                   8428:                                              $args->{'ccdomain'},
                   8429:                                              $args->{'crstype'});
                   8430: 
                   8431:     # Note: The testing routines depend on this being output; see 
                   8432:     # Utils::Course. This needs to at least be output as a comment
                   8433:     # if anyone ever decides to not show this, and Utils::Course::new
                   8434:     # will need to be suitably modified.
1.541     raeburn  8435:     $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
1.444     albertel 8436: #
                   8437: # Check if created correctly
                   8438: #
1.479     albertel 8439:     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444     albertel 8440:     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.541     raeburn  8441:     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566     albertel 8442: 
1.444     albertel 8443: #
1.566     albertel 8444: # Do the cloning
                   8445: #   
                   8446:     if ($can_clone && $cloneid) {
                   8447: 	$clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
                   8448: 	if ($context ne 'auto') {
                   8449: 	    $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
                   8450: 	}
                   8451: 	$outcome .= $clonemsg.$linefeed;
                   8452: 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444     albertel 8453: # Copy all files
1.637     www      8454: 	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
1.444     albertel 8455: # Restore URL
1.566     albertel 8456: 	$cenv{'url'}=$oldcenv{'url'};
1.444     albertel 8457: # Restore title
1.566     albertel 8458: 	$cenv{'description'}=$oldcenv{'description'};
1.444     albertel 8459: # Mark as cloned
1.566     albertel 8460: 	$cenv{'clonedfrom'}=$cloneid;
1.638     www      8461: # Need to clone grading mode
                   8462:         my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
                   8463:         $cenv{'grading'}=$newenv{'grading'};
                   8464: # Do not clone these environment entries
                   8465:         &Apache::lonnet::del('environment',
                   8466:                   ['default_enrollment_start_date',
                   8467:                    'default_enrollment_end_date',
                   8468:                    'question.email',
                   8469:                    'policy.email',
                   8470:                    'comment.email',
                   8471:                    'pch.users.denied',
                   8472:                    'plc.users.denied'],
                   8473:                    $$crsudom,$$crsunum);
1.444     albertel 8474:     }
1.566     albertel 8475: 
1.444     albertel 8476: #
                   8477: # Set environment (will override cloned, if existing)
                   8478: #
                   8479:     my @sections = ();
                   8480:     my @xlists = ();
                   8481:     if ($args->{'crstype'}) {
                   8482:         $cenv{'type'}=$args->{'crstype'};
                   8483:     }
                   8484:     if ($args->{'crsid'}) {
                   8485:         $cenv{'courseid'}=$args->{'crsid'};
                   8486:     }
                   8487:     if ($args->{'crscode'}) {
                   8488:         $cenv{'internal.coursecode'}=$args->{'crscode'};
                   8489:     }
                   8490:     if ($args->{'crsquota'} ne '') {
                   8491:         $cenv{'internal.coursequota'}=$args->{'crsquota'};
                   8492:     } else {
                   8493:         $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
                   8494:     }
                   8495:     if ($args->{'ccuname'}) {
                   8496:         $cenv{'internal.courseowner'} = $args->{'ccuname'}.
                   8497:                                         ':'.$args->{'ccdomain'};
                   8498:     } else {
                   8499:         $cenv{'internal.courseowner'} = $args->{'curruser'};
                   8500:     }
                   8501:     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
                   8502:     if ($args->{'crssections'}) {
                   8503:         $cenv{'internal.sectionnums'} = '';
                   8504:         if ($args->{'crssections'} =~ m/,/) {
                   8505:             @sections = split/,/,$args->{'crssections'};
                   8506:         } else {
                   8507:             $sections[0] = $args->{'crssections'};
                   8508:         }
                   8509:         if (@sections > 0) {
                   8510:             foreach my $item (@sections) {
                   8511:                 my ($sec,$gp) = split/:/,$item;
                   8512:                 my $class = $args->{'crscode'}.$sec;
                   8513:                 my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
                   8514:                 $cenv{'internal.sectionnums'} .= $item.',';
                   8515:                 unless ($addcheck eq 'ok') {
                   8516:                     push @badclasses, $class;
                   8517:                 }
                   8518:             }
                   8519:             $cenv{'internal.sectionnums'} =~ s/,$//;
                   8520:         }
                   8521:     }
                   8522: # do not hide course coordinator from staff listing, 
                   8523: # even if privileged
                   8524:     $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   8525: # add crosslistings
                   8526:     if ($args->{'crsxlist'}) {
                   8527:         $cenv{'internal.crosslistings'}='';
                   8528:         if ($args->{'crsxlist'} =~ m/,/) {
                   8529:             @xlists = split/,/,$args->{'crsxlist'};
                   8530:         } else {
                   8531:             $xlists[0] = $args->{'crsxlist'};
                   8532:         }
                   8533:         if (@xlists > 0) {
                   8534:             foreach my $item (@xlists) {
                   8535:                 my ($xl,$gp) = split/:/,$item;
                   8536:                 my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
                   8537:                 $cenv{'internal.crosslistings'} .= $item.',';
                   8538:                 unless ($addcheck eq 'ok') {
                   8539:                     push @badclasses, $xl;
                   8540:                 }
                   8541:             }
                   8542:             $cenv{'internal.crosslistings'} =~ s/,$//;
                   8543:         }
                   8544:     }
                   8545:     if ($args->{'autoadds'}) {
                   8546:         $cenv{'internal.autoadds'}=$args->{'autoadds'};
                   8547:     }
                   8548:     if ($args->{'autodrops'}) {
                   8549:         $cenv{'internal.autodrops'}=$args->{'autodrops'};
                   8550:     }
                   8551: # check for notification of enrollment changes
                   8552:     my @notified = ();
                   8553:     if ($args->{'notify_owner'}) {
                   8554:         if ($args->{'ccuname'} ne '') {
                   8555:             push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
                   8556:         }
                   8557:     }
                   8558:     if ($args->{'notify_dc'}) {
                   8559:         if ($uname ne '') { 
1.630     raeburn  8560:             push(@notified,$uname.':'.$udom);
1.444     albertel 8561:         }
                   8562:     }
                   8563:     if (@notified > 0) {
                   8564:         my $notifylist;
                   8565:         if (@notified > 1) {
                   8566:             $notifylist = join(',',@notified);
                   8567:         } else {
                   8568:             $notifylist = $notified[0];
                   8569:         }
                   8570:         $cenv{'internal.notifylist'} = $notifylist;
                   8571:     }
                   8572:     if (@badclasses > 0) {
                   8573:         my %lt=&Apache::lonlocal::texthash(
                   8574:                 'tclb' => 'The courses listed below were included as sections or crosslistings affiliated with your new LON-CAPA course.  However, if automated course roster updates are enabled for this class, these particular sections/crosslistings will not contribute towards enrollment, because the user identified as the course owner for this LON-CAPA course',
                   8575:                 'dnhr' => 'does not have rights to access enrollment in these classes',
                   8576:                 'adby' => 'as determined by the policies of your institution on access to official classlists'
                   8577:         );
1.541     raeburn  8578:         my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
                   8579:                            ' ('.$lt{'adby'}.')';
                   8580:         if ($context eq 'auto') {
                   8581:             $outcome .= $badclass_msg.$linefeed;
1.566     albertel 8582:             $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.541     raeburn  8583:             foreach my $item (@badclasses) {
                   8584:                 if ($context eq 'auto') {
                   8585:                     $outcome .= " - $item\n";
                   8586:                 } else {
                   8587:                     $outcome .= "<li>$item</li>\n";
                   8588:                 }
                   8589:             }
                   8590:             if ($context eq 'auto') {
                   8591:                 $outcome .= $linefeed;
                   8592:             } else {
1.566     albertel 8593:                 $outcome .= "</ul><br /><br /></div>\n";
1.541     raeburn  8594:             }
                   8595:         } 
1.444     albertel 8596:     }
                   8597:     if ($args->{'no_end_date'}) {
                   8598:         $args->{'endaccess'} = 0;
                   8599:     }
                   8600:     $cenv{'internal.autostart'}=$args->{'enrollstart'};
                   8601:     $cenv{'internal.autoend'}=$args->{'enrollend'};
                   8602:     $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
                   8603:     $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
                   8604:     if ($args->{'showphotos'}) {
                   8605:       $cenv{'internal.showphotos'}=$args->{'showphotos'};
                   8606:     }
                   8607:     $cenv{'internal.authtype'} = $args->{'authtype'};
                   8608:     $cenv{'internal.autharg'} = $args->{'autharg'}; 
                   8609:     if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
                   8610:         if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'}  eq '') {
1.541     raeburn  8611:             my $krb_msg = &mt('As you did not include the default Kerberos domain to be used for authentication in this class, the institutional data used by the automated enrollment process must include the Kerberos domain for each new student'); 
                   8612:             if ($context eq 'auto') {
                   8613:                 $outcome .= $krb_msg;
                   8614:             } else {
1.566     albertel 8615:                 $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541     raeburn  8616:             }
                   8617:             $outcome .= $linefeed;
1.444     albertel 8618:         }
                   8619:     }
                   8620:     if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
                   8621:        if ($args->{'setpolicy'}) {
                   8622:            $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   8623:        }
                   8624:        if ($args->{'setcontent'}) {
                   8625:            $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   8626:        }
                   8627:     }
                   8628:     if ($args->{'reshome'}) {
                   8629: 	$cenv{'reshome'}=$args->{'reshome'}.'/';
                   8630: 	$cenv{'reshome'}=~s/\/+$/\//;
                   8631:     }
                   8632: #
                   8633: # course has keyed access
                   8634: #
                   8635:     if ($args->{'setkeys'}) {
                   8636:        $cenv{'keyaccess'}='yes';
                   8637:     }
                   8638: # if specified, key authority is not course, but user
                   8639: # only active if keyaccess is yes
                   8640:     if ($args->{'keyauth'}) {
1.487     albertel 8641: 	my ($user,$domain) = split(':',$args->{'keyauth'});
                   8642: 	$user = &LONCAPA::clean_username($user);
                   8643: 	$domain = &LONCAPA::clean_username($domain);
1.488     foxr     8644: 	if ($user ne '' && $domain ne '') {
1.487     albertel 8645: 	    $cenv{'keyauth'}=$user.':'.$domain;
1.444     albertel 8646: 	}
                   8647:     }
                   8648: 
                   8649:     if ($args->{'disresdis'}) {
                   8650:         $cenv{'pch.roles.denied'}='st';
                   8651:     }
                   8652:     if ($args->{'disablechat'}) {
                   8653:         $cenv{'plc.roles.denied'}='st';
                   8654:     }
                   8655: 
                   8656:     # Record we've not yet viewed the Course Initialization Helper for this 
                   8657:     # course
                   8658:     $cenv{'course.helper.not.run'} = 1;
                   8659:     #
                   8660:     # Use new Randomseed
                   8661:     #
                   8662:     $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
                   8663:     $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
                   8664:     #
                   8665:     # The encryption code and receipt prefix for this course
                   8666:     #
                   8667:     $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
                   8668:     $cenv{'internal.encpref'}=100+int(9*rand(99));
                   8669:     #
                   8670:     # By default, use standard grading
                   8671:     if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
                   8672: 
1.541     raeburn  8673:     $outcome .= $linefeed.&mt('Setting environment').': '.                 
                   8674:           &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 8675: #
                   8676: # Open all assignments
                   8677: #
                   8678:     if ($args->{'openall'}) {
                   8679:        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
                   8680:        my %storecontent = ($storeunder         => time,
                   8681:                            $storeunder.'.type' => 'date_start');
                   8682:        
                   8683:        $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541     raeburn  8684:                  ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 8685:    }
                   8686: #
                   8687: # Set first page
                   8688: #
                   8689:     unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
                   8690: 	    || ($cloneid)) {
1.445     albertel 8691: 	use LONCAPA::map;
1.444     albertel 8692: 	$outcome .= &mt('Setting first resource').': ';
1.445     albertel 8693: 
                   8694: 	my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
                   8695:         my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
                   8696: 
1.444     albertel 8697:         $outcome .= ($fatal?$errtext:'read ok').' - ';
                   8698:         my $title; my $url;
                   8699:         if ($args->{'firstres'} eq 'syl') {
                   8700: 	    $title='Syllabus';
                   8701:             $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
                   8702:         } else {
                   8703:             $title='Navigate Contents';
                   8704:             $url='/adm/navmaps';
                   8705:         }
1.445     albertel 8706: 
                   8707:         $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
                   8708: 	(my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
                   8709: 
                   8710: 	if ($errtext) { $fatal=2; }
1.541     raeburn  8711:         $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444     albertel 8712:     }
1.566     albertel 8713: 
                   8714:     return (1,$outcome);
1.444     albertel 8715: }
                   8716: 
                   8717: ############################################################
                   8718: ############################################################
                   8719: 
1.378     raeburn  8720: sub course_type {
                   8721:     my ($cid) = @_;
                   8722:     if (!defined($cid)) {
                   8723:         $cid = $env{'request.course.id'};
                   8724:     }
1.404     albertel 8725:     if (defined($env{'course.'.$cid.'.type'})) {
                   8726:         return $env{'course.'.$cid.'.type'};
1.378     raeburn  8727:     } else {
                   8728:         return 'Course';
1.377     raeburn  8729:     }
                   8730: }
1.156     albertel 8731: 
1.406     raeburn  8732: sub group_term {
                   8733:     my $crstype = &course_type();
                   8734:     my %names = (
                   8735:                   'Course' => 'group',
                   8736:                   'Group' => 'team',
                   8737:                 );
                   8738:     return $names{$crstype};
                   8739: }
                   8740: 
1.156     albertel 8741: sub icon {
                   8742:     my ($file)=@_;
1.505     albertel 8743:     my $curfext = lc((split(/\./,$file))[-1]);
1.168     albertel 8744:     my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156     albertel 8745:     my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168     albertel 8746:     if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
                   8747: 	if (-e  $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                   8748: 	          $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   8749: 	            $curfext.".gif") {
                   8750: 	    $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   8751: 		$curfext.".gif";
                   8752: 	}
                   8753:     }
1.249     albertel 8754:     return &lonhttpdurl($iconname);
1.154     albertel 8755: } 
1.84      albertel 8756: 
1.575     albertel 8757: sub lonhttpd_port {
1.215     albertel 8758:     my $lonhttpd_port=$Apache::lonnet::perlvar{'lonhttpdPort'};
                   8759:     if (!defined($lonhttpd_port)) { $lonhttpd_port='8080'; }
1.574     albertel 8760:     # IE doesn't like a secure page getting images from a non-secure
                   8761:     # port (when logging we haven't parsed the browser type so default
                   8762:     # back to secure
                   8763:     if ((!exists($env{'browser.type'}) || $env{'browser.type'} eq 'explorer')
                   8764: 	&& $ENV{'SERVER_PORT'} == 443) {
1.575     albertel 8765: 	return 443;
                   8766:     }
                   8767:     return $lonhttpd_port;
                   8768: 
                   8769: }
                   8770: 
                   8771: sub lonhttpdurl {
                   8772:     my ($url)=@_;
                   8773: 
                   8774:     my $lonhttpd_port = &lonhttpd_port();
                   8775:     if ($lonhttpd_port == 443) {
1.574     albertel 8776: 	return 'https://'.$ENV{'SERVER_NAME'}.$url;
                   8777:     }
1.215     albertel 8778:     return 'http://'.$ENV{'SERVER_NAME'}.':'.$lonhttpd_port.$url;
                   8779: }
                   8780: 
1.213     albertel 8781: sub connection_aborted {
                   8782:     my ($r)=@_;
                   8783:     $r->print(" ");$r->rflush();
                   8784:     my $c = $r->connection;
                   8785:     return $c->aborted();
                   8786: }
                   8787: 
1.221     foxr     8788: #    Escapes strings that may have embedded 's that will be put into
1.222     foxr     8789: #    strings as 'strings'.
                   8790: sub escape_single {
1.221     foxr     8791:     my ($input) = @_;
1.223     albertel 8792:     $input =~ s/\\/\\\\/g;	# Escape the \'s..(must be first)>
1.221     foxr     8793:     $input =~ s/\'/\\\'/g;	# Esacpe the 's....
                   8794:     return $input;
                   8795: }
1.223     albertel 8796: 
1.222     foxr     8797: #  Same as escape_single, but escape's "'s  This 
                   8798: #  can be used for  "strings"
                   8799: sub escape_double {
                   8800:     my ($input) = @_;
                   8801:     $input =~ s/\\/\\\\/g;	# Escape the /'s..(must be first)>
                   8802:     $input =~ s/\"/\\\"/g;	# Esacpe the "s....
                   8803:     return $input;
                   8804: }
1.223     albertel 8805:  
1.222     foxr     8806: #   Escapes the last element of a full URL.
                   8807: sub escape_url {
                   8808:     my ($url)   = @_;
1.238     raeburn  8809:     my @urlslices = split(/\//, $url,-1);
1.369     www      8810:     my $lastitem = &escape(pop(@urlslices));
1.223     albertel 8811:     return join('/',@urlslices).'/'.$lastitem;
1.222     foxr     8812: }
1.462     albertel 8813: 
                   8814: # -------------------------------------------------------- Initliaze user login
                   8815: sub init_user_environment {
1.463     albertel 8816:     my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462     albertel 8817:     my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
                   8818: 
                   8819:     my $public=($username eq 'public' && $domain eq 'public');
                   8820: 
                   8821: # See if old ID present, if so, remove
                   8822: 
                   8823:     my ($filename,$cookie,$userroles);
                   8824:     my $now=time;
                   8825: 
                   8826:     if ($public) {
                   8827: 	my $max_public=100;
                   8828: 	my $oldest;
                   8829: 	my $oldest_time=0;
                   8830: 	for(my $next=1;$next<=$max_public;$next++) {
                   8831: 	    if (-e $lonids."/publicuser_$next.id") {
                   8832: 		my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
                   8833: 		if ($mtime<$oldest_time || !$oldest_time) {
                   8834: 		    $oldest_time=$mtime;
                   8835: 		    $oldest=$next;
                   8836: 		}
                   8837: 	    } else {
                   8838: 		$cookie="publicuser_$next";
                   8839: 		last;
                   8840: 	    }
                   8841: 	}
                   8842: 	if (!$cookie) { $cookie="publicuser_$oldest"; }
                   8843:     } else {
1.463     albertel 8844: 	# if this isn't a robot, kill any existing non-robot sessions
                   8845: 	if (!$args->{'robot'}) {
                   8846: 	    opendir(DIR,$lonids);
                   8847: 	    while ($filename=readdir(DIR)) {
                   8848: 		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
                   8849: 		    unlink($lonids.'/'.$filename);
                   8850: 		}
1.462     albertel 8851: 	    }
1.463     albertel 8852: 	    closedir(DIR);
1.462     albertel 8853: 	}
                   8854: # Give them a new cookie
1.463     albertel 8855: 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
                   8856: 		                   : $now);
                   8857: 	$cookie="$username\_$id\_$domain\_$authhost";
1.462     albertel 8858:     
                   8859: # Initialize roles
                   8860: 
                   8861: 	$userroles=&Apache::lonnet::rolesinit($domain,$username,$authhost);
                   8862:     }
                   8863: # ------------------------------------ Check browser type and MathML capability
                   8864: 
                   8865:     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
                   8866:         $clientunicode,$clientos) = &decode_user_agent($r);
                   8867: 
                   8868: # -------------------------------------- Any accessibility options to remember?
                   8869:     if (($form->{'interface'}) && ($form->{'remember'} eq 'true')) {
                   8870: 	foreach my $option ('imagesuppress','appletsuppress',
                   8871: 			    'embedsuppress','fontenhance','blackwhite') {
                   8872: 	    if ($form->{$option} eq 'true') {
                   8873: 		&Apache::lonnet::put('environment',{$option => 'on'},
                   8874: 				     $domain,$username);
                   8875: 	    } else {
                   8876: 		&Apache::lonnet::del('environment',[$option],
                   8877: 				     $domain,$username);
                   8878: 	    }
                   8879: 	}
                   8880:     }
                   8881: # ------------------------------------------------------------- Get environment
                   8882: 
                   8883:     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
                   8884:     my ($tmp) = keys(%userenv);
                   8885:     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   8886: 	# default remote control to off
                   8887: 	if ($userenv{'remote'} ne 'on') { $userenv{'remote'} = 'off'; }
                   8888:     } else {
                   8889: 	undef(%userenv);
                   8890:     }
                   8891:     if (($userenv{'interface'}) && (!$form->{'interface'})) {
                   8892: 	$form->{'interface'}=$userenv{'interface'};
                   8893:     }
                   8894:     $env{'environment.remote'}=$userenv{'remote'};
                   8895:     if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
                   8896: 
                   8897: # --------------- Do not trust query string to be put directly into environment
                   8898:     foreach my $option ('imagesuppress','appletsuppress',
                   8899: 			'embedsuppress','fontenhance','blackwhite',
                   8900: 			'interface','localpath','localres') {
                   8901: 	$form->{$option}=~s/[\n\r\=]//gs;
                   8902:     }
                   8903: # --------------------------------------------------------- Write first profile
                   8904: 
                   8905:     {
                   8906: 	my %initial_env = 
                   8907: 	    ("user.name"          => $username,
                   8908: 	     "user.domain"        => $domain,
                   8909: 	     "user.home"          => $authhost,
                   8910: 	     "browser.type"       => $clientbrowser,
                   8911: 	     "browser.version"    => $clientversion,
                   8912: 	     "browser.mathml"     => $clientmathml,
                   8913: 	     "browser.unicode"    => $clientunicode,
                   8914: 	     "browser.os"         => $clientos,
                   8915: 	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
                   8916: 	     "request.course.fn"  => '',
                   8917: 	     "request.course.uri" => '',
                   8918: 	     "request.course.sec" => '',
                   8919: 	     "request.role"       => 'cm',
                   8920: 	     "request.role.adv"   => $env{'user.adv'},
                   8921: 	     "request.host"       => $ENV{'REMOTE_ADDR'},);
                   8922: 
                   8923:         if ($form->{'localpath'}) {
                   8924: 	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
                   8925: 	    $initial_env{"browser.localres"}   = $form->{'localres'};
                   8926:         }
                   8927: 	
                   8928: 	if ($public) {
                   8929: 	    $initial_env{"environment.remote"} = "off";
                   8930: 	}
                   8931: 	if ($form->{'interface'}) {
                   8932: 	    $form->{'interface'}=~s/\W//gs;
                   8933: 	    $initial_env{"browser.interface"} = $form->{'interface'};
                   8934: 	    $env{'browser.interface'}=$form->{'interface'};
                   8935: 	    foreach my $option ('imagesuppress','appletsuppress',
                   8936: 				'embedsuppress','fontenhance','blackwhite') {
                   8937: 		if (($form->{$option} eq 'true') ||
                   8938: 		    ($userenv{$option} eq 'on')) {
                   8939: 		    $initial_env{"browser.$option"} = "on";
                   8940: 		}
                   8941: 	    }
                   8942: 	}
                   8943: 
                   8944: 	$env{'user.environment'} = "$lonids/$cookie.id";
                   8945: 	
                   8946: 	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
                   8947: 		 &GDBM_WRCREAT(),0640)) {
                   8948: 	    &_add_to_env(\%disk_env,\%initial_env);
                   8949: 	    &_add_to_env(\%disk_env,\%userenv,'environment.');
                   8950: 	    &_add_to_env(\%disk_env,$userroles);
1.463     albertel 8951: 	    if (ref($args->{'extra_env'})) {
                   8952: 		&_add_to_env(\%disk_env,$args->{'extra_env'});
                   8953: 	    }
1.462     albertel 8954: 	    untie(%disk_env);
                   8955: 	} else {
                   8956: 	    &Apache::lonnet::logthis("<font color=\"blue\">WARNING: ".
                   8957: 			   'Could not create environment storage in lonauth: '.$!.'</font>');
                   8958: 	    return 'error: '.$!;
                   8959: 	}
                   8960:     }
                   8961:     $env{'request.role'}='cm';
                   8962:     $env{'request.role.adv'}=$env{'user.adv'};
                   8963:     $env{'browser.type'}=$clientbrowser;
                   8964: 
                   8965:     return $cookie;
                   8966: 
                   8967: }
                   8968: 
                   8969: sub _add_to_env {
                   8970:     my ($idf,$env_data,$prefix) = @_;
                   8971:     while (my ($key,$value) = each(%$env_data)) {
                   8972: 	$idf->{$prefix.$key} = $value;
                   8973: 	$env{$prefix.$key}   = $value;
                   8974:     }
                   8975: }
                   8976: 
                   8977: 
1.41      ng       8978: =pod
                   8979: 
                   8980: =back
                   8981: 
1.112     bowersj2 8982: =cut
1.41      ng       8983: 
1.112     bowersj2 8984: 1;
                   8985: __END__;
1.41      ng       8986: 

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