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

1.10      albertel    1: # The LearningOnline Network with CAPA
1.1       albertel    2: # a pile of common routines
1.10      albertel    3: #
1.657   ! raeburn     4: # $Id: loncommon.pm,v 1.656 2008/05/29 15:39:16 www 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.656     www       154: my %timezone;
1.124     www       155: my %supported_language;
1.12      harris41  156: my %cprtag;
1.192     taceyjo1  157: my %scprtag;
1.351     www       158: my %fe; my %fd; my %fm;
1.41      ng        159: my %category_extensions;
1.12      harris41  160: 
1.46      matthew   161: # ---------------------------------------------- Thesaurus variables
1.144     matthew   162: #
                    163: # %Keywords:
                    164: #      A hash used by &keyword to determine if a word is considered a keyword.
                    165: # $thesaurus_db_file 
                    166: #      Scalar containing the full path to the thesaurus database.
1.46      matthew   167: 
                    168: my %Keywords;
                    169: my $thesaurus_db_file;
                    170: 
1.144     matthew   171: #
                    172: # Initialize values from language.tab, copyright.tab, filetypes.tab,
                    173: # thesaurus.tab, and filecategories.tab.
                    174: #
1.18      www       175: BEGIN {
1.46      matthew   176:     # Variable initialization
                    177:     $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
                    178:     #
1.22      www       179:     unless ($readit) {
1.12      harris41  180: # ------------------------------------------------------------------- languages
                    181:     {
1.158     raeburn   182:         my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    183:                                    '/language.tab';
                    184:         if ( open(my $fh,"<$langtabfile") ) {
1.356     albertel  185:             while (my $line = <$fh>) {
                    186:                 next if ($line=~/^\#/);
                    187:                 chomp($line);
                    188:                 my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$line));
1.158     raeburn   189:                 $language{$key}=$val.' - '.$enc;
                    190:                 if ($sup) {
                    191:                     $supported_language{$key}=$sup;
                    192:                 }
                    193:             }
                    194:             close($fh);
                    195:         }
1.12      harris41  196:     }
1.656     www       197: # ------------------------------------------------------------------- timezones
                    198:     {
                    199:         my $timetabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    200:                                    '/timezone.tab';
                    201:         if ( open(my $fh,"<$timetabfile") ) {
                    202:             while (my $line = <$fh>) {
                    203:                 next if ($line=~/^\#/);
                    204:                 chomp($line);
                    205:                 my $value=$line;
                    206:                 $value=~s/\_/ /g;
                    207:                 $timezone{$line}=$value;
                    208:             }
                    209:             close($fh);
                    210:         }
                    211:     }
                    212: 
1.12      harris41  213: # ------------------------------------------------------------------ copyrights
                    214:     {
1.158     raeburn   215:         my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    216:                                   '/copyright.tab';
                    217:         if ( open (my $fh,"<$copyrightfile") ) {
1.356     albertel  218:             while (my $line = <$fh>) {
                    219:                 next if ($line=~/^\#/);
                    220:                 chomp($line);
                    221:                 my ($key,$val)=(split(/\s+/,$line,2));
1.158     raeburn   222:                 $cprtag{$key}=$val;
                    223:             }
                    224:             close($fh);
                    225:         }
1.12      harris41  226:     }
1.351     www       227: # ----------------------------------------------------------- source copyrights
1.192     taceyjo1  228:     {
                    229:         my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    230:                                   '/source_copyright.tab';
                    231:         if ( open (my $fh,"<$sourcecopyrightfile") ) {
1.356     albertel  232:             while (my $line = <$fh>) {
                    233:                 next if ($line =~ /^\#/);
                    234:                 chomp($line);
                    235:                 my ($key,$val)=(split(/\s+/,$line,2));
1.192     taceyjo1  236:                 $scprtag{$key}=$val;
                    237:             }
                    238:             close($fh);
                    239:         }
                    240:     }
1.63      www       241: 
1.517     raeburn   242: # -------------------------------------------------------------- default domain designs
1.63      www       243:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517     raeburn   244:     my $designfile = $designdir.'/default.tab';
                    245:     if ( open (my $fh,"<$designfile") ) {
                    246:         while (my $line = <$fh>) {
                    247:             next if ($line =~ /^\#/);
                    248:             chomp($line);
                    249:             my ($key,$val)=(split(/\=/,$line));
                    250:             if ($val) { $defaultdesign{$key}=$val; }
                    251:         }
                    252:         close($fh);
1.63      www       253:     }
                    254: 
1.15      harris41  255: # ------------------------------------------------------------- file categories
                    256:     {
1.158     raeburn   257:         my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    258:                                   '/filecategories.tab';
                    259:         if ( open (my $fh,"<$categoryfile") ) {
1.356     albertel  260: 	    while (my $line = <$fh>) {
                    261: 		next if ($line =~ /^\#/);
                    262: 		chomp($line);
                    263:                 my ($extension,$category)=(split(/\s+/,$line,2));
1.158     raeburn   264:                 push @{$category_extensions{lc($category)}},$extension;
                    265:             }
                    266:             close($fh);
                    267:         }
                    268: 
1.15      harris41  269:     }
1.12      harris41  270: # ------------------------------------------------------------------ file types
                    271:     {
1.158     raeburn   272:         my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    273:                '/filetypes.tab';
                    274:         if ( open (my $fh,"<$typesfile") ) {
1.356     albertel  275:             while (my $line = <$fh>) {
                    276: 		next if ($line =~ /^\#/);
                    277: 		chomp($line);
                    278:                 my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158     raeburn   279:                 if ($descr ne '') {
                    280:                     $fe{$ending}=lc($emb);
                    281:                     $fd{$ending}=$descr;
1.351     www       282:                     if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158     raeburn   283:                 }
                    284:             }
                    285:             close($fh);
                    286:         }
1.12      harris41  287:     }
1.22      www       288:     &Apache::lonnet::logthis(
1.46      matthew   289:               "<font color=yellow>INFO: Read file types</font>");
1.22      www       290:     $readit=1;
1.46      matthew   291:     }  # end of unless($readit) 
1.32      matthew   292:     
                    293: }
1.112     bowersj2  294: 
1.42      matthew   295: ###############################################################
                    296: ##           HTML and Javascript Helper Functions            ##
                    297: ###############################################################
                    298: 
                    299: =pod 
                    300: 
1.112     bowersj2  301: =head1 HTML and Javascript Functions
1.42      matthew   302: 
1.112     bowersj2  303: =over 4
                    304: 
1.648     raeburn   305: =item * &browser_and_searcher_javascript()
1.112     bowersj2  306: 
                    307: X<browsing, javascript>X<searching, javascript>Returns a string
                    308: containing javascript with two functions, C<openbrowser> and
                    309: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
                    310: tags.
1.42      matthew   311: 
1.648     raeburn   312: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42      matthew   313: 
                    314: inputs: formname, elementname, only, omit
                    315: 
                    316: formname and elementname indicate the name of the html form and name of
                    317: the element that the results of the browsing selection are to be placed in. 
                    318: 
                    319: Specifying 'only' will restrict the browser to displaying only files
1.185     www       320: with the given extension.  Can be a comma separated list.
1.42      matthew   321: 
                    322: Specifying 'omit' will restrict the browser to NOT displaying files
1.185     www       323: with the given extension.  Can be a comma separated list.
1.42      matthew   324: 
1.648     raeburn   325: =item * &opensearcher(formname,elementname) [javascript]
1.42      matthew   326: 
                    327: Inputs: formname, elementname
                    328: 
                    329: formname and elementname specify the name of the html form and the name
                    330: of the element the selection from the search results will be placed in.
1.542     raeburn   331: 
1.42      matthew   332: =cut
                    333: 
                    334: sub browser_and_searcher_javascript {
1.199     albertel  335:     my ($mode)=@_;
                    336:     if (!defined($mode)) { $mode='edit'; }
1.453     albertel  337:     my $resurl=&escape_single(&lastresurl());
1.42      matthew   338:     return <<END;
1.219     albertel  339: // <!-- BEGIN LON-CAPA Internal
1.50      matthew   340:     var editbrowser = null;
1.135     albertel  341:     function openbrowser(formname,elementname,only,omit,titleelement) {
1.170     www       342:         var url = '$resurl/?';
1.42      matthew   343:         if (editbrowser == null) {
                    344:             url += 'launch=1&';
                    345:         }
                    346:         url += 'catalogmode=interactive&';
1.199     albertel  347:         url += 'mode=$mode&';
1.611     albertel  348:         url += 'inhibitmenu=yes&';
1.42      matthew   349:         url += 'form=' + formname + '&';
                    350:         if (only != null) {
                    351:             url += 'only=' + only + '&';
1.217     albertel  352:         } else {
                    353:             url += 'only=&';
                    354: 	}
1.42      matthew   355:         if (omit != null) {
                    356:             url += 'omit=' + omit + '&';
1.217     albertel  357:         } else {
                    358:             url += 'omit=&';
                    359: 	}
1.135     albertel  360:         if (titleelement != null) {
                    361:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  362:         } else {
                    363: 	    url += 'titleelement=&';
                    364: 	}
1.42      matthew   365:         url += 'element=' + elementname + '';
                    366:         var title = 'Browser';
1.435     albertel  367:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   368:         options += ',width=700,height=600';
                    369:         editbrowser = open(url,title,options,'1');
                    370:         editbrowser.focus();
                    371:     }
                    372:     var editsearcher;
1.135     albertel  373:     function opensearcher(formname,elementname,titleelement) {
1.42      matthew   374:         var url = '/adm/searchcat?';
                    375:         if (editsearcher == null) {
                    376:             url += 'launch=1&';
                    377:         }
                    378:         url += 'catalogmode=interactive&';
1.199     albertel  379:         url += 'mode=$mode&';
1.42      matthew   380:         url += 'form=' + formname + '&';
1.135     albertel  381:         if (titleelement != null) {
                    382:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  383:         } else {
                    384: 	    url += 'titleelement=&';
                    385: 	}
1.42      matthew   386:         url += 'element=' + elementname + '';
                    387:         var title = 'Search';
1.435     albertel  388:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   389:         options += ',width=700,height=600';
                    390:         editsearcher = open(url,title,options,'1');
                    391:         editsearcher.focus();
                    392:     }
1.219     albertel  393: // END LON-CAPA Internal -->
1.42      matthew   394: END
1.170     www       395: }
                    396: 
                    397: sub lastresurl {
1.258     albertel  398:     if ($env{'environment.lastresurl'}) {
                    399: 	return $env{'environment.lastresurl'}
1.170     www       400:     } else {
                    401: 	return '/res';
                    402:     }
                    403: }
                    404: 
                    405: sub storeresurl {
                    406:     my $resurl=&Apache::lonnet::clutter(shift);
                    407:     unless ($resurl=~/^\/res/) { return 0; }
                    408:     $resurl=~s/\/$//;
                    409:     &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646     raeburn   410:     &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170     www       411:     return 1;
1.42      matthew   412: }
                    413: 
1.74      www       414: sub studentbrowser_javascript {
1.111     www       415:    unless (
1.258     albertel  416:             (($env{'request.course.id'}) && 
1.302     albertel  417:              (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    418: 	      || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    419: 					  '/'.$env{'request.course.sec'})
                    420: 	      ))
1.258     albertel  421:          || ($env{'request.role'}=~/^(au|dc|su)/)
1.111     www       422:           ) { return ''; }  
1.74      www       423:    return (<<'ENDSTDBRW');
                    424: <script type="text/javascript" language="Javascript" >
                    425:     var stdeditbrowser;
1.558     albertel  426:     function openstdbrowser(formname,uname,udom,roleflag,ignorefilter) {
1.74      www       427:         var url = '/adm/pickstudent?';
                    428:         var filter;
1.558     albertel  429: 	if (!ignorefilter) {
                    430: 	    eval('filter=document.'+formname+'.'+uname+'.value;');
                    431: 	}
1.74      www       432:         if (filter != null) {
                    433:            if (filter != '') {
                    434:                url += 'filter='+filter+'&';
                    435: 	   }
                    436:         }
                    437:         url += 'form=' + formname + '&unameelement='+uname+
                    438:                                     '&udomelement='+udom;
1.111     www       439: 	if (roleflag) { url+="&roles=1"; }
1.102     www       440:         var title = 'Student_Browser';
1.74      www       441:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    442:         options += ',width=700,height=600';
                    443:         stdeditbrowser = open(url,title,options,'1');
                    444:         stdeditbrowser.focus();
                    445:     }
                    446: </script>
                    447: ENDSTDBRW
                    448: }
1.42      matthew   449: 
1.74      www       450: sub selectstudent_link {
1.111     www       451:    my ($form,$unameele,$udomele)=@_;
1.258     albertel  452:    if ($env{'request.course.id'}) {  
1.302     albertel  453:        if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    454: 	   && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    455: 					'/'.$env{'request.course.sec'})) {
1.111     www       456: 	   return '';
                    457:        }
                    458:        return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele.
1.607     albertel  459:         '","'.$udomele.'");'."'>".&mt('Select User')."</a>";
1.74      www       460:    }
1.258     albertel  461:    if ($env{'request.role'}=~/^(au|dc|su)/) {
1.111     www       462:        return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele.
1.119     www       463:         '","'.$udomele.'",1);'."'>".&mt('Select User')."</a>";
1.111     www       464:    }
                    465:    return '';
1.91      www       466: }
                    467: 
1.653     raeburn   468: sub authorbrowser_javascript {
                    469:     return <<"ENDAUTHORBRW";
                    470: <script type="text/javascript">
                    471: var stdeditbrowser;
                    472: 
                    473: function openauthorbrowser(formname,udom) {
                    474:     var url = '/adm/pickauthor?';
                    475:     url += 'form='+formname+'&roledom='+udom;
                    476:     var title = 'Author_Browser';
                    477:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    478:     options += ',width=700,height=600';
                    479:     stdeditbrowser = open(url,title,options,'1');
                    480:     stdeditbrowser.focus();
                    481: }
                    482: 
                    483: </script>
                    484: ENDAUTHORBRW
                    485: }
                    486: 
1.91      www       487: sub coursebrowser_javascript {
1.468     raeburn   488:     my ($domainfilter,$sec_element,$formname)=@_;
1.377     raeburn   489:     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   490:    my $output = '
1.538     albertel  491: <script type="text/javascript">
1.468     raeburn   492:     var stdeditbrowser;'."\n";
                    493:    $output .= <<"ENDSTDBRW";
1.377     raeburn   494:     function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,crstype) {
1.91      www       495:         var url = '/adm/pickcourse?';
1.468     raeburn   496:         var domainfilter = '';
                    497:         var formid = getFormIdByName(formname);
                    498:         if (formid > -1) {
                    499:             var domid = getIndexByName(formid,udom);
                    500:             if (domid > -1) {
                    501:                 if (document.forms[formid].elements[domid].type == 'select-one') {
                    502:                     domainfilter=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
                    503:                 }
                    504:                 if (document.forms[formid].elements[domid].type == 'hidden') {
                    505:                     domainfilter=document.forms[formid].elements[domid].value;
                    506:                 }
                    507:             }
1.91      www       508:         }
1.128     albertel  509:         if (domainfilter != null) {
                    510:            if (domainfilter != '') {
                    511:                url += 'domainfilter='+domainfilter+'&';
                    512: 	   }
                    513:         }
1.91      www       514:         url += 'form=' + formname + '&cnumelement='+uname+
1.187     albertel  515: 	                            '&cdomelement='+udom+
                    516:                                     '&cnameelement='+desc;
1.468     raeburn   517:         if (extra_element !=null && extra_element != '') {
1.594     raeburn   518:             if (formname == 'rolechoice' || formname == 'studentform') {
1.468     raeburn   519:                 url += '&roleelement='+extra_element;
                    520:                 if (domainfilter == null || domainfilter == '') {
                    521:                     url += '&domainfilter='+extra_element;
                    522:                 }
1.234     raeburn   523:             }
1.468     raeburn   524:             else {
                    525:                 if (formname == 'portform') {
                    526:                     url += '&setroles='+extra_element;
                    527:                 }
                    528:             }     
1.230     raeburn   529:         }
1.293     raeburn   530:         if (multflag !=null && multflag != '') {
                    531:             url += '&multiple='+multflag;
                    532:         }
1.377     raeburn   533:         if (crstype == 'Course/Group') {
                    534:             if (formname == 'cu') {
                    535:                 crstype = document.cu.crstype.options[document.cu.crstype.selectedIndex].value; 
                    536:                 if (crstype == "") {
                    537:                     alert("$crs_or_grp_alert");
                    538:                     return;
                    539:                 }
                    540:             }
                    541:         }
                    542:         if (crstype !=null && crstype != '') {
                    543:             url += '&type='+crstype;
                    544:         }
1.102     www       545:         var title = 'Course_Browser';
1.91      www       546:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    547:         options += ',width=700,height=600';
                    548:         stdeditbrowser = open(url,title,options,'1');
                    549:         stdeditbrowser.focus();
                    550:     }
1.468     raeburn   551: 
                    552:     function getFormIdByName(formname) {
                    553:         for (var i=0;i<document.forms.length;i++) {
                    554:             if (document.forms[i].name == formname) {
                    555:                 return i;
                    556:             }
                    557:         }
                    558:         return -1; 
                    559:     }
                    560: 
                    561:     function getIndexByName(formid,item) {
                    562:         for (var i=0;i<document.forms[formid].elements.length;i++) {
                    563:             if (document.forms[formid].elements[i].name == item) {
                    564:                 return i;
                    565:             }
                    566:         }
                    567:         return -1;
                    568:     }
1.91      www       569: ENDSTDBRW
1.468     raeburn   570:     if ($sec_element ne '') {
                    571:         $output .= &setsec_javascript($sec_element,$formname);
                    572:     }
                    573:     $output .= '
                    574: </script>';
                    575:     return $output;
                    576: }
                    577: 
                    578: sub setsec_javascript {
                    579:     my ($sec_element,$formname) = @_;
                    580:     my $setsections = qq|
                    581: function setSect(sectionlist) {
1.629     raeburn   582:     var sectionsArray = new Array();
                    583:     if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
                    584:         sectionsArray = sectionlist.split(",");
                    585:     }
1.468     raeburn   586:     var numSections = sectionsArray.length;
                    587:     document.$formname.$sec_element.length = 0;
                    588:     if (numSections == 0) {
                    589:         document.$formname.$sec_element.multiple=false;
                    590:         document.$formname.$sec_element.size=1;
                    591:         document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
                    592:     } else {
                    593:         if (numSections == 1) {
                    594:             document.$formname.$sec_element.multiple=false;
                    595:             document.$formname.$sec_element.size=1;
                    596:             document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
                    597:             document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
                    598:             document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
                    599:         } else {
                    600:             for (var i=0; i<numSections; i++) {
                    601:                 document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
                    602:             }
                    603:             document.$formname.$sec_element.multiple=true
                    604:             if (numSections < 3) {
                    605:                 document.$formname.$sec_element.size=numSections;
                    606:             } else {
                    607:                 document.$formname.$sec_element.size=3;
                    608:             }
                    609:             document.$formname.$sec_element.options[0].selected = false
                    610:         }
                    611:     }
1.91      www       612: }
1.468     raeburn   613: |;
                    614:     return $setsections;
                    615: }
                    616: 
1.91      www       617: 
                    618: sub selectcourse_link {
1.377     raeburn   619:    my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype)=@_;
1.492     albertel  620:    return "<a href='".'javascript:opencrsbrowser("'.$form.'","'.$unameele.
                    621:         '","'.$udomele.'","'.$desc.'","'.$extra_element.'","'.$multflag.'","'.$selecttype.'");'."'>".&mt('Select Course')."</a>";
1.74      www       622: }
1.42      matthew   623: 
1.653     raeburn   624: sub selectauthor_link {
                    625:    my ($form,$udom)=@_;
                    626:    return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
                    627:           &mt('Select Author').'</a>';
                    628: }
                    629: 
1.273     raeburn   630: sub check_uncheck_jscript {
                    631:     my $jscript = <<"ENDSCRT";
                    632: function checkAll(field) {
                    633:     if (field.length > 0) {
                    634:         for (i = 0; i < field.length; i++) {
                    635:             field[i].checked = true ;
                    636:         }
                    637:     } else {
                    638:         field.checked = true
                    639:     }
                    640: }
                    641:  
                    642: function uncheckAll(field) {
                    643:     if (field.length > 0) {
                    644:         for (i = 0; i < field.length; i++) {
                    645:             field[i].checked = false ;
1.543     albertel  646:         }
                    647:     } else {
1.273     raeburn   648:         field.checked = false ;
                    649:     }
                    650: }
                    651: ENDSCRT
                    652:     return $jscript;
                    653: }
                    654: 
1.656     www       655: sub select_timezone {
                    656:    my ($name,$selected,$onchange)=@_;
                    657:    my $output="<select name='$name' $onchange>\n";
1.657   ! raeburn   658:    my @timezones = DateTime::TimeZone->all_names;
        !           659:    foreach my $tzone (@timezones) {
        !           660:        $output.= '<option value="'.$tzone.'"';
        !           661:        if ($tzone eq $selected) {
        !           662:            $output.=' selected="selected"';
        !           663:        }
        !           664:        $output.=">$tzone</option>\n";
1.656     www       665:    }
                    666:    $output.="</select>";
                    667:    return $output;
                    668: }
1.273     raeburn   669: 
1.42      matthew   670: =pod
1.36      matthew   671: 
1.648     raeburn   672: =item * &linked_select_forms(...)
1.36      matthew   673: 
                    674: linked_select_forms returns a string containing a <script></script> block
                    675: and html for two <select> menus.  The select menus will be linked in that
                    676: changing the value of the first menu will result in new values being placed
                    677: in the second menu.  The values in the select menu will appear in alphabetical
1.609     raeburn   678: order unless a defined order is provided.
1.36      matthew   679: 
                    680: linked_select_forms takes the following ordered inputs:
                    681: 
                    682: =over 4
                    683: 
1.112     bowersj2  684: =item * $formname, the name of the <form> tag
1.36      matthew   685: 
1.112     bowersj2  686: =item * $middletext, the text which appears between the <select> tags
1.36      matthew   687: 
1.112     bowersj2  688: =item * $firstdefault, the default value for the first menu
1.36      matthew   689: 
1.112     bowersj2  690: =item * $firstselectname, the name of the first <select> tag
1.36      matthew   691: 
1.112     bowersj2  692: =item * $secondselectname, the name of the second <select> tag
1.36      matthew   693: 
1.112     bowersj2  694: =item * $hashref, a reference to a hash containing the data for the menus.
1.36      matthew   695: 
1.609     raeburn   696: =item * $menuorder, the order of values in the first menu
                    697: 
1.41      ng        698: =back 
                    699: 
1.36      matthew   700: Below is an example of such a hash.  Only the 'text', 'default', and 
                    701: 'select2' keys must appear as stated.  keys(%menu) are the possible 
                    702: values for the first select menu.  The text that coincides with the 
1.41      ng        703: first menu value is given in $menu{$choice1}->{'text'}.  The values 
1.36      matthew   704: and text for the second menu are given in the hash pointed to by 
                    705: $menu{$choice1}->{'select2'}.  
                    706: 
1.112     bowersj2  707:  my %menu = ( A1 => { text =>"Choice A1" ,
                    708:                        default => "B3",
                    709:                        select2 => { 
                    710:                            B1 => "Choice B1",
                    711:                            B2 => "Choice B2",
                    712:                            B3 => "Choice B3",
                    713:                            B4 => "Choice B4"
1.609     raeburn   714:                            },
                    715:                        order => ['B4','B3','B1','B2'],
1.112     bowersj2  716:                    },
                    717:                A2 => { text =>"Choice A2" ,
                    718:                        default => "C2",
                    719:                        select2 => { 
                    720:                            C1 => "Choice C1",
                    721:                            C2 => "Choice C2",
                    722:                            C3 => "Choice C3"
1.609     raeburn   723:                            },
                    724:                        order => ['C2','C1','C3'],
1.112     bowersj2  725:                    },
                    726:                A3 => { text =>"Choice A3" ,
                    727:                        default => "D6",
                    728:                        select2 => { 
                    729:                            D1 => "Choice D1",
                    730:                            D2 => "Choice D2",
                    731:                            D3 => "Choice D3",
                    732:                            D4 => "Choice D4",
                    733:                            D5 => "Choice D5",
                    734:                            D6 => "Choice D6",
                    735:                            D7 => "Choice D7"
1.609     raeburn   736:                            },
                    737:                        order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112     bowersj2  738:                    }
                    739:                );
1.36      matthew   740: 
                    741: =cut
                    742: 
                    743: sub linked_select_forms {
                    744:     my ($formname,
                    745:         $middletext,
                    746:         $firstdefault,
                    747:         $firstselectname,
                    748:         $secondselectname, 
1.609     raeburn   749:         $hashref,
                    750:         $menuorder,
1.36      matthew   751:         ) = @_;
                    752:     my $second = "document.$formname.$secondselectname";
                    753:     my $first = "document.$formname.$firstselectname";
                    754:     # output the javascript to do the changing
                    755:     my $result = '';
1.219     albertel  756:     $result.="<script type=\"text/javascript\">\n";
1.36      matthew   757:     $result.="var select2data = new Object();\n";
                    758:     $" = '","';
                    759:     my $debug = '';
                    760:     foreach my $s1 (sort(keys(%$hashref))) {
                    761:         $result.="select2data.d_$s1 = new Object();\n";        
                    762:         $result.="select2data.d_$s1.def = new String('".
                    763:             $hashref->{$s1}->{'default'}."');\n";
1.609     raeburn   764:         $result.="select2data.d_$s1.values = new Array(";
1.36      matthew   765:         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609     raeburn   766:         if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
                    767:             @s2values = @{$hashref->{$s1}->{'order'}};
                    768:         }
1.36      matthew   769:         $result.="\"@s2values\");\n";
                    770:         $result.="select2data.d_$s1.texts = new Array(";        
                    771:         my @s2texts;
                    772:         foreach my $value (@s2values) {
                    773:             push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
                    774:         }
                    775:         $result.="\"@s2texts\");\n";
                    776:     }
                    777:     $"=' ';
                    778:     $result.= <<"END";
                    779: 
                    780: function select1_changed() {
                    781:     // Determine new choice
                    782:     var newvalue = "d_" + $first.value;
                    783:     // update select2
                    784:     var values     = select2data[newvalue].values;
                    785:     var texts      = select2data[newvalue].texts;
                    786:     var select2def = select2data[newvalue].def;
                    787:     var i;
                    788:     // out with the old
                    789:     for (i = 0; i < $second.options.length; i++) {
                    790:         $second.options[i] = null;
                    791:     }
                    792:     // in with the nuclear
                    793:     for (i=0;i<values.length; i++) {
                    794:         $second.options[i] = new Option(values[i]);
1.143     matthew   795:         $second.options[i].value = values[i];
1.36      matthew   796:         $second.options[i].text = texts[i];
                    797:         if (values[i] == select2def) {
                    798:             $second.options[i].selected = true;
                    799:         }
                    800:     }
                    801: }
                    802: </script>
                    803: END
                    804:     # output the initial values for the selection lists
                    805:     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed()\">\n";
1.609     raeburn   806:     my @order = sort(keys(%{$hashref}));
                    807:     if (ref($menuorder) eq 'ARRAY') {
                    808:         @order = @{$menuorder};
                    809:     }
                    810:     foreach my $value (@order) {
1.36      matthew   811:         $result.="    <option value=\"$value\" ";
1.253     albertel  812:         $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119     www       813:         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36      matthew   814:     }
                    815:     $result .= "</select>\n";
                    816:     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
                    817:     $result .= $middletext;
                    818:     $result .= "<select size=\"1\" name=\"$secondselectname\">\n";
                    819:     my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609     raeburn   820:     
                    821:     my @secondorder = sort(keys(%select2));
                    822:     if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
                    823:         @secondorder = @{$hashref->{$firstdefault}->{'order'}};
                    824:     }
                    825:     foreach my $value (@secondorder) {
1.36      matthew   826:         $result.="    <option value=\"$value\" ";        
1.253     albertel  827:         $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119     www       828:         $result.=">".&mt($select2{$value})."</option>\n";
1.36      matthew   829:     }
                    830:     $result .= "</select>\n";
                    831:     #    return $debug;
                    832:     return $result;
                    833: }   #  end of sub linked_select_forms {
                    834: 
1.45      matthew   835: =pod
1.44      bowersj2  836: 
1.648     raeburn   837: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height)
1.44      bowersj2  838: 
1.112     bowersj2  839: Returns a string corresponding to an HTML link to the given help
                    840: $topic, where $topic corresponds to the name of a .tex file in
                    841: /home/httpd/html/adm/help/tex, with underscores replaced by
                    842: spaces. 
                    843: 
                    844: $text will optionally be linked to the same topic, allowing you to
                    845: link text in addition to the graphic. If you do not want to link
                    846: text, but wish to specify one of the later parameters, pass an
                    847: empty string. 
                    848: 
                    849: $stayOnPage is a value that will be interpreted as a boolean. If true,
                    850: the link will not open a new window. If false, the link will open
                    851: a new window using Javascript. (Default is false.) 
                    852: 
                    853: $width and $height are optional numerical parameters that will
                    854: override the width and height of the popped up window, which may
                    855: be useful for certain help topics with big pictures included. 
1.44      bowersj2  856: 
                    857: =cut
                    858: 
                    859: sub help_open_topic {
1.48      bowersj2  860:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
                    861:     $text = "" if (not defined $text);
1.44      bowersj2  862:     $stayOnPage = 0 if (not defined $stayOnPage);
1.552     banghart  863:     if ($env{'browser.interface'} eq 'textual') {
1.79      www       864: 	$stayOnPage=1;
                    865:     }
1.44      bowersj2  866:     $width = 350 if (not defined $width);
                    867:     $height = 400 if (not defined $height);
                    868:     my $filename = $topic;
                    869:     $filename =~ s/ /_/g;
                    870: 
1.48      bowersj2  871:     my $template = "";
                    872:     my $link;
1.572     banghart  873:     
1.159     www       874:     $topic=~s/\W/\_/g;
1.44      bowersj2  875: 
1.572     banghart  876:     if (!$stayOnPage) {
1.72      bowersj2  877: 	$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  878:     } else {
1.48      bowersj2  879: 	$link = "/adm/help/${filename}.hlp";
                    880:     }
                    881: 
                    882:     # Add the text
1.572     banghart  883:     if ($text ne "") {
1.77      www       884: 	$template .= 
1.572     banghart  885:             "<table bgcolor='#3333AA' cellspacing='1' cellpadding='1' border='0'><tr>".
                    886:             "<td bgcolor='#5555FF'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.48      bowersj2  887:     }
                    888: 
                    889:     # Add the graphic
1.179     matthew   890:     my $title = &mt('Online Help');
1.649     www       891:     my $helpicon=&lonhttpdurl("/res/adm/pages/help.png");
1.48      bowersj2  892:     $template .= <<"ENDTEMPLATE";
1.436     albertel  893:  <a target="_top" href="$link" title="$title"><img src="$helpicon" border="0" alt="(Help: $topic)" /></a>
1.44      bowersj2  894: ENDTEMPLATE
1.78      www       895:     if ($text ne '') { $template.='</td></tr></table>' };
1.44      bowersj2  896:     return $template;
                    897: 
1.106     bowersj2  898: }
                    899: 
                    900: # This is a quicky function for Latex cheatsheet editing, since it 
                    901: # appears in at least four places
                    902: sub helpLatexCheatsheet {
                    903:     my $other = shift;
                    904:     my $addOther = '';
                    905:     if ($other) {
                    906: 	$addOther = Apache::loncommon::help_open_topic($other, shift,
                    907: 						       undef, undef, 600) .
                    908: 							   '</td><td>';
                    909:     }
                    910:     return '<table><tr><td>'.
                    911: 	$addOther .
1.636     raeburn   912: 	&Apache::loncommon::help_open_topic("Greek_Symbols",&mt('Greek Symbols'),
1.106     bowersj2  913: 					    undef,undef,600)
                    914: 	.'</td><td>'.
1.636     raeburn   915: 	&Apache::loncommon::help_open_topic("Other_Symbols",&mt('Other Symbols'),
1.106     bowersj2  916: 					    undef,undef,600)
                    917: 	.'</td></tr></table>';
1.172     www       918: }
                    919: 
1.430     albertel  920: sub general_help {
                    921:     my $helptopic='Student_Intro';
                    922:     if ($env{'request.role'}=~/^(ca|au)/) {
                    923: 	$helptopic='Authoring_Intro';
                    924:     } elsif ($env{'request.role'}=~/^cc/) {
                    925: 	$helptopic='Course_Coordination_Intro';
                    926:     }
                    927:     return $helptopic;
                    928: }
                    929: 
                    930: sub update_help_link {
                    931:     my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
                    932:     my $origurl = $ENV{'REQUEST_URI'};
                    933:     $origurl=~s|^/~|/priv/|;
                    934:     my $timestamp = time;
                    935:     foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
                    936:         $$datum = &escape($$datum);
                    937:     }
                    938: 
                    939:     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";
                    940:     my $output .= <<"ENDOUTPUT";
                    941: <script type="text/javascript">
                    942: banner_link = '$banner_link';
                    943: </script>
                    944: ENDOUTPUT
                    945:     return $output;
                    946: }
                    947: 
                    948: # now just updates the help link and generates a blue icon
1.193     raeburn   949: sub help_open_menu {
1.430     albertel  950:     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text) 
1.552     banghart  951: 	= @_;    
1.430     albertel  952:     $stayOnPage = 0 if (not defined $stayOnPage);
1.572     banghart  953:     # only use pop-up help (stayOnPage == 0)
1.552     banghart  954:     # if environment.remote is on (using remote control UI)
1.572     banghart  955:     if ($env{'browser.interface'} eq 'textual' ||
                    956:     	$env{'environment.remote'} eq 'off' ) {
1.552     banghart  957:         $stayOnPage=1;
1.430     albertel  958:     }
                    959:     my $output;
                    960:     if ($component_help) {
                    961: 	if (!$text) {
                    962: 	    $output=&help_open_topic($component_help,undef,$stayOnPage,
                    963: 				       $width,$height);
                    964: 	} else {
                    965: 	    my $help_text;
                    966: 	    $help_text=&unescape($topic);
                    967: 	    $output='<table><tr><td>'.
                    968: 		&help_open_topic($component_help,$help_text,$stayOnPage,
                    969: 				 $width,$height).'</td></tr></table>';
                    970: 	}
                    971:     }
                    972:     my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
                    973:     return $output.$banner_link;
                    974: }
                    975: 
                    976: sub top_nav_help {
                    977:     my ($text) = @_;
1.436     albertel  978:     $text = &mt($text);
1.572     banghart  979:     my $stay_on_page = 
1.436     albertel  980: 	($env{'browser.interface'}  eq 'textual' ||
                    981: 	 $env{'environment.remote'} eq 'off' );
1.572     banghart  982:     my $link = ($stay_on_page) ? "javascript:helpMenu('display')"
1.436     albertel  983: 	                     : "javascript:helpMenu('open')";
1.572     banghart  984:     my $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
1.436     albertel  985: 
1.201     raeburn   986:     my $title = &mt('Get help');
1.436     albertel  987: 
                    988:     return <<"END";
                    989: $banner_link
                    990:  <a href="$link" title="$title">$text</a>
                    991: END
                    992: }
                    993: 
                    994: sub help_menu_js {
                    995:     my ($text) = @_;
                    996: 
                    997:     my $stayOnPage = 
                    998: 	($env{'browser.interface'}  eq 'textual' ||
                    999: 	 $env{'environment.remote'} eq 'off' );
                   1000: 
                   1001:     my $width = 620;
                   1002:     my $height = 600;
1.430     albertel 1003:     my $helptopic=&general_help();
                   1004:     my $details_link = '/adm/help/'.$helptopic.'.hlp';
1.261     albertel 1005:     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331     albertel 1006:     my $start_page =
                   1007:         &Apache::loncommon::start_page('Help Menu', undef,
                   1008: 				       {'frameset'    => 1,
                   1009: 					'js_ready'    => 1,
                   1010: 					'add_entries' => {
                   1011: 					    'border' => '0',
1.579     raeburn  1012: 					    'rows'   => "110,*",},});
1.331     albertel 1013:     my $end_page =
                   1014:         &Apache::loncommon::end_page({'frameset' => 1,
                   1015: 				      'js_ready' => 1,});
                   1016: 
1.436     albertel 1017:     my $template .= <<"ENDTEMPLATE";
                   1018: <script type="text/javascript">
1.253     albertel 1019: // <!-- BEGIN LON-CAPA Internal
                   1020: // <![CDATA[
1.430     albertel 1021: var banner_link = '';
1.243     raeburn  1022: function helpMenu(target) {
                   1023:     var caller = this;
                   1024:     if (target == 'open') {
                   1025:         var newWindow = null;
                   1026:         try {
1.262     albertel 1027:             newWindow =  window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243     raeburn  1028:         }
                   1029:         catch(error) {
                   1030:             writeHelp(caller);
                   1031:             return;
                   1032:         }
                   1033:         if (newWindow) {
                   1034:             caller = newWindow;
                   1035:         }
1.193     raeburn  1036:     }
1.243     raeburn  1037:     writeHelp(caller);
                   1038:     return;
                   1039: }
                   1040: function writeHelp(caller) {
1.430     albertel 1041:     caller.document.writeln('$start_page<frame name="bannerframe"  src="'+banner_link+'" /><frame name="bodyframe" src="$details_link" /> $end_page')
1.243     raeburn  1042:     caller.document.close()
                   1043:     caller.focus()
1.193     raeburn  1044: }
1.253     albertel 1045: // ]]>
1.219     albertel 1046: // END LON-CAPA Internal -->
1.436     albertel 1047: </script>
1.193     raeburn  1048: ENDTEMPLATE
                   1049:     return $template;
                   1050: }
                   1051: 
1.172     www      1052: sub help_open_bug {
                   1053:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1054:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1055:     unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
                   1056:     $text = "" if (not defined $text);
                   1057:     $stayOnPage = 0 if (not defined $stayOnPage);
1.258     albertel 1058:     if ($env{'browser.interface'} eq 'textual' ||
                   1059: 	$env{'environment.remote'} eq 'off' ) {
1.172     www      1060: 	$stayOnPage=1;
                   1061:     }
1.184     albertel 1062:     $width = 600 if (not defined $width);
                   1063:     $height = 600 if (not defined $height);
1.172     www      1064: 
                   1065:     $topic=~s/\W+/\+/g;
                   1066:     my $link='';
                   1067:     my $template='';
1.379     albertel 1068:     my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&amp;bug_file_loc='.
                   1069: 	&escape($ENV{'REQUEST_URI'}).'&amp;component='.$topic;
1.172     www      1070:     if (!$stayOnPage)
                   1071:     {
                   1072: 	$link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1073:     }
                   1074:     else
                   1075:     {
                   1076: 	$link = $url;
                   1077:     }
                   1078:     # Add the text
                   1079:     if ($text ne "")
                   1080:     {
                   1081: 	$template .= 
                   1082:   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436     albertel 1083:   "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.172     www      1084:     }
                   1085: 
                   1086:     # Add the graphic
1.179     matthew  1087:     my $title = &mt('Report a Bug');
1.215     albertel 1088:     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172     www      1089:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1090:  <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172     www      1091: ENDTEMPLATE
                   1092:     if ($text ne '') { $template.='</td></tr></table>' };
                   1093:     return $template;
                   1094: 
                   1095: }
                   1096: 
                   1097: sub help_open_faq {
                   1098:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1099:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1100:     unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
                   1101:     $text = "" if (not defined $text);
                   1102:     $stayOnPage = 0 if (not defined $stayOnPage);
1.258     albertel 1103:     if ($env{'browser.interface'} eq 'textual' ||
                   1104: 	$env{'environment.remote'} eq 'off' ) {
1.172     www      1105: 	$stayOnPage=1;
                   1106:     }
                   1107:     $width = 350 if (not defined $width);
                   1108:     $height = 400 if (not defined $height);
                   1109: 
                   1110:     $topic=~s/\W+/\+/g;
                   1111:     my $link='';
                   1112:     my $template='';
                   1113:     my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
                   1114:     if (!$stayOnPage)
                   1115:     {
                   1116: 	$link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1117:     }
                   1118:     else
                   1119:     {
                   1120: 	$link = $url;
                   1121:     }
                   1122: 
                   1123:     # Add the text
                   1124:     if ($text ne "")
                   1125:     {
                   1126: 	$template .= 
1.173     www      1127:   "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436     albertel 1128:   "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.172     www      1129:     }
                   1130: 
                   1131:     # Add the graphic
1.179     matthew  1132:     my $title = &mt('View the FAQ');
1.215     albertel 1133:     my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172     www      1134:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1135:  <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172     www      1136: ENDTEMPLATE
                   1137:     if ($text ne '') { $template.='</td></tr></table>' };
                   1138:     return $template;
                   1139: 
1.44      bowersj2 1140: }
1.37      matthew  1141: 
1.180     matthew  1142: ###############################################################
                   1143: ###############################################################
                   1144: 
1.45      matthew  1145: =pod
                   1146: 
1.648     raeburn  1147: =item * &change_content_javascript():
1.256     matthew  1148: 
                   1149: This and the next function allow you to create small sections of an
                   1150: otherwise static HTML page that you can update on the fly with
                   1151: Javascript, even in Netscape 4.
                   1152: 
                   1153: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
                   1154: must be written to the HTML page once. It will prove the Javascript
                   1155: function "change(name, content)". Calling the change function with the
                   1156: name of the section 
                   1157: you want to update, matching the name passed to C<changable_area>, and
                   1158: the new content you want to put in there, will put the content into
                   1159: that area.
                   1160: 
                   1161: B<Note>: Netscape 4 only reserves enough space for the changable area
                   1162: to contain room for the original contents. You need to "make space"
                   1163: for whatever changes you wish to make, and be B<sure> to check your
                   1164: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
                   1165: it's adequate for updating a one-line status display, but little more.
                   1166: This script will set the space to 100% width, so you only need to
                   1167: worry about height in Netscape 4.
                   1168: 
                   1169: Modern browsers are much less limiting, and if you can commit to the
                   1170: user not using Netscape 4, this feature may be used freely with
                   1171: pretty much any HTML.
                   1172: 
                   1173: =cut
                   1174: 
                   1175: sub change_content_javascript {
                   1176:     # If we're on Netscape 4, we need to use Layer-based code
1.258     albertel 1177:     if ($env{'browser.type'} eq 'netscape' &&
                   1178: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1179: 	return (<<NETSCAPE4);
                   1180: 	function change(name, content) {
                   1181: 	    doc = document.layers[name+"___escape"].layers[0].document;
                   1182: 	    doc.open();
                   1183: 	    doc.write(content);
                   1184: 	    doc.close();
                   1185: 	}
                   1186: NETSCAPE4
                   1187:     } else {
                   1188: 	# Otherwise, we need to use semi-standards-compliant code
                   1189: 	# (technically, "innerHTML" isn't standard but the equivalent
                   1190: 	# is really scary, and every useful browser supports it
                   1191: 	return (<<DOMBASED);
                   1192: 	function change(name, content) {
                   1193: 	    element = document.getElementById(name);
                   1194: 	    element.innerHTML = content;
                   1195: 	}
                   1196: DOMBASED
                   1197:     }
                   1198: }
                   1199: 
                   1200: =pod
                   1201: 
1.648     raeburn  1202: =item * &changable_area($name,$origContent):
1.256     matthew  1203: 
                   1204: This provides a "changable area" that can be modified on the fly via
                   1205: the Javascript code provided in C<change_content_javascript>. $name is
                   1206: the name you will use to reference the area later; do not repeat the
                   1207: same name on a given HTML page more then once. $origContent is what
                   1208: the area will originally contain, which can be left blank.
                   1209: 
                   1210: =cut
                   1211: 
                   1212: sub changable_area {
                   1213:     my ($name, $origContent) = @_;
                   1214: 
1.258     albertel 1215:     if ($env{'browser.type'} eq 'netscape' &&
                   1216: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1217: 	# If this is netscape 4, we need to use the Layer tag
                   1218: 	return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
                   1219:     } else {
                   1220: 	return "<span id='$name'>$origContent</span>";
                   1221:     }
                   1222: }
                   1223: 
                   1224: =pod
                   1225: 
1.648     raeburn  1226: =item * &viewport_geometry_js 
1.590     raeburn  1227: 
                   1228: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
                   1229: 
                   1230: =cut
                   1231: 
                   1232: 
                   1233: sub viewport_geometry_js { 
                   1234:     return <<"GEOMETRY";
                   1235: var Geometry = {};
                   1236: function init_geometry() {
                   1237:     if (Geometry.init) { return };
                   1238:     Geometry.init=1;
                   1239:     if (window.innerHeight) {
                   1240:         Geometry.getViewportHeight   = function() { return window.innerHeight; };
                   1241:         Geometry.getViewportWidth   = function() { return window.innerWidth; };
                   1242:         Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
                   1243:         Geometry.getVerticalScroll   = function() { return window.pageYOffset; };
                   1244:     }
                   1245:     else if (document.documentElement && document.documentElement.clientHeight) {
                   1246:         Geometry.getViewportHeight =
                   1247:             function() { return document.documentElement.clientHeight; };
                   1248:         Geometry.getViewportWidth =
                   1249:             function() { return document.documentElement.clientWidth; };
                   1250: 
                   1251:         Geometry.getHorizontalScroll =
                   1252:             function() { return document.documentElement.scrollLeft; };
                   1253:         Geometry.getVerticalScroll =
                   1254:             function() { return document.documentElement.scrollTop; };
                   1255:     }
                   1256:     else if (document.body.clientHeight) {
                   1257:         Geometry.getViewportHeight =
                   1258:             function() { return document.body.clientHeight; };
                   1259:         Geometry.getViewportWidth =
                   1260:             function() { return document.body.clientWidth; };
                   1261:         Geometry.getHorizontalScroll =
                   1262:             function() { return document.body.scrollLeft; };
                   1263:         Geometry.getVerticalScroll =
                   1264:             function() { return document.body.scrollTop; };
                   1265:     }
                   1266: }
                   1267: 
                   1268: GEOMETRY
                   1269: }
                   1270: 
                   1271: =pod
                   1272: 
1.648     raeburn  1273: =item * &viewport_size_js()
1.590     raeburn  1274: 
                   1275: 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. 
                   1276: 
                   1277: =cut
                   1278: 
                   1279: sub viewport_size_js {
                   1280:     my $geometry = &viewport_geometry_js();
                   1281:     return <<"DIMS";
                   1282: 
                   1283: $geometry
                   1284: 
                   1285: function getViewportDims(width,height) {
                   1286:     init_geometry();
                   1287:     width.value = Geometry.getViewportWidth();
                   1288:     height.value = Geometry.getViewportHeight();
                   1289:     return;
                   1290: }
                   1291: 
                   1292: DIMS
                   1293: }
                   1294: 
                   1295: =pod
                   1296: 
1.648     raeburn  1297: =item * &resize_textarea_js()
1.565     albertel 1298: 
                   1299: emits the needed javascript to resize a textarea to be as big as possible
                   1300: 
                   1301: creates a function resize_textrea that takes two IDs first should be
                   1302: the id of the element to resize, second should be the id of a div that
                   1303: surrounds everything that comes after the textarea, this routine needs
                   1304: to be attached to the <body> for the onload and onresize events.
                   1305: 
1.648     raeburn  1306: =back
1.565     albertel 1307: 
                   1308: =cut
                   1309: 
                   1310: sub resize_textarea_js {
1.590     raeburn  1311:     my $geometry = &viewport_geometry_js();
1.565     albertel 1312:     return <<"RESIZE";
                   1313:     <script type="text/javascript">
1.590     raeburn  1314: $geometry
1.565     albertel 1315: 
1.588     albertel 1316: function getX(element) {
                   1317:     var x = 0;
                   1318:     while (element) {
                   1319: 	x += element.offsetLeft;
                   1320: 	element = element.offsetParent;
                   1321:     }
                   1322:     return x;
                   1323: }
                   1324: function getY(element) {
                   1325:     var y = 0;
                   1326:     while (element) {
                   1327: 	y += element.offsetTop;
                   1328: 	element = element.offsetParent;
                   1329:     }
                   1330:     return y;
                   1331: }
                   1332: 
                   1333: 
1.565     albertel 1334: function resize_textarea(textarea_id,bottom_id) {
                   1335:     init_geometry();
                   1336:     var textarea        = document.getElementById(textarea_id);
                   1337:     //alert(textarea);
                   1338: 
1.588     albertel 1339:     var textarea_top    = getY(textarea);
1.565     albertel 1340:     var textarea_height = textarea.offsetHeight;
                   1341:     var bottom          = document.getElementById(bottom_id);
1.588     albertel 1342:     var bottom_top      = getY(bottom);
1.565     albertel 1343:     var bottom_height   = bottom.offsetHeight;
                   1344:     var window_height   = Geometry.getViewportHeight();
1.588     albertel 1345:     var fudge           = 23;
1.565     albertel 1346:     var new_height      = window_height-fudge-textarea_top-bottom_height;
                   1347:     if (new_height < 300) {
                   1348: 	new_height = 300;
                   1349:     }
                   1350:     textarea.style.height=new_height+'px';
                   1351: }
                   1352: </script>
                   1353: RESIZE
                   1354: 
                   1355: }
                   1356: 
                   1357: =pod
                   1358: 
1.256     matthew  1359: =head1 Excel and CSV file utility routines
                   1360: 
                   1361: =over 4
                   1362: 
                   1363: =cut
                   1364: 
                   1365: ###############################################################
                   1366: ###############################################################
                   1367: 
                   1368: =pod
                   1369: 
1.648     raeburn  1370: =item * &csv_translate($text) 
1.37      matthew  1371: 
1.185     www      1372: Translate $text to allow it to be output as a 'comma separated values' 
1.37      matthew  1373: format.
                   1374: 
                   1375: =cut
                   1376: 
1.180     matthew  1377: ###############################################################
                   1378: ###############################################################
1.37      matthew  1379: sub csv_translate {
                   1380:     my $text = shift;
                   1381:     $text =~ s/\"/\"\"/g;
1.209     albertel 1382:     $text =~ s/\n/ /g;
1.37      matthew  1383:     return $text;
                   1384: }
1.180     matthew  1385: 
                   1386: ###############################################################
                   1387: ###############################################################
                   1388: 
                   1389: =pod
                   1390: 
1.648     raeburn  1391: =item * &define_excel_formats()
1.180     matthew  1392: 
                   1393: Define some commonly used Excel cell formats.
                   1394: 
                   1395: Currently supported formats:
                   1396: 
                   1397: =over 4
                   1398: 
                   1399: =item header
                   1400: 
                   1401: =item bold
                   1402: 
                   1403: =item h1
                   1404: 
                   1405: =item h2
                   1406: 
                   1407: =item h3
                   1408: 
1.256     matthew  1409: =item h4
                   1410: 
                   1411: =item i
                   1412: 
1.180     matthew  1413: =item date
                   1414: 
                   1415: =back
                   1416: 
                   1417: Inputs: $workbook
                   1418: 
                   1419: Returns: $format, a hash reference.
                   1420: 
                   1421: =cut
                   1422: 
                   1423: ###############################################################
                   1424: ###############################################################
                   1425: sub define_excel_formats {
                   1426:     my ($workbook) = @_;
                   1427:     my $format;
                   1428:     $format->{'header'} = $workbook->add_format(bold      => 1, 
                   1429:                                                 bottom    => 1,
                   1430:                                                 align     => 'center');
                   1431:     $format->{'bold'} = $workbook->add_format(bold=>1);
                   1432:     $format->{'h1'}   = $workbook->add_format(bold=>1, size=>18);
                   1433:     $format->{'h2'}   = $workbook->add_format(bold=>1, size=>16);
                   1434:     $format->{'h3'}   = $workbook->add_format(bold=>1, size=>14);
1.255     matthew  1435:     $format->{'h4'}   = $workbook->add_format(bold=>1, size=>12);
1.246     matthew  1436:     $format->{'i'}    = $workbook->add_format(italic=>1);
1.180     matthew  1437:     $format->{'date'} = $workbook->add_format(num_format=>
1.207     matthew  1438:                                             'mm/dd/yyyy hh:mm:ss');
1.180     matthew  1439:     return $format;
                   1440: }
                   1441: 
                   1442: ###############################################################
                   1443: ###############################################################
1.113     bowersj2 1444: 
                   1445: =pod
                   1446: 
1.648     raeburn  1447: =item * &create_workbook()
1.255     matthew  1448: 
                   1449: Create an Excel worksheet.  If it fails, output message on the
                   1450: request object and return undefs.
                   1451: 
                   1452: Inputs: Apache request object
                   1453: 
                   1454: Returns (undef) on failure, 
                   1455:     Excel worksheet object, scalar with filename, and formats 
                   1456:     from &Apache::loncommon::define_excel_formats on success
                   1457: 
                   1458: =cut
                   1459: 
                   1460: ###############################################################
                   1461: ###############################################################
                   1462: sub create_workbook {
                   1463:     my ($r) = @_;
                   1464:         #
                   1465:     # Create the excel spreadsheet
                   1466:     my $filename = '/prtspool/'.
1.258     albertel 1467:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255     matthew  1468:         time.'_'.rand(1000000000).'.xls';
                   1469:     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
                   1470:     if (! defined($workbook)) {
                   1471:         $r->log_error("Error creating excel spreadsheet $filename: $!");
                   1472:         $r->print('<p>'.&mt("Unable to create new Excel file.  ".
                   1473:                             "This error has been logged.  ".
                   1474:                             "Please alert your LON-CAPA administrator").
                   1475:                   '</p>');
                   1476:         return (undef);
                   1477:     }
                   1478:     #
                   1479:     $workbook->set_tempdir('/home/httpd/perl/tmp');
                   1480:     #
                   1481:     my $format = &Apache::loncommon::define_excel_formats($workbook);
                   1482:     return ($workbook,$filename,$format);
                   1483: }
                   1484: 
                   1485: ###############################################################
                   1486: ###############################################################
                   1487: 
                   1488: =pod
                   1489: 
1.648     raeburn  1490: =item * &create_text_file()
1.113     bowersj2 1491: 
1.542     raeburn  1492: Create a file to write to and eventually make available to the user.
1.256     matthew  1493: If file creation fails, outputs an error message on the request object and 
                   1494: return undefs.
1.113     bowersj2 1495: 
1.256     matthew  1496: Inputs: Apache request object, and file suffix
1.113     bowersj2 1497: 
1.256     matthew  1498: Returns (undef) on failure, 
                   1499:     Filehandle and filename on success.
1.113     bowersj2 1500: 
                   1501: =cut
                   1502: 
1.256     matthew  1503: ###############################################################
                   1504: ###############################################################
                   1505: sub create_text_file {
                   1506:     my ($r,$suffix) = @_;
                   1507:     if (! defined($suffix)) { $suffix = 'txt'; };
                   1508:     my $fh;
                   1509:     my $filename = '/prtspool/'.
1.258     albertel 1510:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256     matthew  1511:         time.'_'.rand(1000000000).'.'.$suffix;
                   1512:     $fh = Apache::File->new('>/home/httpd'.$filename);
                   1513:     if (! defined($fh)) {
                   1514:         $r->log_error("Couldn't open $filename for output $!");
                   1515:         $r->print("Problems occured in creating the output file.  ".
                   1516:                   "This error has been logged.  ".
                   1517:                   "Please alert your LON-CAPA administrator.");
1.113     bowersj2 1518:     }
1.256     matthew  1519:     return ($fh,$filename)
1.113     bowersj2 1520: }
                   1521: 
                   1522: 
1.256     matthew  1523: =pod 
1.113     bowersj2 1524: 
                   1525: =back
                   1526: 
                   1527: =cut
1.37      matthew  1528: 
                   1529: ###############################################################
1.33      matthew  1530: ##        Home server <option> list generating code          ##
                   1531: ###############################################################
1.35      matthew  1532: 
1.169     www      1533: # ------------------------------------------
                   1534: 
                   1535: sub domain_select {
                   1536:     my ($name,$value,$multiple)=@_;
                   1537:     my %domains=map { 
1.514     albertel 1538: 	$_ => $_.' '. &Apache::lonnet::domain($_,'description') 
1.512     albertel 1539:     } &Apache::lonnet::all_domains();
1.169     www      1540:     if ($multiple) {
                   1541: 	$domains{''}=&mt('Any domain');
1.550     albertel 1542: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287     albertel 1543: 	return &multiple_select_form($name,$value,4,\%domains);
1.169     www      1544:     } else {
1.550     albertel 1545: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.169     www      1546: 	return &select_form($name,$value,%domains);
                   1547:     }
                   1548: }
                   1549: 
1.282     albertel 1550: #-------------------------------------------
                   1551: 
                   1552: =pod
                   1553: 
1.519     raeburn  1554: =head1 Routines for form select boxes
                   1555: 
                   1556: =over 4
                   1557: 
1.648     raeburn  1558: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282     albertel 1559: 
                   1560: Returns a string containing a <select> element int multiple mode
                   1561: 
                   1562: 
                   1563: Args:
                   1564:   $name - name of the <select> element
1.506     raeburn  1565:   $value - scalar or array ref of values that should already be selected
1.282     albertel 1566:   $size - number of rows long the select element is
1.283     albertel 1567:   $hash - the elements should be 'option' => 'shown text'
1.282     albertel 1568:           (shown text should already have been &mt())
1.506     raeburn  1569:   $order - (optional) array ref of the order to show the elements in
1.283     albertel 1570: 
1.282     albertel 1571: =cut
                   1572: 
                   1573: #-------------------------------------------
1.169     www      1574: sub multiple_select_form {
1.284     albertel 1575:     my ($name,$value,$size,$hash,$order)=@_;
1.169     www      1576:     my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
                   1577:     my $output='';
1.191     matthew  1578:     if (! defined($size)) {
                   1579:         $size = 4;
1.283     albertel 1580:         if (scalar(keys(%$hash))<4) {
                   1581:             $size = scalar(keys(%$hash));
1.191     matthew  1582:         }
                   1583:     }
1.169     www      1584:     $output.="\n<select name='$name' size='$size' multiple='1'>";
1.501     banghart 1585:     my @order;
1.506     raeburn  1586:     if (ref($order) eq 'ARRAY')  {
                   1587:         @order = @{$order};
                   1588:     } else {
                   1589:         @order = sort(keys(%$hash));
1.501     banghart 1590:     }
                   1591:     if (exists($$hash{'select_form_order'})) {
                   1592:         @order = @{$$hash{'select_form_order'}};
                   1593:     }
                   1594:         
1.284     albertel 1595:     foreach my $key (@order) {
1.356     albertel 1596:         $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284     albertel 1597:         $output.='selected="selected" ' if ($selected{$key});
                   1598:         $output.='>'.$hash->{$key}."</option>\n";
1.169     www      1599:     }
                   1600:     $output.="</select>\n";
                   1601:     return $output;
                   1602: }
                   1603: 
1.88      www      1604: #-------------------------------------------
                   1605: 
                   1606: =pod
                   1607: 
1.648     raeburn  1608: =item * &select_form($defdom,$name,%hash)
1.88      www      1609: 
                   1610: Returns a string containing a <select name='$name' size='1'> form to 
                   1611: allow a user to select options from a hash option_name => displayed text.  
                   1612: See lonrights.pm for an example invocation and use.
                   1613: 
                   1614: =cut
                   1615: 
                   1616: #-------------------------------------------
                   1617: sub select_form {
                   1618:     my ($def,$name,%hash) = @_;
                   1619:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
1.128     albertel 1620:     my @keys;
                   1621:     if (exists($hash{'select_form_order'})) {
                   1622: 	@keys=@{$hash{'select_form_order'}};
                   1623:     } else {
                   1624: 	@keys=sort(keys(%hash));
                   1625:     }
1.356     albertel 1626:     foreach my $key (@keys) {
                   1627:         $selectform.=
                   1628: 	    '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
                   1629:             ($key eq $def ? 'selected="selected" ' : '').
                   1630:                 ">".&mt($hash{$key})."</option>\n";
1.88      www      1631:     }
                   1632:     $selectform.="</select>";
                   1633:     return $selectform;
                   1634: }
                   1635: 
1.475     www      1636: # For display filters
                   1637: 
                   1638: sub display_filter {
                   1639:     if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477     www      1640:     if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.475     www      1641:     return '<nobr><label>'.&mt('Records [_1]',
                   1642: 			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
                   1643: 							   (&mt('all'),10,20,50,100,1000,10000))).
1.478     www      1644: 	   '</label></nobr> <nobr>'.
1.475     www      1645:            &mt('Filter [_1]',
1.477     www      1646: 	   &select_form($env{'form.displayfilter'},
                   1647: 			'displayfilter',
                   1648: 			('currentfolder' => 'Current folder/page',
                   1649: 			 'containing' => 'Containing phrase',
                   1650: 			 'none' => 'None'))).
1.478     www      1651: 			 '<input type="text" name="containingphrase" size="30" value="'.&HTML::Entities::encode($env{'form.containingphrase'}).'" /></nobr>';
1.475     www      1652: }
                   1653: 
1.167     www      1654: sub gradeleveldescription {
                   1655:     my $gradelevel=shift;
                   1656:     my %gradelevels=(0 => 'Not specified',
                   1657: 		     1 => 'Grade 1',
                   1658: 		     2 => 'Grade 2',
                   1659: 		     3 => 'Grade 3',
                   1660: 		     4 => 'Grade 4',
                   1661: 		     5 => 'Grade 5',
                   1662: 		     6 => 'Grade 6',
                   1663: 		     7 => 'Grade 7',
                   1664: 		     8 => 'Grade 8',
                   1665: 		     9 => 'Grade 9',
                   1666: 		     10 => 'Grade 10',
                   1667: 		     11 => 'Grade 11',
                   1668: 		     12 => 'Grade 12',
                   1669: 		     13 => 'Grade 13',
                   1670: 		     14 => '100 Level',
                   1671: 		     15 => '200 Level',
                   1672: 		     16 => '300 Level',
                   1673: 		     17 => '400 Level',
                   1674: 		     18 => 'Graduate Level');
                   1675:     return &mt($gradelevels{$gradelevel});
                   1676: }
                   1677: 
1.163     www      1678: sub select_level_form {
                   1679:     my ($deflevel,$name)=@_;
                   1680:     unless ($deflevel) { $deflevel=0; }
1.167     www      1681:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
                   1682:     for (my $i=0; $i<=18; $i++) {
                   1683:         $selectform.="<option value=\"$i\" ".
1.253     albertel 1684:             ($i==$deflevel ? 'selected="selected" ' : '').
1.167     www      1685:                 ">".&gradeleveldescription($i)."</option>\n";
                   1686:     }
                   1687:     $selectform.="</select>";
                   1688:     return $selectform;
1.163     www      1689: }
1.167     www      1690: 
1.35      matthew  1691: #-------------------------------------------
                   1692: 
1.45      matthew  1693: =pod
                   1694: 
1.648     raeburn  1695: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc)
1.35      matthew  1696: 
                   1697: Returns a string containing a <select name='$name' size='1'> form to 
                   1698: allow a user to select the domain to preform an operation in.  
                   1699: See loncreateuser.pm for an example invocation and use.
                   1700: 
1.90      www      1701: If the $includeempty flag is set, it also includes an empty choice ("no domain
                   1702: selected");
                   1703: 
1.563     raeburn  1704: If the $showdomdesc flag is set, the domain name is followed by the domain description. 
                   1705: 
1.35      matthew  1706: =cut
                   1707: 
                   1708: #-------------------------------------------
1.34      matthew  1709: sub select_dom_form {
1.563     raeburn  1710:     my ($defdom,$name,$includeempty,$showdomdesc) = @_;
1.550     albertel 1711:     my @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
1.90      www      1712:     if ($includeempty) { @domains=('',@domains); }
1.34      matthew  1713:     my $selectdomain = "<select name=\"$name\" size=\"1\">\n";
1.356     albertel 1714:     foreach my $dom (@domains) {
                   1715:         $selectdomain.="<option value=\"$dom\" ".
1.563     raeburn  1716:             ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
                   1717:         if ($showdomdesc) {
                   1718:             if ($dom ne '') {
                   1719:                 my $domdesc = &Apache::lonnet::domain($dom,'description');
                   1720:                 if ($domdesc ne '') {
                   1721:                     $selectdomain .= ' ('.$domdesc.')';
                   1722:                 }
                   1723:             } 
                   1724:         }
                   1725:         $selectdomain .= "</option>\n";
1.34      matthew  1726:     }
                   1727:     $selectdomain.="</select>";
                   1728:     return $selectdomain;
                   1729: }
                   1730: 
1.35      matthew  1731: #-------------------------------------------
                   1732: 
1.45      matthew  1733: =pod
                   1734: 
1.648     raeburn  1735: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35      matthew  1736: 
1.586     raeburn  1737: input: 4 arguments (two required, two optional) - 
                   1738:     $domain - domain of new user
                   1739:     $name - name of form element
                   1740:     $default - Value of 'default' causes a default item to be first 
                   1741:                             option, and selected by default. 
                   1742:     $hide - Value of 'hide' causes hiding of the name of the server, 
                   1743:                             if 1 server found, or default, if 0 found.
1.594     raeburn  1744: output: returns 2 items: 
1.586     raeburn  1745: (a) form element which contains either:
                   1746:    (i) <select name="$name">
                   1747:         <option value="$hostid1">$hostid $servers{$hostid}</option>
                   1748:         <option value="$hostid2">$hostid $servers{$hostid}</option>       
                   1749:        </select>
                   1750:        form item if there are multiple library servers in $domain, or
                   1751:    (ii) an <input type="hidden" name="$name" value="$hostid" /> form item 
                   1752:        if there is only one library server in $domain.
                   1753: 
                   1754: (b) number of library servers found.
                   1755: 
                   1756: See loncreateuser.pm for example of use.
1.35      matthew  1757: 
                   1758: =cut
                   1759: 
                   1760: #-------------------------------------------
1.586     raeburn  1761: sub home_server_form_item {
                   1762:     my ($domain,$name,$default,$hide) = @_;
1.513     albertel 1763:     my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586     raeburn  1764:     my $result;
                   1765:     my $numlib = keys(%servers);
                   1766:     if ($numlib > 1) {
                   1767:         $result .= '<select name="'.$name.'" />'."\n";
                   1768:         if ($default) {
                   1769:             $result .= '<option value="default" selected>'.&mt('default').
                   1770:                        '</option>'."\n";
                   1771:         }
                   1772:         foreach my $hostid (sort(keys(%servers))) {
                   1773:             $result.= '<option value="'.$hostid.'">'.
                   1774: 	              $hostid.' '.$servers{$hostid}."</option>\n";
                   1775:         }
                   1776:         $result .= '</select>'."\n";
                   1777:     } elsif ($numlib == 1) {
                   1778:         my $hostid;
                   1779:         foreach my $item (keys(%servers)) {
                   1780:             $hostid = $item;
                   1781:         }
                   1782:         $result .= '<input type="hidden" name="'.$name.'" value="'.
                   1783:                    $hostid.'" />';
                   1784:                    if (!$hide) {
                   1785:                        $result .= $hostid.' '.$servers{$hostid};
                   1786:                    }
                   1787:                    $result .= "\n";
                   1788:     } elsif ($default) {
                   1789:         $result .= '<input type="hidden" name="'.$name.
                   1790:                    '" value="default" />';
                   1791:                    if (!$hide) {
                   1792:                        $result .= &mt('default');
                   1793:                    }
                   1794:                    $result .= "\n";
1.33      matthew  1795:     }
1.586     raeburn  1796:     return ($result,$numlib);
1.33      matthew  1797: }
1.112     bowersj2 1798: 
                   1799: =pod
                   1800: 
1.534     albertel 1801: =back 
                   1802: 
1.112     bowersj2 1803: =cut
1.87      matthew  1804: 
                   1805: ###############################################################
1.112     bowersj2 1806: ##                  Decoding User Agent                      ##
1.87      matthew  1807: ###############################################################
                   1808: 
                   1809: =pod
                   1810: 
1.112     bowersj2 1811: =head1 Decoding the User Agent
                   1812: 
                   1813: =over 4
                   1814: 
                   1815: =item * &decode_user_agent()
1.87      matthew  1816: 
                   1817: Inputs: $r
                   1818: 
                   1819: Outputs:
                   1820: 
                   1821: =over 4
                   1822: 
1.112     bowersj2 1823: =item * $httpbrowser
1.87      matthew  1824: 
1.112     bowersj2 1825: =item * $clientbrowser
1.87      matthew  1826: 
1.112     bowersj2 1827: =item * $clientversion
1.87      matthew  1828: 
1.112     bowersj2 1829: =item * $clientmathml
1.87      matthew  1830: 
1.112     bowersj2 1831: =item * $clientunicode
1.87      matthew  1832: 
1.112     bowersj2 1833: =item * $clientos
1.87      matthew  1834: 
                   1835: =back
                   1836: 
1.157     matthew  1837: =back 
                   1838: 
1.87      matthew  1839: =cut
                   1840: 
                   1841: ###############################################################
                   1842: ###############################################################
                   1843: sub decode_user_agent {
1.247     albertel 1844:     my ($r)=@_;
1.87      matthew  1845:     my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
                   1846:     my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
                   1847:     my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247     albertel 1848:     if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87      matthew  1849:     my $clientbrowser='unknown';
                   1850:     my $clientversion='0';
                   1851:     my $clientmathml='';
                   1852:     my $clientunicode='0';
                   1853:     for (my $i=0;$i<=$#browsertype;$i++) {
                   1854:         my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\:/,$browsertype[$i]);
                   1855: 	if (($httpbrowser=~/$match/i)  && ($httpbrowser!~/$notmatch/i)) {
                   1856: 	    $clientbrowser=$bname;
                   1857:             $httpbrowser=~/$vreg/i;
                   1858: 	    $clientversion=$1;
                   1859:             $clientmathml=($clientversion>=$minv);
                   1860:             $clientunicode=($clientversion>=$univ);
                   1861: 	}
                   1862:     }
                   1863:     my $clientos='unknown';
                   1864:     if (($httpbrowser=~/linux/i) ||
                   1865:         ($httpbrowser=~/unix/i) ||
                   1866:         ($httpbrowser=~/ux/i) ||
                   1867:         ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
                   1868:     if (($httpbrowser=~/vax/i) ||
                   1869:         ($httpbrowser=~/vms/i)) { $clientos='vms'; }
                   1870:     if ($httpbrowser=~/next/i) { $clientos='next'; }
                   1871:     if (($httpbrowser=~/mac/i) ||
                   1872:         ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
                   1873:     if ($httpbrowser=~/win/i) { $clientos='win'; }
                   1874:     if ($httpbrowser=~/embed/i) { $clientos='pda'; }
                   1875:     return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
                   1876:             $clientunicode,$clientos,);
                   1877: }
                   1878: 
1.32      matthew  1879: ###############################################################
                   1880: ##    Authentication changing form generation subroutines    ##
                   1881: ###############################################################
                   1882: ##
                   1883: ## All of the authform_xxxxxxx subroutines take their inputs in a
                   1884: ## hash, and have reasonable default values.
                   1885: ##
                   1886: ##    formname = the name given in the <form> tag.
1.35      matthew  1887: #-------------------------------------------
                   1888: 
1.45      matthew  1889: =pod
                   1890: 
1.112     bowersj2 1891: =head1 Authentication Routines
                   1892: 
                   1893: =over 4
                   1894: 
1.648     raeburn  1895: =item * &authform_xxxxxx()
1.35      matthew  1896: 
                   1897: The authform_xxxxxx subroutines provide javascript and html forms which 
                   1898: handle some of the conveniences required for authentication forms.  
                   1899: This is not an optimal method, but it works.  
                   1900: 
                   1901: =over 4
                   1902: 
1.112     bowersj2 1903: =item * authform_header
1.35      matthew  1904: 
1.112     bowersj2 1905: =item * authform_authorwarning
1.35      matthew  1906: 
1.112     bowersj2 1907: =item * authform_nochange
1.35      matthew  1908: 
1.112     bowersj2 1909: =item * authform_kerberos
1.35      matthew  1910: 
1.112     bowersj2 1911: =item * authform_internal
1.35      matthew  1912: 
1.112     bowersj2 1913: =item * authform_filesystem
1.35      matthew  1914: 
                   1915: =back
                   1916: 
1.648     raeburn  1917: See loncreateuser.pm for invocation and use examples.
1.157     matthew  1918: 
1.35      matthew  1919: =cut
                   1920: 
                   1921: #-------------------------------------------
1.32      matthew  1922: sub authform_header{  
                   1923:     my %in = (
                   1924:         formname => 'cu',
1.80      albertel 1925:         kerb_def_dom => '',
1.32      matthew  1926:         @_,
                   1927:     );
                   1928:     $in{'formname'} = 'document.' . $in{'formname'};
                   1929:     my $result='';
1.80      albertel 1930: 
                   1931: #---------------------------------------------- Code for upper case translation
                   1932:     my $Javascript_toUpperCase;
                   1933:     unless ($in{kerb_def_dom}) {
                   1934:         $Javascript_toUpperCase =<<"END";
                   1935:         switch (choice) {
                   1936:            case 'krb': currentform.elements[choicearg].value =
                   1937:                currentform.elements[choicearg].value.toUpperCase();
                   1938:                break;
                   1939:            default:
                   1940:         }
                   1941: END
                   1942:     } else {
                   1943:         $Javascript_toUpperCase = "";
                   1944:     }
                   1945: 
1.165     raeburn  1946:     my $radioval = "'nochange'";
1.591     raeburn  1947:     if (defined($in{'curr_authtype'})) {
                   1948:         if ($in{'curr_authtype'} ne '') {
                   1949:             $radioval = "'".$in{'curr_authtype'}."arg'";
                   1950:         }
1.174     matthew  1951:     }
1.165     raeburn  1952:     my $argfield = 'null';
1.591     raeburn  1953:     if (defined($in{'mode'})) {
1.165     raeburn  1954:         if ($in{'mode'} eq 'modifycourse')  {
1.591     raeburn  1955:             if (defined($in{'curr_autharg'})) {
                   1956:                 if ($in{'curr_autharg'} ne '') {
1.165     raeburn  1957:                     $argfield = "'$in{'curr_autharg'}'";
                   1958:                 }
                   1959:             }
                   1960:         }
                   1961:     }
                   1962: 
1.32      matthew  1963:     $result.=<<"END";
                   1964: var current = new Object();
1.165     raeburn  1965: current.radiovalue = $radioval;
                   1966: current.argfield = $argfield;
1.32      matthew  1967: 
                   1968: function changed_radio(choice,currentform) {
                   1969:     var choicearg = choice + 'arg';
                   1970:     // If a radio button in changed, we need to change the argfield
                   1971:     if (current.radiovalue != choice) {
                   1972:         current.radiovalue = choice;
                   1973:         if (current.argfield != null) {
                   1974:             currentform.elements[current.argfield].value = '';
                   1975:         }
                   1976:         if (choice == 'nochange') {
                   1977:             current.argfield = null;
                   1978:         } else {
                   1979:             current.argfield = choicearg;
                   1980:             switch(choice) {
                   1981:                 case 'krb': 
                   1982:                     currentform.elements[current.argfield].value = 
                   1983:                         "$in{'kerb_def_dom'}";
                   1984:                 break;
                   1985:               default:
                   1986:                 break;
                   1987:             }
                   1988:         }
                   1989:     }
                   1990:     return;
                   1991: }
1.22      www      1992: 
1.32      matthew  1993: function changed_text(choice,currentform) {
                   1994:     var choicearg = choice + 'arg';
                   1995:     if (currentform.elements[choicearg].value !='') {
1.80      albertel 1996:         $Javascript_toUpperCase
1.32      matthew  1997:         // clear old field
                   1998:         if ((current.argfield != choicearg) && (current.argfield != null)) {
                   1999:             currentform.elements[current.argfield].value = '';
                   2000:         }
                   2001:         current.argfield = choicearg;
                   2002:     }
                   2003:     set_auth_radio_buttons(choice,currentform);
                   2004:     return;
1.20      www      2005: }
1.32      matthew  2006: 
                   2007: function set_auth_radio_buttons(newvalue,currentform) {
                   2008:     var i=0;
                   2009:     while (i < currentform.login.length) {
                   2010:         if (currentform.login[i].value == newvalue) { break; }
                   2011:         i++;
                   2012:     }
                   2013:     if (i == currentform.login.length) {
                   2014:         return;
                   2015:     }
                   2016:     current.radiovalue = newvalue;
                   2017:     currentform.login[i].checked = true;
                   2018:     return;
                   2019: }
                   2020: END
                   2021:     return $result;
                   2022: }
                   2023: 
                   2024: sub authform_authorwarning{
                   2025:     my $result='';
1.144     matthew  2026:     $result='<i>'.
                   2027:         &mt('As a general rule, only authors or co-authors should be '.
                   2028:             'filesystem authenticated '.
                   2029:             '(which allows access to the server filesystem).')."</i>\n";
1.32      matthew  2030:     return $result;
                   2031: }
                   2032: 
                   2033: sub authform_nochange{  
                   2034:     my %in = (
                   2035:               formname => 'document.cu',
                   2036:               kerb_def_dom => 'MSU.EDU',
                   2037:               @_,
                   2038:           );
1.586     raeburn  2039:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'}); 
                   2040:     my $result;
                   2041:     if (keys(%can_assign) == 0) {
                   2042:         $result = &mt('Under you current role you are not permitted to change login settings for this user');  
                   2043:     } else {
                   2044:         $result = '<label>'.&mt('[_1] Do not change login data',
                   2045:                   '<input type="radio" name="login" value="nochange" '.
                   2046:                   'checked="checked" onclick="'.
1.281     albertel 2047:             "javascript:changed_radio('nochange',$in{'formname'});".'" />').
                   2048: 	    '</label>';
1.586     raeburn  2049:     }
1.32      matthew  2050:     return $result;
                   2051: }
                   2052: 
1.591     raeburn  2053: sub authform_kerberos {
1.32      matthew  2054:     my %in = (
                   2055:               formname => 'document.cu',
                   2056:               kerb_def_dom => 'MSU.EDU',
1.80      albertel 2057:               kerb_def_auth => 'krb4',
1.32      matthew  2058:               @_,
                   2059:               );
1.586     raeburn  2060:     my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
                   2061:         $autharg,$jscall);
                   2062:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.80      albertel 2063:     if ($in{'kerb_def_auth'} eq 'krb5') {
1.586     raeburn  2064:        $check5 = ' checked="on"';
1.80      albertel 2065:     } else {
1.586     raeburn  2066:        $check4 = ' checked="on"';
1.80      albertel 2067:     }
1.165     raeburn  2068:     $krbarg = $in{'kerb_def_dom'};
1.591     raeburn  2069:     if (defined($in{'curr_authtype'})) {
                   2070:         if ($in{'curr_authtype'} eq 'krb') {
1.586     raeburn  2071:             $krbcheck = ' checked="on"';
1.623     raeburn  2072:             if (defined($in{'mode'})) {
                   2073:                 if ($in{'mode'} eq 'modifyuser') {
                   2074:                     $krbcheck = '';
                   2075:                 }
                   2076:             }
1.591     raeburn  2077:             if (defined($in{'curr_kerb_ver'})) {
                   2078:                 if ($in{'curr_krb_ver'} eq '5') {
                   2079:                     $check5 = ' checked="on"';
                   2080:                     $check4 = '';
                   2081:                 } else {
                   2082:                     $check4 = ' checked="on"';
                   2083:                     $check5 = '';
                   2084:                 }
1.586     raeburn  2085:             }
1.591     raeburn  2086:             if (defined($in{'curr_autharg'})) {
1.165     raeburn  2087:                 $krbarg = $in{'curr_autharg'};
                   2088:             }
1.586     raeburn  2089:             if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591     raeburn  2090:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2091:                     $result = 
                   2092:     &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
                   2093:         $in{'curr_autharg'},$krbver);
                   2094:                 } else {
                   2095:                     $result =
                   2096:     &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
                   2097:                 }
                   2098:                 return $result; 
                   2099:             }
                   2100:         }
                   2101:     } else {
                   2102:         if ($authnum == 1) {
                   2103:             $authtype = '<input type="hidden" name="login" value="krb">';
1.165     raeburn  2104:         }
                   2105:     }
1.586     raeburn  2106:     if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
                   2107:         return;
1.587     raeburn  2108:     } elsif ($authtype eq '') {
1.591     raeburn  2109:         if (defined($in{'mode'})) {
1.587     raeburn  2110:             if ($in{'mode'} eq 'modifycourse') {
                   2111:                 if ($authnum == 1) {
                   2112:                     $authtype = '<input type="hidden" name="login" value="krb">';
                   2113:                 }
                   2114:             }
                   2115:         }
1.586     raeburn  2116:     }
                   2117:     $jscall = "javascript:changed_radio('krb',$in{'formname'});";
                   2118:     if ($authtype eq '') {
                   2119:         $authtype = '<input type="radio" name="login" value="krb" '.
                   2120:                     'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
                   2121:                     $krbcheck.' />';
                   2122:     }
                   2123:     if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
                   2124:         ($can_assign{'krb4'} && !$can_assign{'krb5'} && 
                   2125:          $in{'curr_authtype'} eq 'krb5') ||
                   2126:         (!$can_assign{'krb4'} && $can_assign{'krb5'} && 
                   2127:          $in{'curr_authtype'} eq 'krb4')) {
                   2128:         $result .= &mt
1.144     matthew  2129:         ('[_1] Kerberos authenticated with domain [_2] '.
1.281     albertel 2130:          '[_3] Version 4 [_4] Version 5 [_5]',
1.586     raeburn  2131:          '<label>'.$authtype,
1.281     albertel 2132:          '</label><input type="text" size="10" name="krbarg" '.
1.165     raeburn  2133:              'value="'.$krbarg.'" '.
1.144     matthew  2134:              'onchange="'.$jscall.'" />',
1.281     albertel 2135:          '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
                   2136:          '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
                   2137: 	 '</label>');
1.586     raeburn  2138:     } elsif ($can_assign{'krb4'}) {
                   2139:         $result .= &mt
                   2140:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2141:          '[_3] Version 4 [_4]',
                   2142:          '<label>'.$authtype,
                   2143:          '</label><input type="text" size="10" name="krbarg" '.
                   2144:              'value="'.$krbarg.'" '.
                   2145:              'onchange="'.$jscall.'" />',
                   2146:          '<label><input type="hidden" name="krbver" value="4" />',
                   2147:          '</label>');
                   2148:     } elsif ($can_assign{'krb5'}) {
                   2149:         $result .= &mt
                   2150:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2151:          '[_3] Version 5 [_4]',
                   2152:          '<label>'.$authtype,
                   2153:          '</label><input type="text" size="10" name="krbarg" '.
                   2154:              'value="'.$krbarg.'" '.
                   2155:              'onchange="'.$jscall.'" />',
                   2156:          '<label><input type="hidden" name="krbver" value="5" />',
                   2157:          '</label>');
                   2158:     }
1.32      matthew  2159:     return $result;
                   2160: }
                   2161: 
                   2162: sub authform_internal{  
1.586     raeburn  2163:     my %in = (
1.32      matthew  2164:                 formname => 'document.cu',
                   2165:                 kerb_def_dom => 'MSU.EDU',
                   2166:                 @_,
                   2167:                 );
1.586     raeburn  2168:     my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
                   2169:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2170:     if (defined($in{'curr_authtype'})) {
                   2171:         if ($in{'curr_authtype'} eq 'int') {
1.586     raeburn  2172:             if ($can_assign{'int'}) {
                   2173:                 $intcheck = 'checked="on" ';
1.623     raeburn  2174:                 if (defined($in{'mode'})) {
                   2175:                     if ($in{'mode'} eq 'modifyuser') {
                   2176:                         $intcheck = '';
                   2177:                     }
                   2178:                 }
1.591     raeburn  2179:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2180:                     $intarg = $in{'curr_autharg'};
                   2181:                 }
                   2182:             } else {
                   2183:                 $result = &mt('Currently internally authenticated.');
                   2184:                 return $result;
1.165     raeburn  2185:             }
                   2186:         }
1.586     raeburn  2187:     } else {
                   2188:         if ($authnum == 1) {
                   2189:             $authtype = '<input type="hidden" name="login" value="int">';
                   2190:         }
                   2191:     }
                   2192:     if (!$can_assign{'int'}) {
                   2193:         return;
1.587     raeburn  2194:     } elsif ($authtype eq '') {
1.591     raeburn  2195:         if (defined($in{'mode'})) {
1.587     raeburn  2196:             if ($in{'mode'} eq 'modifycourse') {
                   2197:                 if ($authnum == 1) {
                   2198:                     $authtype = '<input type="hidden" name="login" value="int">';
                   2199:                 }
                   2200:             }
                   2201:         }
1.165     raeburn  2202:     }
1.586     raeburn  2203:     $jscall = "javascript:changed_radio('int',$in{'formname'});";
                   2204:     if ($authtype eq '') {
                   2205:         $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
                   2206:                     ' onchange="'.$jscall.'" onclick="'.$jscall.'" />';
                   2207:     }
1.605     bisitz   2208:     $autharg = '<input type="password" size="10" name="intarg" value="'.
1.586     raeburn  2209:                $intarg.'" onchange="'.$jscall.'" />';
                   2210:     $result = &mt
1.144     matthew  2211:         ('[_1] Internally authenticated (with initial password [_2])',
1.586     raeburn  2212:          '<label>'.$authtype,'</label>'.$autharg);
1.620     www      2213:     $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  2214:     return $result;
                   2215: }
                   2216: 
                   2217: sub authform_local{  
                   2218:     my %in = (
                   2219:               formname => 'document.cu',
                   2220:               kerb_def_dom => 'MSU.EDU',
                   2221:               @_,
                   2222:               );
1.586     raeburn  2223:     my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
                   2224:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2225:     if (defined($in{'curr_authtype'})) {
                   2226:         if ($in{'curr_authtype'} eq 'loc') {
1.586     raeburn  2227:             if ($can_assign{'loc'}) {
                   2228:                 $loccheck = 'checked="on" ';
1.623     raeburn  2229:                 if (defined($in{'mode'})) {
                   2230:                     if ($in{'mode'} eq 'modifyuser') {
                   2231:                         $loccheck = '';
                   2232:                     }
                   2233:                 }
1.591     raeburn  2234:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2235:                     $locarg = $in{'curr_autharg'};
                   2236:                 }
                   2237:             } else {
                   2238:                 $result = &mt('Currently using local (institutional) authentication.');
                   2239:                 return $result;
1.165     raeburn  2240:             }
                   2241:         }
1.586     raeburn  2242:     } else {
                   2243:         if ($authnum == 1) {
                   2244:             $authtype = '<input type="hidden" name="login" value="loc">';
                   2245:         }
                   2246:     }
                   2247:     if (!$can_assign{'loc'}) {
                   2248:         return;
1.587     raeburn  2249:     } elsif ($authtype eq '') {
1.591     raeburn  2250:         if (defined($in{'mode'})) {
1.587     raeburn  2251:             if ($in{'mode'} eq 'modifycourse') {
                   2252:                 if ($authnum == 1) {
                   2253:                     $authtype = '<input type="hidden" name="login" value="loc">';
                   2254:                 }
                   2255:             }
                   2256:         }
1.165     raeburn  2257:     }
1.586     raeburn  2258:     $jscall = "javascript:changed_radio('loc',$in{'formname'});";
                   2259:     if ($authtype eq '') {
                   2260:         $authtype = '<input type="radio" name="login" value="loc" '.
                   2261:                     $loccheck.' onchange="'.$jscall.'" onclick="'.
                   2262:                     $jscall.'" />';
                   2263:     }
                   2264:     $autharg = '<input type="text" size="10" name="locarg" value="'.
                   2265:                $locarg.'" onchange="'.$jscall.'" />';
                   2266:     $result = &mt('[_1] Local Authentication with argument [_2]',
                   2267:                   '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  2268:     return $result;
                   2269: }
                   2270: 
                   2271: sub authform_filesystem{  
                   2272:     my %in = (
                   2273:               formname => 'document.cu',
                   2274:               kerb_def_dom => 'MSU.EDU',
                   2275:               @_,
                   2276:               );
1.586     raeburn  2277:     my ($fsyscheck,$result,$authtype,$autharg,$jscall);
                   2278:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2279:     if (defined($in{'curr_authtype'})) {
                   2280:         if ($in{'curr_authtype'} eq 'fsys') {
1.586     raeburn  2281:             if ($can_assign{'fsys'}) {
                   2282:                 $fsyscheck = 'checked="on" ';
1.623     raeburn  2283:                 if (defined($in{'mode'})) {
                   2284:                     if ($in{'mode'} eq 'modifyuser') {
                   2285:                         $fsyscheck = '';
                   2286:                     }
                   2287:                 }
1.586     raeburn  2288:             } else {
                   2289:                 $result = &mt('Currently Filesystem Authenticated.');
                   2290:                 return $result;
                   2291:             }           
                   2292:         }
                   2293:     } else {
                   2294:         if ($authnum == 1) {
                   2295:             $authtype = '<input type="hidden" name="login" value="fsys">';
                   2296:         }
                   2297:     }
                   2298:     if (!$can_assign{'fsys'}) {
                   2299:         return;
1.587     raeburn  2300:     } elsif ($authtype eq '') {
1.591     raeburn  2301:         if (defined($in{'mode'})) {
1.587     raeburn  2302:             if ($in{'mode'} eq 'modifycourse') {
                   2303:                 if ($authnum == 1) {
                   2304:                     $authtype = '<input type="hidden" name="login" value="fsys">';
                   2305:                 }
                   2306:             }
                   2307:         }
1.586     raeburn  2308:     }
                   2309:     $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
                   2310:     if ($authtype eq '') {
                   2311:         $authtype = '<input type="radio" name="login" value="fsys" '.
                   2312:                     $fsyscheck.' onchange="'.$jscall.'" onclick="'.
                   2313:                     $jscall.'" />';
                   2314:     }
                   2315:     $autharg = '<input type="text" size="10" name="fsysarg" value=""'.
                   2316:                ' onchange="'.$jscall.'" />';
                   2317:     $result = &mt
1.144     matthew  2318:         ('[_1] Filesystem Authenticated (with initial password [_2])',
1.281     albertel 2319:          '<label><input type="radio" name="login" value="fsys" '.
1.586     raeburn  2320:          $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.605     bisitz   2321:          '</label><input type="password" size="10" name="fsysarg" value="" '.
1.144     matthew  2322:                   'onchange="'.$jscall.'" />');
1.32      matthew  2323:     return $result;
                   2324: }
                   2325: 
1.586     raeburn  2326: sub get_assignable_auth {
                   2327:     my ($dom) = @_;
                   2328:     if ($dom eq '') {
                   2329:         $dom = $env{'request.role.domain'};
                   2330:     }
                   2331:     my %can_assign = (
                   2332:                           krb4 => 1,
                   2333:                           krb5 => 1,
                   2334:                           int  => 1,
                   2335:                           loc  => 1,
                   2336:                      );
                   2337:     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
                   2338:     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   2339:         if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
                   2340:             my $authhash = $domconfig{'usercreation'}{'authtypes'};
                   2341:             my $context;
                   2342:             if ($env{'request.role'} =~ /^au/) {
                   2343:                 $context = 'author';
                   2344:             } elsif ($env{'request.role'} =~ /^dc/) {
                   2345:                 $context = 'domain';
                   2346:             } elsif ($env{'request.course.id'}) {
                   2347:                 $context = 'course';
                   2348:             }
                   2349:             if ($context) {
                   2350:                 if (ref($authhash->{$context}) eq 'HASH') {
                   2351:                    %can_assign = %{$authhash->{$context}}; 
                   2352:                 }
                   2353:             }
                   2354:         }
                   2355:     }
                   2356:     my $authnum = 0;
                   2357:     foreach my $key (keys(%can_assign)) {
                   2358:         if ($can_assign{$key}) {
                   2359:             $authnum ++;
                   2360:         }
                   2361:     }
                   2362:     if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
                   2363:         $authnum --;
                   2364:     }
                   2365:     return ($authnum,%can_assign);
                   2366: }
                   2367: 
1.80      albertel 2368: ###############################################################
                   2369: ##    Get Kerberos Defaults for Domain                 ##
                   2370: ###############################################################
                   2371: ##
                   2372: ## Returns default kerberos version and an associated argument
                   2373: ## as listed in file domain.tab. If not listed, provides
                   2374: ## appropriate default domain and kerberos version.
                   2375: ##
                   2376: #-------------------------------------------
                   2377: 
                   2378: =pod
                   2379: 
1.648     raeburn  2380: =item * &get_kerberos_defaults()
1.80      albertel 2381: 
                   2382: get_kerberos_defaults($target_domain) returns the default kerberos
1.641     raeburn  2383: version and domain. If not found, it defaults to version 4 and the 
                   2384: domain of the server.
1.80      albertel 2385: 
1.648     raeburn  2386: =over 4
                   2387: 
1.80      albertel 2388: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
                   2389: 
1.648     raeburn  2390: =back
                   2391: 
                   2392: =back
                   2393: 
1.80      albertel 2394: =cut
                   2395: 
                   2396: #-------------------------------------------
                   2397: sub get_kerberos_defaults {
                   2398:     my $domain=shift;
1.641     raeburn  2399:     my ($krbdef,$krbdefdom);
                   2400:     my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
                   2401:     if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
                   2402:         $krbdef = $domdefaults{'auth_def'};
                   2403:         $krbdefdom = $domdefaults{'auth_arg_def'};
                   2404:     } else {
1.80      albertel 2405:         $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
                   2406:         my $krbdefdom=$1;
                   2407:         $krbdefdom=~tr/a-z/A-Z/;
                   2408:         $krbdef = "krb4";
                   2409:     }
                   2410:     return ($krbdef,$krbdefdom);
                   2411: }
1.112     bowersj2 2412: 
1.32      matthew  2413: 
1.46      matthew  2414: ###############################################################
                   2415: ##                Thesaurus Functions                        ##
                   2416: ###############################################################
1.20      www      2417: 
1.46      matthew  2418: =pod
1.20      www      2419: 
1.112     bowersj2 2420: =head1 Thesaurus Functions
                   2421: 
                   2422: =over 4
                   2423: 
1.648     raeburn  2424: =item * &initialize_keywords()
1.46      matthew  2425: 
                   2426: Initializes the package variable %Keywords if it is empty.  Uses the
                   2427: package variable $thesaurus_db_file.
                   2428: 
                   2429: =cut
                   2430: 
                   2431: ###################################################
                   2432: 
                   2433: sub initialize_keywords {
                   2434:     return 1 if (scalar keys(%Keywords));
                   2435:     # If we are here, %Keywords is empty, so fill it up
                   2436:     #   Make sure the file we need exists...
                   2437:     if (! -e $thesaurus_db_file) {
                   2438:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
                   2439:                                  " failed because it does not exist");
                   2440:         return 0;
                   2441:     }
                   2442:     #   Set up the hash as a database
                   2443:     my %thesaurus_db;
                   2444:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 2445:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  2446:         &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
                   2447:                                  $thesaurus_db_file);
                   2448:         return 0;
                   2449:     } 
                   2450:     #  Get the average number of appearances of a word.
                   2451:     my $avecount = $thesaurus_db{'average.count'};
                   2452:     #  Put keywords (those that appear > average) into %Keywords
                   2453:     while (my ($word,$data)=each (%thesaurus_db)) {
                   2454:         my ($count,undef) = split /:/,$data;
                   2455:         $Keywords{$word}++ if ($count > $avecount);
                   2456:     }
                   2457:     untie %thesaurus_db;
                   2458:     # Remove special values from %Keywords.
1.356     albertel 2459:     foreach my $value ('total.count','average.count') {
                   2460:         delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586     raeburn  2461:   }
1.46      matthew  2462:     return 1;
                   2463: }
                   2464: 
                   2465: ###################################################
                   2466: 
                   2467: =pod
                   2468: 
1.648     raeburn  2469: =item * &keyword($word)
1.46      matthew  2470: 
                   2471: Returns true if $word is a keyword.  A keyword is a word that appears more 
                   2472: than the average number of times in the thesaurus database.  Calls 
                   2473: &initialize_keywords
                   2474: 
                   2475: =cut
                   2476: 
                   2477: ###################################################
1.20      www      2478: 
                   2479: sub keyword {
1.46      matthew  2480:     return if (!&initialize_keywords());
                   2481:     my $word=lc(shift());
                   2482:     $word=~s/\W//g;
                   2483:     return exists($Keywords{$word});
1.20      www      2484: }
1.46      matthew  2485: 
                   2486: ###############################################################
                   2487: 
                   2488: =pod 
1.20      www      2489: 
1.648     raeburn  2490: =item * &get_related_words()
1.46      matthew  2491: 
1.160     matthew  2492: Look up a word in the thesaurus.  Takes a scalar argument and returns
1.46      matthew  2493: an array of words.  If the keyword is not in the thesaurus, an empty array
                   2494: will be returned.  The order of the words returned is determined by the
                   2495: database which holds them.
                   2496: 
                   2497: Uses global $thesaurus_db_file.
                   2498: 
                   2499: =cut
                   2500: 
                   2501: ###############################################################
                   2502: sub get_related_words {
                   2503:     my $keyword = shift;
                   2504:     my %thesaurus_db;
                   2505:     if (! -e $thesaurus_db_file) {
                   2506:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
                   2507:                                  "failed because the file does not exist");
                   2508:         return ();
                   2509:     }
                   2510:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 2511:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  2512:         return ();
                   2513:     } 
                   2514:     my @Words=();
1.429     www      2515:     my $count=0;
1.46      matthew  2516:     if (exists($thesaurus_db{$keyword})) {
1.356     albertel 2517: 	# The first element is the number of times
                   2518: 	# the word appears.  We do not need it now.
1.429     www      2519: 	my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
                   2520: 	my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
                   2521: 	my $threshold=$mostfrequentcount/10;
                   2522:         foreach my $possibleword (@RelatedWords) {
                   2523:             my ($word,$wordcount)=split(/\,/,$possibleword);
                   2524:             if ($wordcount>$threshold) {
                   2525: 		push(@Words,$word);
                   2526:                 $count++;
                   2527:                 if ($count>10) { last; }
                   2528: 	    }
1.20      www      2529:         }
                   2530:     }
1.46      matthew  2531:     untie %thesaurus_db;
                   2532:     return @Words;
1.14      harris41 2533: }
1.46      matthew  2534: 
1.112     bowersj2 2535: =pod
                   2536: 
                   2537: =back
                   2538: 
                   2539: =cut
1.61      www      2540: 
                   2541: # -------------------------------------------------------------- Plaintext name
1.81      albertel 2542: =pod
                   2543: 
1.112     bowersj2 2544: =head1 User Name Functions
                   2545: 
                   2546: =over 4
                   2547: 
1.648     raeburn  2548: =item * &plainname($uname,$udom,$first)
1.81      albertel 2549: 
1.112     bowersj2 2550: Takes a users logon name and returns it as a string in
1.226     albertel 2551: "first middle last generation" form 
                   2552: if $first is set to 'lastname' then it returns it as
                   2553: 'lastname generation, firstname middlename' if their is a lastname
1.81      albertel 2554: 
                   2555: =cut
1.61      www      2556: 
1.295     www      2557: 
1.81      albertel 2558: ###############################################################
1.61      www      2559: sub plainname {
1.226     albertel 2560:     my ($uname,$udom,$first)=@_;
1.537     albertel 2561:     return if (!defined($uname) || !defined($udom));
1.295     www      2562:     my %names=&getnames($uname,$udom);
1.226     albertel 2563:     my $name=&Apache::lonnet::format_name($names{'firstname'},
                   2564: 					  $names{'middlename'},
                   2565: 					  $names{'lastname'},
                   2566: 					  $names{'generation'},$first);
                   2567:     $name=~s/^\s+//;
1.62      www      2568:     $name=~s/\s+$//;
                   2569:     $name=~s/\s+/ /g;
1.353     albertel 2570:     if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62      www      2571:     return $name;
1.61      www      2572: }
1.66      www      2573: 
                   2574: # -------------------------------------------------------------------- Nickname
1.81      albertel 2575: =pod
                   2576: 
1.648     raeburn  2577: =item * &nickname($uname,$udom)
1.81      albertel 2578: 
                   2579: Gets a users name and returns it as a string as
                   2580: 
                   2581: "&quot;nickname&quot;"
1.66      www      2582: 
1.81      albertel 2583: if the user has a nickname or
                   2584: 
                   2585: "first middle last generation"
                   2586: 
                   2587: if the user does not
                   2588: 
                   2589: =cut
1.66      www      2590: 
                   2591: sub nickname {
                   2592:     my ($uname,$udom)=@_;
1.537     albertel 2593:     return if (!defined($uname) || !defined($udom));
1.295     www      2594:     my %names=&getnames($uname,$udom);
1.68      albertel 2595:     my $name=$names{'nickname'};
1.66      www      2596:     if ($name) {
                   2597:        $name='&quot;'.$name.'&quot;'; 
                   2598:     } else {
                   2599:        $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
                   2600: 	     $names{'lastname'}.' '.$names{'generation'};
                   2601:        $name=~s/\s+$//;
                   2602:        $name=~s/\s+/ /g;
                   2603:     }
                   2604:     return $name;
                   2605: }
                   2606: 
1.295     www      2607: sub getnames {
                   2608:     my ($uname,$udom)=@_;
1.537     albertel 2609:     return if (!defined($uname) || !defined($udom));
1.433     albertel 2610:     if ($udom eq 'public' && $uname eq 'public') {
                   2611: 	return ('lastname' => &mt('Public'));
                   2612:     }
1.295     www      2613:     my $id=$uname.':'.$udom;
                   2614:     my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
                   2615:     if ($cached) {
                   2616: 	return %{$names};
                   2617:     } else {
                   2618: 	my %loadnames=&Apache::lonnet::get('environment',
                   2619:                     ['firstname','middlename','lastname','generation','nickname'],
                   2620: 					 $udom,$uname);
                   2621: 	&Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
                   2622: 	return %loadnames;
                   2623:     }
                   2624: }
1.61      www      2625: 
1.542     raeburn  2626: # -------------------------------------------------------------------- getemails
1.648     raeburn  2627: 
1.542     raeburn  2628: =pod
                   2629: 
1.648     raeburn  2630: =item * &getemails($uname,$udom)
1.542     raeburn  2631: 
                   2632: Gets a user's email information and returns it as a hash with keys:
                   2633: notification, critnotification, permanentemail
                   2634: 
                   2635: For notification and critnotification, values are comma-separated lists 
1.648     raeburn  2636: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542     raeburn  2637:  
1.648     raeburn  2638: 
1.542     raeburn  2639: =cut
                   2640: 
1.648     raeburn  2641: 
1.466     albertel 2642: sub getemails {
                   2643:     my ($uname,$udom)=@_;
                   2644:     if ($udom eq 'public' && $uname eq 'public') {
                   2645: 	return;
                   2646:     }
1.467     www      2647:     if (!$udom) { $udom=$env{'user.domain'}; }
                   2648:     if (!$uname) { $uname=$env{'user.name'}; }
1.466     albertel 2649:     my $id=$uname.':'.$udom;
                   2650:     my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
                   2651:     if ($cached) {
                   2652: 	return %{$names};
                   2653:     } else {
                   2654: 	my %loadnames=&Apache::lonnet::get('environment',
                   2655:                     			   ['notification','critnotification',
                   2656: 					    'permanentemail'],
                   2657: 					   $udom,$uname);
                   2658: 	&Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
                   2659: 	return %loadnames;
                   2660:     }
                   2661: }
                   2662: 
1.551     albertel 2663: sub flush_email_cache {
                   2664:     my ($uname,$udom)=@_;
                   2665:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   2666:     if (!$uname) { $uname=$env{'user.name'};   }
                   2667:     return if ($udom eq 'public' && $uname eq 'public');
                   2668:     my $id=$uname.':'.$udom;
                   2669:     &Apache::lonnet::devalidate_cache_new('emailscache',$id);
                   2670: }
                   2671: 
1.61      www      2672: # ------------------------------------------------------------------ Screenname
1.81      albertel 2673: 
                   2674: =pod
                   2675: 
1.648     raeburn  2676: =item * &screenname($uname,$udom)
1.81      albertel 2677: 
                   2678: Gets a users screenname and returns it as a string
                   2679: 
                   2680: =cut
1.61      www      2681: 
                   2682: sub screenname {
                   2683:     my ($uname,$udom)=@_;
1.258     albertel 2684:     if ($uname eq $env{'user.name'} &&
                   2685: 	$udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212     albertel 2686:     my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68      albertel 2687:     return $names{'screenname'};
1.62      www      2688: }
                   2689: 
1.212     albertel 2690: 
1.62      www      2691: # ------------------------------------------------------------- Message Wrapper
                   2692: 
                   2693: sub messagewrapper {
1.369     www      2694:     my ($link,$username,$domain,$subject,$text)=@_;
1.62      www      2695:     return 
1.441     albertel 2696:         '<a href="/adm/email?compose=individual&amp;'.
                   2697:         'recname='.$username.'&amp;recdom='.$domain.
                   2698: 	'&amp;subject='.&escape($subject).'&amp;text='.&escape($text).'" '.
1.200     matthew  2699:         'title="'.&mt('Send message').'">'.$link.'</a>';
1.74      www      2700: }
                   2701: # --------------------------------------------------------------- Notes Wrapper
                   2702: 
                   2703: sub noteswrapper {
                   2704:     my ($link,$un,$do)=@_;
                   2705:     return 
                   2706: "<a href='/adm/email?recordftf=retrieve&recname=$un&recdom=$do'>$link</a>";
1.62      www      2707: }
                   2708: # ------------------------------------------------------------- Aboutme Wrapper
                   2709: 
                   2710: sub aboutmewrapper {
1.166     www      2711:     my ($link,$username,$domain,$target)=@_;
1.447     raeburn  2712:     if (!defined($username)  && !defined($domain)) {
                   2713:         return;
                   2714:     }
1.205     www      2715:     return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.454     banghart 2716: 	($target?' target="$target"':'').' title="'.&mt("View this user's personal page").'">'.$link.'</a>';
1.62      www      2717: }
                   2718: 
                   2719: # ------------------------------------------------------------ Syllabus Wrapper
                   2720: 
                   2721: 
                   2722: sub syllabuswrapper {
1.109     matthew  2723:     my ($linktext,$coursedir,$domain,$fontcolor)=@_;
                   2724:     if ($fontcolor) { 
                   2725:         $linktext='<font color="'.$fontcolor.'">'.$linktext.'</font>'; 
                   2726:     }
1.208     matthew  2727:     return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61      www      2728: }
1.14      harris41 2729: 
1.208     matthew  2730: sub track_student_link {
1.268     albertel 2731:     my ($linktext,$sname,$sdom,$target,$start) = @_;
                   2732:     my $link ="/adm/trackstudent?";
1.208     matthew  2733:     my $title = 'View recent activity';
                   2734:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   2735:         defined($sdom)  && $sdom  !~ /^\s*$/) {
1.268     albertel 2736:         $link .= "selected_student=$sname:$sdom";
1.208     matthew  2737:         $title .= ' of this student';
1.268     albertel 2738:     } 
1.208     matthew  2739:     if (defined($target) && $target !~ /^\s*$/) {
                   2740:         $target = qq{target="$target"};
                   2741:     } else {
                   2742:         $target = '';
                   2743:     }
1.268     albertel 2744:     if ($start) { $link.='&amp;start='.$start; }
1.554     albertel 2745:     $title = &mt($title);
                   2746:     $linktext = &mt($linktext);
1.448     albertel 2747:     return qq{<a href="$link" title="$title" $target>$linktext</a>}.
                   2748: 	&help_open_topic('View_recent_activity');
1.208     matthew  2749: }
                   2750: 
1.508     www      2751: # ===================================================== Display a student photo
                   2752: 
                   2753: 
1.509     albertel 2754: sub student_image_tag {
1.508     www      2755:     my ($domain,$user)=@_;
                   2756:     my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
                   2757:     if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
                   2758: 	return '<img src="'.$imgsrc.'" align="right" />';
                   2759:     } else {
                   2760: 	return '';
                   2761:     }
                   2762: }
                   2763: 
1.112     bowersj2 2764: =pod
                   2765: 
                   2766: =back
                   2767: 
                   2768: =head1 Access .tab File Data
                   2769: 
                   2770: =over 4
                   2771: 
1.648     raeburn  2772: =item * &languageids() 
1.112     bowersj2 2773: 
                   2774: returns list of all language ids
                   2775: 
                   2776: =cut
                   2777: 
1.14      harris41 2778: sub languageids {
1.16      harris41 2779:     return sort(keys(%language));
1.14      harris41 2780: }
                   2781: 
1.112     bowersj2 2782: =pod
                   2783: 
1.648     raeburn  2784: =item * &languagedescription() 
1.112     bowersj2 2785: 
                   2786: returns description of a specified language id
                   2787: 
                   2788: =cut
                   2789: 
1.14      harris41 2790: sub languagedescription {
1.125     www      2791:     my $code=shift;
                   2792:     return  ($supported_language{$code}?'* ':'').
                   2793:             $language{$code}.
1.126     www      2794: 	    ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145     www      2795: }
                   2796: 
                   2797: sub plainlanguagedescription {
                   2798:     my $code=shift;
                   2799:     return $language{$code};
                   2800: }
                   2801: 
                   2802: sub supportedlanguagecode {
                   2803:     my $code=shift;
                   2804:     return $supported_language{$code};
1.97      www      2805: }
                   2806: 
1.112     bowersj2 2807: =pod
                   2808: 
1.648     raeburn  2809: =item * &copyrightids() 
1.112     bowersj2 2810: 
                   2811: returns list of all copyrights
                   2812: 
                   2813: =cut
                   2814: 
                   2815: sub copyrightids {
                   2816:     return sort(keys(%cprtag));
                   2817: }
                   2818: 
                   2819: =pod
                   2820: 
1.648     raeburn  2821: =item * &copyrightdescription() 
1.112     bowersj2 2822: 
                   2823: returns description of a specified copyright id
                   2824: 
                   2825: =cut
                   2826: 
                   2827: sub copyrightdescription {
1.166     www      2828:     return &mt($cprtag{shift(@_)});
1.112     bowersj2 2829: }
1.197     matthew  2830: 
                   2831: =pod
                   2832: 
1.648     raeburn  2833: =item * &source_copyrightids() 
1.192     taceyjo1 2834: 
                   2835: returns list of all source copyrights
                   2836: 
                   2837: =cut
                   2838: 
                   2839: sub source_copyrightids {
                   2840:     return sort(keys(%scprtag));
                   2841: }
                   2842: 
                   2843: =pod
                   2844: 
1.648     raeburn  2845: =item * &source_copyrightdescription() 
1.192     taceyjo1 2846: 
                   2847: returns description of a specified source copyright id
                   2848: 
                   2849: =cut
                   2850: 
                   2851: sub source_copyrightdescription {
                   2852:     return &mt($scprtag{shift(@_)});
                   2853: }
1.112     bowersj2 2854: 
                   2855: =pod
                   2856: 
1.648     raeburn  2857: =item * &filecategories() 
1.112     bowersj2 2858: 
                   2859: returns list of all file categories
                   2860: 
                   2861: =cut
                   2862: 
                   2863: sub filecategories {
                   2864:     return sort(keys(%category_extensions));
                   2865: }
                   2866: 
                   2867: =pod
                   2868: 
1.648     raeburn  2869: =item * &filecategorytypes() 
1.112     bowersj2 2870: 
                   2871: returns list of file types belonging to a given file
                   2872: category
                   2873: 
                   2874: =cut
                   2875: 
                   2876: sub filecategorytypes {
1.356     albertel 2877:     my ($cat) = @_;
                   2878:     return @{$category_extensions{lc($cat)}};
1.112     bowersj2 2879: }
                   2880: 
                   2881: =pod
                   2882: 
1.648     raeburn  2883: =item * &fileembstyle() 
1.112     bowersj2 2884: 
                   2885: returns embedding style for a specified file type
                   2886: 
                   2887: =cut
                   2888: 
                   2889: sub fileembstyle {
                   2890:     return $fe{lc(shift(@_))};
1.169     www      2891: }
                   2892: 
1.351     www      2893: sub filemimetype {
                   2894:     return $fm{lc(shift(@_))};
                   2895: }
                   2896: 
1.169     www      2897: 
                   2898: sub filecategoryselect {
                   2899:     my ($name,$value)=@_;
1.189     matthew  2900:     return &select_form($value,$name,
1.169     www      2901: 			'' => &mt('Any category'),
                   2902: 			map { $_,$_ } sort(keys(%category_extensions)));
1.112     bowersj2 2903: }
                   2904: 
                   2905: =pod
                   2906: 
1.648     raeburn  2907: =item * &filedescription() 
1.112     bowersj2 2908: 
                   2909: returns description for a specified file type
                   2910: 
                   2911: =cut
                   2912: 
                   2913: sub filedescription {
1.188     matthew  2914:     my $file_description = $fd{lc(shift())};
                   2915:     $file_description =~ s:([\[\]]):~$1:g;
                   2916:     return &mt($file_description);
1.112     bowersj2 2917: }
                   2918: 
                   2919: =pod
                   2920: 
1.648     raeburn  2921: =item * &filedescriptionex() 
1.112     bowersj2 2922: 
                   2923: returns description for a specified file type with
                   2924: extra formatting
                   2925: 
                   2926: =cut
                   2927: 
                   2928: sub filedescriptionex {
                   2929:     my $ex=shift;
1.188     matthew  2930:     my $file_description = $fd{lc($ex)};
                   2931:     $file_description =~ s:([\[\]]):~$1:g;
                   2932:     return '.'.$ex.' '.&mt($file_description);
1.112     bowersj2 2933: }
                   2934: 
                   2935: # End of .tab access
                   2936: =pod
                   2937: 
                   2938: =back
                   2939: 
                   2940: =cut
                   2941: 
                   2942: # ------------------------------------------------------------------ File Types
                   2943: sub fileextensions {
                   2944:     return sort(keys(%fe));
                   2945: }
                   2946: 
1.97      www      2947: # ----------------------------------------------------------- Display Languages
                   2948: # returns a hash with all desired display languages
                   2949: #
                   2950: 
                   2951: sub display_languages {
                   2952:     my %languages=();
1.356     albertel 2953:     foreach my $lang (&preferred_languages()) {
                   2954: 	$languages{$lang}=1;
1.97      www      2955:     }
                   2956:     &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258     albertel 2957:     if ($env{'form.displaylanguage'}) {
1.356     albertel 2958: 	foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
                   2959: 	    $languages{$lang}=1;
1.97      www      2960:         }
                   2961:     }
                   2962:     return %languages;
1.14      harris41 2963: }
                   2964: 
1.117     www      2965: sub preferred_languages {
                   2966:     my @languages=();
1.654     www      2967:     if (($env{'request.role.adv'}) && ($env{'form.languages'})) {
                   2968:         @languages=(@languages,split(/\s*(\,|\;|\:)\s*/,$env{'form.languages'}));
                   2969:     }
1.258     albertel 2970:     if ($env{'course.'.$env{'request.course.id'}.'.languages'}) {
1.117     www      2971: 	@languages=(@languages,split(/\s*(\,|\;|\:)\s*/,
1.258     albertel 2972: 	         $env{'course.'.$env{'request.course.id'}.'.languages'}));
1.177     www      2973:     }
1.654     www      2974: 
1.258     albertel 2975:     if ($env{'environment.languages'}) {
1.459     albertel 2976: 	@languages=(@languages,
                   2977: 		    split(/\s*(\,|\;|\:)\s*/,$env{'environment.languages'}));
1.118     www      2978:     }
1.583     albertel 2979:     my $browser=$ENV{'HTTP_ACCEPT_LANGUAGE'};
1.162     www      2980:     if ($browser) {
1.583     albertel 2981: 	my @browser = 
                   2982: 	    map { (split(/\s*;\s*/,$_))[0] } (split(/\s*,\s*/,$browser));
                   2983: 	push(@languages,@browser);
1.162     www      2984:     }
1.641     raeburn  2985: 
                   2986:     foreach my $domtype ($env{'user.domain'},$env{'request.role.domain'},
                   2987:                          $Apache::lonnet::perlvar{'lonDefDomain'}) {
                   2988:         if ($domtype ne '') {
                   2989:             my %domdefs = &Apache::lonnet::get_domain_defaults($domtype);
                   2990:             if ($domdefs{'lang_def'} ne '') {
                   2991:                 push(@languages,$domdefs{'lang_def'});
                   2992:             }
                   2993:         }
1.118     www      2994:     }
                   2995: # turn "en-ca" into "en-ca,en"
                   2996:     my @genlanguages;
1.356     albertel 2997:     foreach my $lang (@languages) {
                   2998: 	unless ($lang=~/\w/) { next; }
1.583     albertel 2999: 	push(@genlanguages,$lang);
1.356     albertel 3000: 	if ($lang=~/(\-|\_)/) {
                   3001: 	    push(@genlanguages,(split(/(\-|\_)/,$lang))[0]);
1.118     www      3002: 	}
                   3003:     }
1.583     albertel 3004:     #uniqueify the languages list
                   3005:     my %count;
                   3006:     @genlanguages = map { $count{$_}++ == 0 ? $_ : () } @genlanguages;
1.118     www      3007:     return @genlanguages;
1.117     www      3008: }
                   3009: 
1.582     albertel 3010: sub languages {
                   3011:     my ($possible_langs) = @_;
                   3012:     my @preferred_langs = &preferred_languages();
                   3013:     if (!ref($possible_langs)) {
                   3014: 	if( wantarray ) {
                   3015: 	    return @preferred_langs;
                   3016: 	} else {
                   3017: 	    return $preferred_langs[0];
                   3018: 	}
                   3019:     }
                   3020:     my %possibilities = map { $_ => 1 } (@$possible_langs);
                   3021:     my @preferred_possibilities;
                   3022:     foreach my $preferred_lang (@preferred_langs) {
                   3023: 	if (exists($possibilities{$preferred_lang})) {
                   3024: 	    push(@preferred_possibilities, $preferred_lang);
                   3025: 	}
                   3026:     }
                   3027:     if( wantarray ) {
                   3028: 	return @preferred_possibilities;
                   3029:     }
                   3030:     return $preferred_possibilities[0];
                   3031: }
                   3032: 
1.112     bowersj2 3033: ###############################################################
                   3034: ##               Student Answer Attempts                     ##
                   3035: ###############################################################
                   3036: 
                   3037: =pod
                   3038: 
                   3039: =head1 Alternate Problem Views
                   3040: 
                   3041: =over 4
                   3042: 
1.648     raeburn  3043: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.112     bowersj2 3044:     $getattempt, $regexp, $gradesub)
                   3045: 
                   3046: Return string with previous attempt on problem. Arguments:
                   3047: 
                   3048: =over 4
                   3049: 
                   3050: =item * $symb: Problem, including path
                   3051: 
                   3052: =item * $username: username of the desired student
                   3053: 
                   3054: =item * $domain: domain of the desired student
1.14      harris41 3055: 
1.112     bowersj2 3056: =item * $course: Course ID
1.14      harris41 3057: 
1.112     bowersj2 3058: =item * $getattempt: Leave blank for all attempts, otherwise put
                   3059:     something
1.14      harris41 3060: 
1.112     bowersj2 3061: =item * $regexp: if string matches this regexp, the string will be
                   3062:     sent to $gradesub
1.14      harris41 3063: 
1.112     bowersj2 3064: =item * $gradesub: routine that processes the string if it matches $regexp
1.14      harris41 3065: 
1.112     bowersj2 3066: =back
1.14      harris41 3067: 
1.112     bowersj2 3068: The output string is a table containing all desired attempts, if any.
1.16      harris41 3069: 
1.112     bowersj2 3070: =cut
1.1       albertel 3071: 
                   3072: sub get_previous_attempt {
1.43      ng       3073:   my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub)=@_;
1.1       albertel 3074:   my $prevattempts='';
1.43      ng       3075:   no strict 'refs';
1.1       albertel 3076:   if ($symb) {
1.3       albertel 3077:     my (%returnhash)=
                   3078:       &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1       albertel 3079:     if ($returnhash{'version'}) {
                   3080:       my %lasthash=();
                   3081:       my $version;
                   3082:       for ($version=1;$version<=$returnhash{'version'};$version++) {
1.356     albertel 3083:         foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
                   3084: 	  $lasthash{$key}=$returnhash{$version.':'.$key};
1.19      harris41 3085:         }
1.1       albertel 3086:       }
1.596     albertel 3087:       $prevattempts=&start_data_table().&start_data_table_header_row();
                   3088:       $prevattempts.='<th>'.&mt('History').'</th>';
1.356     albertel 3089:       foreach my $key (sort(keys(%lasthash))) {
                   3090: 	my ($ign,@parts) = split(/\./,$key);
1.41      ng       3091: 	if ($#parts > 0) {
1.31      albertel 3092: 	  my $data=$parts[-1];
                   3093: 	  pop(@parts);
1.596     albertel 3094: 	  $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.'&nbsp;</th>';
1.31      albertel 3095: 	} else {
1.41      ng       3096: 	  if ($#parts == 0) {
                   3097: 	    $prevattempts.='<th>'.$parts[0].'</th>';
                   3098: 	  } else {
                   3099: 	    $prevattempts.='<th>'.$ign.'</th>';
                   3100: 	  }
1.31      albertel 3101: 	}
1.16      harris41 3102:       }
1.596     albertel 3103:       $prevattempts.=&end_data_table_header_row();
1.40      ng       3104:       if ($getattempt eq '') {
                   3105: 	for ($version=1;$version<=$returnhash{'version'};$version++) {
1.596     albertel 3106: 	  $prevattempts.=&start_data_table_row().
                   3107: 	      '<td>'.&mt('Transaction [_1]',$version).'</td>';
1.356     albertel 3108: 	    foreach my $key (sort(keys(%lasthash))) {
1.581     albertel 3109: 		my $value = &format_previous_attempt_value($key,
                   3110: 							   $returnhash{$version.':'.$key});
                   3111: 		$prevattempts.='<td>'.$value.'&nbsp;</td>';   
1.40      ng       3112: 	    }
1.596     albertel 3113: 	  $prevattempts.=&end_data_table_row();
1.40      ng       3114: 	 }
1.1       albertel 3115:       }
1.596     albertel 3116:       $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356     albertel 3117:       foreach my $key (sort(keys(%lasthash))) {
1.581     albertel 3118: 	my $value = &format_previous_attempt_value($key,$lasthash{$key});
1.356     albertel 3119: 	if ($key =~/$regexp$/ && (defined &$gradesub)) {$value = &$gradesub($value)}
1.40      ng       3120: 	$prevattempts.='<td>'.$value.'&nbsp;</td>';
1.16      harris41 3121:       }
1.596     albertel 3122:       $prevattempts.= &end_data_table_row().&end_data_table();
1.1       albertel 3123:     } else {
1.596     albertel 3124:       $prevattempts=
                   3125: 	  &start_data_table().&start_data_table_row().
                   3126: 	  '<td>'.&mt('Nothing submitted - no attempts.').'</td>'.
                   3127: 	  &end_data_table_row().&end_data_table();
1.1       albertel 3128:     }
                   3129:   } else {
1.596     albertel 3130:     $prevattempts=
                   3131: 	  &start_data_table().&start_data_table_row().
                   3132: 	  '<td>'.&mt('No data.').'</td>'.
                   3133: 	  &end_data_table_row().&end_data_table();
1.1       albertel 3134:   }
1.10      albertel 3135: }
                   3136: 
1.581     albertel 3137: sub format_previous_attempt_value {
                   3138:     my ($key,$value) = @_;
                   3139:     if ($key =~ /timestamp/) {
                   3140: 	$value = &Apache::lonlocal::locallocaltime($value);
                   3141:     } elsif (ref($value) eq 'ARRAY') {
                   3142: 	$value = '('.join(', ', @{ $value }).')';
                   3143:     } else {
                   3144: 	$value = &unescape($value);
                   3145:     }
                   3146:     return $value;
                   3147: }
                   3148: 
                   3149: 
1.107     albertel 3150: sub relative_to_absolute {
                   3151:     my ($url,$output)=@_;
                   3152:     my $parser=HTML::TokeParser->new(\$output);
                   3153:     my $token;
                   3154:     my $thisdir=$url;
                   3155:     my @rlinks=();
                   3156:     while ($token=$parser->get_token) {
                   3157: 	if ($token->[0] eq 'S') {
                   3158: 	    if ($token->[1] eq 'a') {
                   3159: 		if ($token->[2]->{'href'}) {
                   3160: 		    $rlinks[$#rlinks+1]=$token->[2]->{'href'};
                   3161: 		}
                   3162: 	    } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
                   3163: 		$rlinks[$#rlinks+1]=$token->[2]->{'src'};
                   3164: 	    } elsif ($token->[1] eq 'base') {
                   3165: 		$thisdir=$token->[2]->{'href'};
                   3166: 	    }
                   3167: 	}
                   3168:     }
                   3169:     $thisdir=~s-/[^/]*$--;
1.356     albertel 3170:     foreach my $link (@rlinks) {
                   3171: 	unless (($link=~/^http:\/\//i) ||
                   3172: 		($link=~/^\//) ||
                   3173: 		($link=~/^javascript:/i) ||
                   3174: 		($link=~/^mailto:/i) ||
                   3175: 		($link=~/^\#/)) {
                   3176: 	    my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
                   3177: 	    $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107     albertel 3178: 	}
                   3179:     }
                   3180: # -------------------------------------------------- Deal with Applet codebases
                   3181:     $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
                   3182:     return $output;
                   3183: }
                   3184: 
1.112     bowersj2 3185: =pod
                   3186: 
1.648     raeburn  3187: =item * &get_student_view()
1.112     bowersj2 3188: 
                   3189: show a snapshot of what student was looking at
                   3190: 
                   3191: =cut
                   3192: 
1.10      albertel 3193: sub get_student_view {
1.186     albertel 3194:   my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114     www      3195:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 3196:   my (%form);
1.10      albertel 3197:   my @elements=('symb','courseid','domain','username');
                   3198:   foreach my $element (@elements) {
1.186     albertel 3199:       $form{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 3200:   }
1.186     albertel 3201:   if (defined($moreenv)) {
                   3202:       %form=(%form,%{$moreenv});
                   3203:   }
1.236     albertel 3204:   if (defined($target)) { $form{'grade_target'} = $target; }
1.107     albertel 3205:   $feedurl=&Apache::lonnet::clutter($feedurl);
1.650     www      3206:   my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11      albertel 3207:   $userview=~s/\<body[^\>]*\>//gi;
                   3208:   $userview=~s/\<\/body\>//gi;
                   3209:   $userview=~s/\<html\>//gi;
                   3210:   $userview=~s/\<\/html\>//gi;
                   3211:   $userview=~s/\<head\>//gi;
                   3212:   $userview=~s/\<\/head\>//gi;
                   3213:   $userview=~s/action\s*\=/would_be_action\=/gi;
1.107     albertel 3214:   $userview=&relative_to_absolute($feedurl,$userview);
1.650     www      3215:   if (wantarray) {
                   3216:      return ($userview,$response);
                   3217:   } else {
                   3218:      return $userview;
                   3219:   }
                   3220: }
                   3221: 
                   3222: sub get_student_view_with_retries {
                   3223:   my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
                   3224: 
                   3225:     my $ok = 0;                 # True if we got a good response.
                   3226:     my $content;
                   3227:     my $response;
                   3228: 
                   3229:     # Try to get the student_view done. within the retries count:
                   3230:     
                   3231:     do {
                   3232:          ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
                   3233:          $ok      = $response->is_success;
                   3234:          if (!$ok) {
                   3235:             &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
                   3236:          }
                   3237:          $retries--;
                   3238:     } while (!$ok && ($retries > 0));
                   3239:     
                   3240:     if (!$ok) {
                   3241:        $content = '';          # On error return an empty content.
                   3242:     }
1.651     www      3243:     if (wantarray) {
                   3244:        return ($content, $response);
                   3245:     } else {
                   3246:        return $content;
                   3247:     }
1.11      albertel 3248: }
                   3249: 
1.112     bowersj2 3250: =pod
                   3251: 
1.648     raeburn  3252: =item * &get_student_answers() 
1.112     bowersj2 3253: 
                   3254: show a snapshot of how student was answering problem
                   3255: 
                   3256: =cut
                   3257: 
1.11      albertel 3258: sub get_student_answers {
1.100     sakharuk 3259:   my ($symb,$username,$domain,$courseid,%form) = @_;
1.114     www      3260:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 3261:   my (%moreenv);
1.11      albertel 3262:   my @elements=('symb','courseid','domain','username');
                   3263:   foreach my $element (@elements) {
1.186     albertel 3264:     $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 3265:   }
1.186     albertel 3266:   $moreenv{'grade_target'}='answer';
                   3267:   %moreenv=(%form,%moreenv);
1.497     raeburn  3268:   $feedurl = &Apache::lonnet::clutter($feedurl);
                   3269:   my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10      albertel 3270:   return $userview;
1.1       albertel 3271: }
1.116     albertel 3272: 
                   3273: =pod
                   3274: 
                   3275: =item * &submlink()
                   3276: 
1.242     albertel 3277: Inputs: $text $uname $udom $symb $target
1.116     albertel 3278: 
                   3279: Returns: A link to grades.pm such as to see the SUBM view of a student
                   3280: 
                   3281: =cut
                   3282: 
                   3283: ###############################################
                   3284: sub submlink {
1.242     albertel 3285:     my ($text,$uname,$udom,$symb,$target)=@_;
1.116     albertel 3286:     if (!($uname && $udom)) {
                   3287: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 3288: 	    &Apache::lonnet::whichuser($symb);
1.116     albertel 3289: 	if (!$symb) { $symb=$cursymb; }
                   3290:     }
1.254     matthew  3291:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      3292:     $symb=&escape($symb);
1.242     albertel 3293:     if ($target) { $target="target=\"$target\""; }
                   3294:     return '<a href="/adm/grades?&command=submission&'.
                   3295: 	'symb='.$symb.'&student='.$uname.
                   3296: 	'&userdom='.$udom.'" '.$target.'>'.$text.'</a>';
                   3297: }
                   3298: ##############################################
                   3299: 
                   3300: =pod
                   3301: 
                   3302: =item * &pgrdlink()
                   3303: 
                   3304: Inputs: $text $uname $udom $symb $target
                   3305: 
                   3306: Returns: A link to grades.pm such as to see the PGRD view of a student
                   3307: 
                   3308: =cut
                   3309: 
                   3310: ###############################################
                   3311: sub pgrdlink {
                   3312:     my $link=&submlink(@_);
                   3313:     $link=~s/(&command=submission)/$1&showgrading=yes/;
                   3314:     return $link;
                   3315: }
                   3316: ##############################################
                   3317: 
                   3318: =pod
                   3319: 
                   3320: =item * &pprmlink()
                   3321: 
                   3322: Inputs: $text $uname $udom $symb $target
                   3323: 
                   3324: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283     albertel 3325: student and a specific resource
1.242     albertel 3326: 
                   3327: =cut
                   3328: 
                   3329: ###############################################
                   3330: sub pprmlink {
                   3331:     my ($text,$uname,$udom,$symb,$target)=@_;
                   3332:     if (!($uname && $udom)) {
                   3333: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 3334: 	    &Apache::lonnet::whichuser($symb);
1.242     albertel 3335: 	if (!$symb) { $symb=$cursymb; }
                   3336:     }
1.254     matthew  3337:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      3338:     $symb=&escape($symb);
1.242     albertel 3339:     if ($target) { $target="target=\"$target\""; }
1.595     albertel 3340:     return '<a href="/adm/parmset?command=set&amp;'.
                   3341: 	'symb='.$symb.'&amp;uname='.$uname.
                   3342: 	'&amp;udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116     albertel 3343: }
                   3344: ##############################################
1.37      matthew  3345: 
1.112     bowersj2 3346: =pod
                   3347: 
                   3348: =back
                   3349: 
                   3350: =cut
                   3351: 
1.37      matthew  3352: ###############################################
1.51      www      3353: 
                   3354: 
                   3355: sub timehash {
                   3356:     my @ltime=localtime(shift);
                   3357:     return ( 'seconds' => $ltime[0],
                   3358:              'minutes' => $ltime[1],
                   3359:              'hours'   => $ltime[2],
                   3360:              'day'     => $ltime[3],
                   3361:              'month'   => $ltime[4]+1,
                   3362:              'year'    => $ltime[5]+1900,
                   3363:              'weekday' => $ltime[6],
                   3364:              'dayyear' => $ltime[7]+1,
                   3365:              'dlsav'   => $ltime[8] );
                   3366: }
                   3367: 
1.370     www      3368: sub utc_string {
                   3369:     my ($date)=@_;
1.371     www      3370:     return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370     www      3371: }
                   3372: 
1.51      www      3373: sub maketime {
                   3374:     my %th=@_;
                   3375:     return POSIX::mktime(
                   3376:         ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210     www      3377:          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70      www      3378: }
                   3379: 
                   3380: #########################################
1.51      www      3381: 
                   3382: sub findallcourses {
1.482     raeburn  3383:     my ($roles,$uname,$udom) = @_;
1.355     albertel 3384:     my %roles;
                   3385:     if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348     albertel 3386:     my %courses;
1.51      www      3387:     my $now=time;
1.482     raeburn  3388:     if (!defined($uname)) {
                   3389:         $uname = $env{'user.name'};
                   3390:     }
                   3391:     if (!defined($udom)) {
                   3392:         $udom = $env{'user.domain'};
                   3393:     }
                   3394:     if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
                   3395:         my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
                   3396:         if (!%roles) {
                   3397:             %roles = (
                   3398:                        cc => 1,
                   3399:                        in => 1,
                   3400:                        ep => 1,
                   3401:                        ta => 1,
                   3402:                        cr => 1,
                   3403:                        st => 1,
                   3404:              );
                   3405:         }
                   3406:         foreach my $entry (keys(%roleshash)) {
                   3407:             my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
                   3408:             if ($trole =~ /^cr/) { 
                   3409:                 next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
                   3410:             } else {
                   3411:                 next if (!exists($roles{$trole}));
                   3412:             }
                   3413:             if ($tend) {
                   3414:                 next if ($tend < $now);
                   3415:             }
                   3416:             if ($tstart) {
                   3417:                 next if ($tstart > $now);
                   3418:             }
                   3419:             my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role,$realsec);
                   3420:             (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
                   3421:             if ($secpart eq '') {
                   3422:                 ($cnum,$role) = split(/_/,$cnumpart); 
                   3423:                 $sec = 'none';
                   3424:                 $realsec = '';
                   3425:             } else {
                   3426:                 $cnum = $cnumpart;
                   3427:                 ($sec,$role) = split(/_/,$secpart);
                   3428:                 $realsec = $sec;
1.490     raeburn  3429:             }
1.482     raeburn  3430:             $courses{$cdom.'_'.$cnum}{$sec} = $trole.'/'.$cdom.'/'.$cnum.'/'.$realsec;
                   3431:         }
                   3432:     } else {
                   3433:         foreach my $key (keys(%env)) {
1.483     albertel 3434: 	    if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
                   3435:                  $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482     raeburn  3436: 	        my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
                   3437: 	        next if ($role eq 'ca' || $role eq 'aa');
                   3438: 	        next if (%roles && !exists($roles{$role}));
                   3439: 	        my ($starttime,$endtime)=split(/\./,$env{$key});
                   3440:                 my $active=1;
                   3441:                 if ($starttime) {
                   3442: 		    if ($now<$starttime) { $active=0; }
                   3443:                 }
                   3444:                 if ($endtime) {
                   3445:                     if ($now>$endtime) { $active=0; }
                   3446:                 }
                   3447:                 if ($active) {
                   3448:                     if ($sec eq '') {
                   3449:                         $sec = 'none';
                   3450:                     }
                   3451:                     $courses{$cdom.'_'.$cnum}{$sec} = 
                   3452:                                      $role.'/'.$cdom.'/'.$cnum.'/'.$sec;
1.474     raeburn  3453:                 }
                   3454:             }
1.51      www      3455:         }
                   3456:     }
1.474     raeburn  3457:     return %courses;
1.51      www      3458: }
1.37      matthew  3459: 
1.54      www      3460: ###############################################
1.474     raeburn  3461: 
                   3462: sub blockcheck {
1.482     raeburn  3463:     my ($setters,$activity,$uname,$udom) = @_;
1.490     raeburn  3464: 
                   3465:     if (!defined($udom)) {
                   3466:         $udom = $env{'user.domain'};
                   3467:     }
                   3468:     if (!defined($uname)) {
                   3469:         $uname = $env{'user.name'};
                   3470:     }
                   3471: 
                   3472:     # If uname and udom are for a course, check for blocks in the course.
                   3473: 
                   3474:     if (&Apache::lonnet::is_course($udom,$uname)) {
                   3475:         my %records = &Apache::lonnet::dump('comm_block',$udom,$uname);
1.502     raeburn  3476:         my ($startblock,$endblock)=&get_blocks($setters,$activity,$udom,$uname);
1.490     raeburn  3477:         return ($startblock,$endblock);
                   3478:     }
1.474     raeburn  3479: 
1.502     raeburn  3480:     my $startblock = 0;
                   3481:     my $endblock = 0;
1.482     raeburn  3482:     my %live_courses = &findallcourses(undef,$uname,$udom);
1.474     raeburn  3483: 
1.490     raeburn  3484:     # If uname is for a user, and activity is course-specific, i.e.,
                   3485:     # boards, chat or groups, check for blocking in current course only.
1.474     raeburn  3486: 
1.490     raeburn  3487:     if (($activity eq 'boards' || $activity eq 'chat' ||
                   3488:          $activity eq 'groups') && ($env{'request.course.id'})) {
                   3489:         foreach my $key (keys(%live_courses)) {
                   3490:             if ($key ne $env{'request.course.id'}) {
                   3491:                 delete($live_courses{$key});
                   3492:             }
                   3493:         }
                   3494:     }
                   3495: 
                   3496:     my $otheruser = 0;
                   3497:     my %own_courses;
                   3498:     if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
                   3499:         # Resource belongs to user other than current user.
                   3500:         $otheruser = 1;
                   3501:         # Gather courses for current user
                   3502:         %own_courses = 
                   3503:             &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
                   3504:     }
                   3505: 
                   3506:     # Gather active course roles - course coordinator, instructor, 
                   3507:     # exam proctor, ta, student, or custom role.
1.474     raeburn  3508: 
                   3509:     foreach my $course (keys(%live_courses)) {
1.482     raeburn  3510:         my ($cdom,$cnum);
                   3511:         if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
                   3512:             $cdom = $env{'course.'.$course.'.domain'};
                   3513:             $cnum = $env{'course.'.$course.'.num'};
                   3514:         } else {
1.490     raeburn  3515:             ($cdom,$cnum) = split(/_/,$course); 
1.482     raeburn  3516:         }
                   3517:         my $no_ownblock = 0;
                   3518:         my $no_userblock = 0;
1.533     raeburn  3519:         if ($otheruser && $activity ne 'com') {
1.490     raeburn  3520:             # Check if current user has 'evb' priv for this
                   3521:             if (defined($own_courses{$course})) {
                   3522:                 foreach my $sec (keys(%{$own_courses{$course}})) {
                   3523:                     my $checkrole = 'cm./'.$cdom.'/'.$cnum;
                   3524:                     if ($sec ne 'none') {
                   3525:                         $checkrole .= '/'.$sec;
                   3526:                     }
                   3527:                     if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   3528:                         $no_ownblock = 1;
                   3529:                         last;
                   3530:                     }
                   3531:                 }
                   3532:             }
                   3533:             # if they have 'evb' priv and are currently not playing student
                   3534:             next if (($no_ownblock) &&
                   3535:                  ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
                   3536:         }
1.474     raeburn  3537:         foreach my $sec (keys(%{$live_courses{$course}})) {
1.482     raeburn  3538:             my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474     raeburn  3539:             if ($sec ne 'none') {
1.482     raeburn  3540:                 $checkrole .= '/'.$sec;
1.474     raeburn  3541:             }
1.490     raeburn  3542:             if ($otheruser) {
                   3543:                 # Resource belongs to user other than current user.
                   3544:                 # Assemble privs for that user, and check for 'evb' priv.
1.482     raeburn  3545:                 my ($trole,$tdom,$tnum,$tsec);
                   3546:                 my $entry = $live_courses{$course}{$sec};
                   3547:                 if ($entry =~ /^cr/) {
                   3548:                     ($trole,$tdom,$tnum,$tsec) = 
                   3549:                       ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
                   3550:                 } else {
                   3551:                     ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
                   3552:                 }
                   3553:                 my ($spec,$area,$trest,%allroles,%userroles);
                   3554:                 $area = '/'.$tdom.'/'.$tnum;
                   3555:                 $trest = $tnum;
                   3556:                 if ($tsec ne '') {
                   3557:                     $area .= '/'.$tsec;
                   3558:                     $trest .= '/'.$tsec;
                   3559:                 }
                   3560:                 $spec = $trole.'.'.$area;
                   3561:                 if ($trole =~ /^cr/) {
                   3562:                     &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
                   3563:                                                       $tdom,$spec,$trest,$area);
                   3564:                 } else {
                   3565:                     &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
                   3566:                                                        $tdom,$spec,$trest,$area);
                   3567:                 }
                   3568:                 my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
1.486     raeburn  3569:                 if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
                   3570:                     if ($1) {
                   3571:                         $no_userblock = 1;
                   3572:                         last;
                   3573:                     }
                   3574:                 }
1.490     raeburn  3575:             } else {
                   3576:                 # Resource belongs to current user
                   3577:                 # Check for 'evb' priv via lonnet::allowed().
1.482     raeburn  3578:                 if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   3579:                     $no_ownblock = 1;
                   3580:                     last;
                   3581:                 }
1.474     raeburn  3582:             }
                   3583:         }
                   3584:         # if they have the evb priv and are currently not playing student
1.482     raeburn  3585:         next if (($no_ownblock) &&
1.491     albertel 3586:                  ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482     raeburn  3587:         next if ($no_userblock);
1.474     raeburn  3588: 
1.490     raeburn  3589:         # Retrieve blocking times and identity of blocker for course
                   3590:         # of specified user, unless user has 'evb' privilege.
1.502     raeburn  3591:         
                   3592:         my ($start,$end)=&get_blocks($setters,$activity,$cdom,$cnum);
                   3593:         if (($start != 0) && 
                   3594:             (($startblock == 0) || ($startblock > $start))) {
                   3595:             $startblock = $start;
                   3596:         }
                   3597:         if (($end != 0)  &&
                   3598:             (($endblock == 0) || ($endblock < $end))) {
                   3599:             $endblock = $end;
                   3600:         }
1.490     raeburn  3601:     }
                   3602:     return ($startblock,$endblock);
                   3603: }
                   3604: 
                   3605: sub get_blocks {
                   3606:     my ($setters,$activity,$cdom,$cnum) = @_;
                   3607:     my $startblock = 0;
                   3608:     my $endblock = 0;
                   3609:     my $course = $cdom.'_'.$cnum;
                   3610:     $setters->{$course} = {};
                   3611:     $setters->{$course}{'staff'} = [];
                   3612:     $setters->{$course}{'times'} = [];
                   3613:     my %records = &Apache::lonnet::dump('comm_block',$cdom,$cnum);
                   3614:     foreach my $record (keys(%records)) {
                   3615:         my ($start,$end) = ($record =~ m/^(\d+)____(\d+)$/);
                   3616:         if ($start <= time && $end >= time) {
                   3617:             my ($staff_name,$staff_dom,$title,$blocks) =
                   3618:                 &parse_block_record($records{$record});
                   3619:             if ($blocks->{$activity} eq 'on') {
                   3620:                 push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
                   3621:                 push(@{$$setters{$course}{'times'}}, [$start,$end]);
1.491     albertel 3622:                 if ( ($startblock == 0) || ($startblock > $start) ) {
                   3623:                     $startblock = $start;
1.490     raeburn  3624:                 }
1.491     albertel 3625:                 if ( ($endblock == 0) || ($endblock < $end) ) {
                   3626:                     $endblock = $end;
1.474     raeburn  3627:                 }
                   3628:             }
                   3629:         }
                   3630:     }
                   3631:     return ($startblock,$endblock);
                   3632: }
                   3633: 
                   3634: sub parse_block_record {
                   3635:     my ($record) = @_;
                   3636:     my ($setuname,$setudom,$title,$blocks);
                   3637:     if (ref($record) eq 'HASH') {
                   3638:         ($setuname,$setudom) = split(/:/,$record->{'setter'});
                   3639:         $title = &unescape($record->{'event'});
                   3640:         $blocks = $record->{'blocks'};
                   3641:     } else {
                   3642:         my @data = split(/:/,$record,3);
                   3643:         if (scalar(@data) eq 2) {
                   3644:             $title = $data[1];
                   3645:             ($setuname,$setudom) = split(/@/,$data[0]);
                   3646:         } else {
                   3647:             ($setuname,$setudom,$title) = @data;
                   3648:         }
                   3649:         $blocks = { 'com' => 'on' };
                   3650:     }
                   3651:     return ($setuname,$setudom,$title,$blocks);
                   3652: }
                   3653: 
                   3654: sub build_block_table {
                   3655:     my ($startblock,$endblock,$setters) = @_;
                   3656:     my %lt = &Apache::lonlocal::texthash(
                   3657:         'cacb' => 'Currently active communication blocks',
                   3658:         'cour' => 'Course',
                   3659:         'dura' => 'Duration',
                   3660:         'blse' => 'Block set by'
                   3661:     );
                   3662:     my $output;
1.476     raeburn  3663:     $output = '<br />'.$lt{'cacb'}.':<br />';
1.474     raeburn  3664:     $output .= &start_data_table();
                   3665:     $output .= '
                   3666: <tr>
                   3667:  <th>'.$lt{'cour'}.'</th>
                   3668:  <th>'.$lt{'dura'}.'</th>
                   3669:  <th>'.$lt{'blse'}.'</th>
                   3670: </tr>
                   3671: ';
                   3672:     foreach my $course (keys(%{$setters})) {
                   3673:         my %courseinfo=&Apache::lonnet::coursedescription($course);
                   3674:         for (my $i=0; $i<@{$$setters{$course}{staff}}; $i++) {
                   3675:             my ($uname,$udom) = @{$$setters{$course}{staff}[$i]};
1.490     raeburn  3676:             my $fullname = &plainname($uname,$udom);
                   3677:             if (defined($env{'user.name'}) && defined($env{'user.domain'})
                   3678:                 && $env{'user.name'} ne 'public' 
                   3679:                 && $env{'user.domain'} ne 'public') {
                   3680:                 $fullname = &aboutmewrapper($fullname,$uname,$udom);
                   3681:             }
1.474     raeburn  3682:             my ($openblock,$closeblock) = @{$$setters{$course}{times}[$i]};
                   3683:             $openblock = &Apache::lonlocal::locallocaltime($openblock);
                   3684:             $closeblock= &Apache::lonlocal::locallocaltime($closeblock);
                   3685:             $output .= &Apache::loncommon::start_data_table_row().
                   3686:                        '<td>'.$courseinfo{'description'}.'</td>'.
                   3687:                        '<td>'.$openblock.' to '.$closeblock.'</td>'.
1.490     raeburn  3688:                        '<td>'.$fullname.'</td>'.
1.474     raeburn  3689:                         &Apache::loncommon::end_data_table_row();
                   3690:         }
                   3691:     }
                   3692:     $output .= &end_data_table();
                   3693: }
                   3694: 
1.490     raeburn  3695: sub blocking_status {
                   3696:     my ($activity,$uname,$udom) = @_;
                   3697:     my %setters;
                   3698:     my ($blocked,$output,$ownitem,$is_course);
                   3699:     my ($startblock,$endblock)=&blockcheck(\%setters,$activity,$uname,$udom);
                   3700:     if ($startblock && $endblock) {
                   3701:         $blocked = 1;
                   3702:         if (wantarray) {
                   3703:             my $category;
                   3704:             if ($activity eq 'boards') {
                   3705:                 $category = 'Discussion posts in this course';
                   3706:             } elsif ($activity eq 'blogs') {
                   3707:                 $category = 'Blogs';
                   3708:             } elsif ($activity eq 'port') {
                   3709:                 if (defined($uname) && defined($udom)) {
                   3710:                     if ($uname eq $env{'user.name'} &&
                   3711:                         $udom eq $env{'user.domain'}) {
                   3712:                         $ownitem = 1;
                   3713:                     }
                   3714:                 }
                   3715:                 $is_course = &Apache::lonnet::is_course($udom,$uname);
                   3716:                 if ($ownitem) { 
                   3717:                     $category = 'Your portfolio files';  
                   3718:                 } elsif ($is_course) {
                   3719:                     my $coursedesc;
                   3720:                     foreach my $course (keys(%setters)) {
                   3721:                         my %courseinfo =
                   3722:                              &Apache::lonnet::coursedescription($course);
                   3723:                         $coursedesc = $courseinfo{'description'};
                   3724:                     }
                   3725:                     $category = "Group files in the course '$coursedesc'";
                   3726:                 } else {
                   3727:                     $category = 'Portfolio files belonging to ';
                   3728:                     if ($env{'user.name'} eq 'public' && 
                   3729:                         $env{'user.domain'} eq 'public') {
                   3730:                         $category .= &plainname($uname,$udom);
                   3731:                     } else {
                   3732:                         $category .= &aboutmewrapper(&plainname($uname,$udom),$uname,$udom);  
                   3733:                     }
                   3734:                 }
                   3735:             } elsif ($activity eq 'groups') {
                   3736:                 $category = 'Groups in this course';
                   3737:             }
                   3738:             my $showstart = &Apache::lonlocal::locallocaltime($startblock);
                   3739:             my $showend = &Apache::lonlocal::locallocaltime($endblock);
                   3740:             $output = '<br />'.&mt('[_1] will be inaccessible between [_2] and [_3] because communication is being blocked.',$category,$showstart,$showend).'<br />';
                   3741:             if (!($activity eq 'port' && !($ownitem) && !($is_course))) { 
                   3742:                 $output .= &build_block_table($startblock,$endblock,\%setters);
                   3743:             }
                   3744:         }
                   3745:     }
                   3746:     if (wantarray) {
                   3747:         return ($blocked,$output);
                   3748:     } else {
                   3749:         return $blocked;
                   3750:     }
                   3751: }
                   3752: 
1.60      matthew  3753: ###############################################
                   3754: 
                   3755: =pod
                   3756: 
1.112     bowersj2 3757: =head1 Domain Template Functions
                   3758: 
                   3759: =over 4
                   3760: 
                   3761: =item * &determinedomain()
1.60      matthew  3762: 
                   3763: Inputs: $domain (usually will be undef)
                   3764: 
1.63      www      3765: Returns: Determines which domain should be used for designs
1.60      matthew  3766: 
                   3767: =cut
1.54      www      3768: 
1.60      matthew  3769: ###############################################
1.63      www      3770: sub determinedomain {
                   3771:     my $domain=shift;
1.531     albertel 3772:     if (! $domain) {
1.60      matthew  3773:         # Determine domain if we have not been given one
                   3774:         $domain = $Apache::lonnet::perlvar{'lonDefDomain'};
1.258     albertel 3775:         if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
                   3776:         if ($env{'request.role.domain'}) { 
                   3777:             $domain=$env{'request.role.domain'}; 
1.60      matthew  3778:         }
                   3779:     }
1.63      www      3780:     return $domain;
                   3781: }
                   3782: ###############################################
1.517     raeburn  3783: 
1.518     albertel 3784: sub devalidate_domconfig_cache {
                   3785:     my ($udom)=@_;
                   3786:     &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
                   3787: }
                   3788: 
                   3789: # ---------------------- Get domain configuration for a domain
                   3790: sub get_domainconf {
                   3791:     my ($udom) = @_;
                   3792:     my $cachetime=1800;
                   3793:     my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
                   3794:     if (defined($cached)) { return %{$result}; }
                   3795: 
                   3796:     my %domconfig = &Apache::lonnet::get_dom('configuration',
                   3797: 					     ['login','rolecolors'],$udom);
1.632     raeburn  3798:     my (%designhash,%legacy);
1.518     albertel 3799:     if (keys(%domconfig) > 0) {
                   3800:         if (ref($domconfig{'login'}) eq 'HASH') {
1.632     raeburn  3801:             if (keys(%{$domconfig{'login'}})) {
                   3802:                 foreach my $key (keys(%{$domconfig{'login'}})) {
                   3803:                     $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
                   3804:                 }
                   3805:             } else {
                   3806:                 $legacy{'login'} = 1;
1.518     albertel 3807:             }
1.632     raeburn  3808:         } else {
                   3809:             $legacy{'login'} = 1;
1.518     albertel 3810:         }
                   3811:         if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632     raeburn  3812:             if (keys(%{$domconfig{'rolecolors'}})) {
                   3813:                 foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
                   3814:                     if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
                   3815:                         foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
                   3816:                             $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
                   3817:                         }
1.518     albertel 3818:                     }
                   3819:                 }
1.632     raeburn  3820:             } else {
                   3821:                 $legacy{'rolecolors'} = 1;
1.518     albertel 3822:             }
1.632     raeburn  3823:         } else {
                   3824:             $legacy{'rolecolors'} = 1;
1.518     albertel 3825:         }
1.632     raeburn  3826:         if (keys(%legacy) > 0) {
                   3827:             my %legacyhash = &get_legacy_domconf($udom);
                   3828:             foreach my $item (keys(%legacyhash)) {
                   3829:                 if ($item =~ /^\Q$udom\E\.login/) {
                   3830:                     if ($legacy{'login'}) { 
                   3831:                         $designhash{$item} = $legacyhash{$item};
                   3832:                     }
                   3833:                 } else {
                   3834:                     if ($legacy{'rolecolors'}) {
                   3835:                         $designhash{$item} = $legacyhash{$item};
                   3836:                     }
1.518     albertel 3837:                 }
                   3838:             }
                   3839:         }
1.632     raeburn  3840:     } else {
                   3841:         %designhash = &get_legacy_domconf($udom); 
1.518     albertel 3842:     }
                   3843:     &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
                   3844: 				  $cachetime);
                   3845:     return %designhash;
                   3846: }
                   3847: 
1.632     raeburn  3848: sub get_legacy_domconf {
                   3849:     my ($udom) = @_;
                   3850:     my %legacyhash;
                   3851:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
                   3852:     my $designfile =  $designdir.'/'.$udom.'.tab';
                   3853:     if (-e $designfile) {
                   3854:         if ( open (my $fh,"<$designfile") ) {
                   3855:             while (my $line = <$fh>) {
                   3856:                 next if ($line =~ /^\#/);
                   3857:                 chomp($line);
                   3858:                 my ($key,$val)=(split(/\=/,$line));
                   3859:                 if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
                   3860:             }
                   3861:             close($fh);
                   3862:         }
                   3863:     }
                   3864:     if (-e '/home/httpd/html/adm/lonDomLogos/'.$udom.'.gif') {
                   3865:         $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
                   3866:     }
                   3867:     return %legacyhash;
                   3868: }
                   3869: 
1.63      www      3870: =pod
                   3871: 
1.112     bowersj2 3872: =item * &domainlogo()
1.63      www      3873: 
                   3874: Inputs: $domain (usually will be undef)
                   3875: 
                   3876: Returns: A link to a domain logo, if the domain logo exists.
                   3877: If the domain logo does not exist, a description of the domain.
                   3878: 
                   3879: =cut
1.112     bowersj2 3880: 
1.63      www      3881: ###############################################
                   3882: sub domainlogo {
1.517     raeburn  3883:     my $domain = &determinedomain(shift);
1.518     albertel 3884:     my %designhash = &get_domainconf($domain);    
1.517     raeburn  3885:     # See if there is a logo
                   3886:     if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519     raeburn  3887:         my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538     albertel 3888:         if ($imgsrc =~ m{^/(adm|res)/}) {
                   3889: 	    if ($imgsrc =~ m{^/res/}) {
                   3890: 		my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
                   3891: 		&Apache::lonnet::repcopy($local_name);
                   3892: 	    }
                   3893: 	   $imgsrc = &lonhttpdurl($imgsrc);
1.519     raeburn  3894:         } 
                   3895:         return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514     albertel 3896:     } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
                   3897:         return &Apache::lonnet::domain($domain,'description');
1.59      www      3898:     } else {
1.60      matthew  3899:         return '';
1.59      www      3900:     }
                   3901: }
1.63      www      3902: ##############################################
                   3903: 
                   3904: =pod
                   3905: 
1.112     bowersj2 3906: =item * &designparm()
1.63      www      3907: 
                   3908: Inputs: $which parameter; $domain (usually will be undef)
                   3909: 
                   3910: Returns: value of designparamter $which
                   3911: 
                   3912: =cut
1.112     bowersj2 3913: 
1.397     albertel 3914: 
1.400     albertel 3915: ##############################################
1.397     albertel 3916: sub designparm {
                   3917:     my ($which,$domain)=@_;
1.258     albertel 3918:     if ($env{'browser.blackwhite'} eq 'on') {
1.635     raeburn  3919: 	if ($which=~/\.(font|alink|vlink|link|textcol)$/) {
1.110     www      3920: 	    return '#000000';
                   3921: 	}
1.635     raeburn  3922: 	if ($which=~/\.(pgbg|sidebg|bgcol)$/) {
1.110     www      3923: 	    return '#FFFFFF';
                   3924: 	}
                   3925: 	if ($which=~/\.tabbg$/) {
                   3926: 	    return '#CCCCCC';
                   3927: 	}
                   3928:     }
1.397     albertel 3929:     if (exists($env{'environment.color.'.$which})) {
1.258     albertel 3930: 	return $env{'environment.color.'.$which};
1.96      www      3931:     }
1.63      www      3932:     $domain=&determinedomain($domain);
1.518     albertel 3933:     my %domdesign = &get_domainconf($domain);
1.520     raeburn  3934:     my $output;
1.517     raeburn  3935:     if ($domdesign{$domain.'.'.$which} ne '') {
1.520     raeburn  3936: 	$output = $domdesign{$domain.'.'.$which};
1.63      www      3937:     } else {
1.520     raeburn  3938:         $output = $defaultdesign{$which};
                   3939:     }
                   3940:     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635     raeburn  3941:         ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538     albertel 3942:         if ($output =~ m{^/(adm|res)/}) {
                   3943: 	    if ($output =~ m{^/res/}) {
                   3944: 		my $local_name = &Apache::lonnet::filelocation('',$output);
                   3945: 		&Apache::lonnet::repcopy($local_name);
                   3946: 	    }
1.520     raeburn  3947:             $output = &lonhttpdurl($output);
                   3948:         }
1.63      www      3949:     }
1.520     raeburn  3950:     return $output;
1.63      www      3951: }
1.59      www      3952: 
1.60      matthew  3953: ###############################################
                   3954: ###############################################
                   3955: 
                   3956: =pod
                   3957: 
1.112     bowersj2 3958: =back
                   3959: 
1.549     albertel 3960: =head1 HTML Helpers
1.112     bowersj2 3961: 
                   3962: =over 4
                   3963: 
                   3964: =item * &bodytag()
1.60      matthew  3965: 
                   3966: Returns a uniform header for LON-CAPA web pages.
                   3967: 
                   3968: Inputs: 
                   3969: 
1.112     bowersj2 3970: =over 4
                   3971: 
                   3972: =item * $title, A title to be displayed on the page.
                   3973: 
                   3974: =item * $function, the current role (can be undef).
                   3975: 
                   3976: =item * $addentries, extra parameters for the <body> tag.
                   3977: 
                   3978: =item * $bodyonly, if defined, only return the <body> tag.
                   3979: 
                   3980: =item * $domain, if defined, force a given domain.
                   3981: 
                   3982: =item * $forcereg, if page should register as content page (relevant for 
1.86      www      3983:             text interface only)
1.60      matthew  3984: 
1.326     albertel 3985: =item * $customtitle, alternate text to use instead of $title
                   3986:                       in the title box that appears, this text
                   3987:                       is not auto translated like the $title is
1.309     albertel 3988: 
                   3989: =item * $notopbar, if true, keep the 'what is this' info but remove the
                   3990:                    navigational links
1.317     albertel 3991: 
1.338     albertel 3992: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
                   3993: 
                   3994: =item * $notitle, if true keep the nav controls, but remove the title bar
                   3995: 
1.361     albertel 3996: =item * $no_inline_link, if true and in remote mode, don't show the 
                   3997:          'Switch To Inline Menu' link
                   3998: 
1.460     albertel 3999: =item * $args, optional argument valid values are
                   4000:             no_auto_mt_title -> prevents &mt()ing the title arg
1.562     albertel 4001:             inherit_jsmath -> when creating popup window in a page,
                   4002:                               should it have jsmath forced on by the
                   4003:                               current page
1.460     albertel 4004: 
1.112     bowersj2 4005: =back
                   4006: 
1.60      matthew  4007: Returns: A uniform header for LON-CAPA web pages.  
                   4008: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
                   4009: If $bodyonly is undef or zero, an html string containing a <body> tag and 
                   4010: other decorations will be returned.
                   4011: 
                   4012: =cut
                   4013: 
1.54      www      4014: sub bodytag {
1.309     albertel 4015:     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,$customtitle,
1.460     albertel 4016: 	$notopbar,$bgcolor,$notitle,$no_inline_link,$args)=@_;
1.339     albertel 4017: 
1.460     albertel 4018:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.339     albertel 4019: 
1.183     matthew  4020:     $function = &get_users_function() if (!$function);
1.339     albertel 4021:     my $img =    &designparm($function.'.img',$domain);
                   4022:     my $font =   &designparm($function.'.font',$domain);
                   4023:     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
                   4024: 
                   4025:     my %design = ( 'style'   => 'margin-top: 0px',
1.535     albertel 4026: 		   'bgcolor' => $pgbg,
1.339     albertel 4027: 		   'text'    => $font,
                   4028:                    'alink'   => &designparm($function.'.alink',$domain),
                   4029: 		   'vlink'   => &designparm($function.'.vlink',$domain),
                   4030: 		   'link'    => &designparm($function.'.link',$domain),);
1.438     albertel 4031:     @design{keys(%$addentries)} = @$addentries{keys(%$addentries)}; 
1.339     albertel 4032: 
1.63      www      4033:  # role and realm
1.378     raeburn  4034:     my ($role,$realm) = split(/\./,$env{'request.role'},2);
                   4035:     if ($role  eq 'ca') {
1.479     albertel 4036:         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500     albertel 4037:         $realm = &plainname($rname,$rdom);
1.378     raeburn  4038:     } 
1.55      www      4039: # realm
1.258     albertel 4040:     if ($env{'request.course.id'}) {
1.378     raeburn  4041:         if ($env{'request.role'} !~ /^cr/) {
                   4042:             $role = &Apache::lonnet::plaintext($role,&course_type());
                   4043:         }
1.359     albertel 4044: 	$realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378     raeburn  4045:     } else {
                   4046:         $role = &Apache::lonnet::plaintext($role);
1.54      www      4047:     }
1.433     albertel 4048: 
1.359     albertel 4049:     if (!$realm) { $realm='&nbsp;'; }
1.55      www      4050: # Set messages
1.60      matthew  4051:     my $messages=&domainlogo($domain);
1.330     albertel 4052: 
1.438     albertel 4053:     my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329     albertel 4054: 
1.101     www      4055: # construct main body tag
1.359     albertel 4056:     my $bodytag = "<body $extra_body_attr>".
1.562     albertel 4057: 	&Apache::lontexconvert::init_math_support($args->{'inherit_jsmath'});
1.252     albertel 4058: 
1.530     albertel 4059:     if ($bodyonly) {
1.60      matthew  4060:         return $bodytag;
1.258     albertel 4061:     } elsif ($env{'browser.interface'} eq 'textual') {
1.95      www      4062: # Accessibility
1.224     raeburn  4063:           
1.337     albertel 4064: 	$bodytag.=&Apache::lonmenu::menubuttons($forcereg,$forcereg);
1.338     albertel 4065: 	if (!$notitle) {
1.337     albertel 4066: 	    $bodytag.='<h1>LON-CAPA: '.$title.'</h1>';
                   4067: 	}
                   4068: 	return $bodytag;
1.359     albertel 4069:     }
                   4070: 
1.410     albertel 4071:     my $name = &plainname($env{'user.name'},$env{'user.domain'});
1.433     albertel 4072:     if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
                   4073: 	undef($role);
1.434     albertel 4074:     } else {
                   4075: 	$name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'});
1.433     albertel 4076:     }
1.359     albertel 4077:     
                   4078:     my $roleinfo=(<<ENDROLE);
                   4079: <td class="LC_title_bar_who">
                   4080: <div class="LC_title_bar_name">
1.410     albertel 4081:     $name
1.361     albertel 4082:     &nbsp;
1.359     albertel 4083: </div>
                   4084: <div class="LC_title_bar_role">
1.361     albertel 4085: $role&nbsp;
1.359     albertel 4086: </div>
                   4087: <div class="LC_title_bar_realm">
1.361     albertel 4088: $realm&nbsp;
1.359     albertel 4089: </div>
1.206     albertel 4090: </td>
                   4091: ENDROLE
1.235     raeburn  4092: 
1.359     albertel 4093:     my $titleinfo = '<span class="LC_title_bar_title">'.$title.'</span>';
                   4094:     if ($customtitle) {
                   4095:         $titleinfo = $customtitle;
                   4096:     }
                   4097:     #
                   4098:     # Extra info if you are the DC
                   4099:     my $dc_info = '';
                   4100:     if ($env{'user.adv'} && exists($env{'user.role.dc./'.
                   4101:                         $env{'course.'.$env{'request.course.id'}.
                   4102:                                  '.domain'}.'/'})) {
                   4103:         my $cid = $env{'request.course.id'};
                   4104:         $dc_info.= $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380     www      4105:         $dc_info =~ s/\s+$//;
1.359     albertel 4106:         $dc_info = '('.$dc_info.')';
                   4107:     }
                   4108: 
1.644     www      4109:     if (($env{'environment.remote'} eq 'off') || ($args->{'suppress_header_logos'})) {
1.359     albertel 4110:         # No Remote
1.258     albertel 4111: 	if ($env{'request.state'} eq 'construct') {
1.359     albertel 4112: 	    $forcereg=1;
                   4113: 	}
                   4114: 
                   4115: 	if (!$customtitle && $env{'request.state'} eq 'construct') {
                   4116: 	    # this is for resources; directories have customtitle, and crumbs
                   4117:             # and select recent are created in lonpubdir.pm  
1.229     albertel 4118: 	    my ($uname,$thisdisfn)=
1.258     albertel 4119: 		($env{'request.filename'} =~ m|^/home/([^/]+)/public_html/(.*)|);
1.229     albertel 4120: 	    my $formaction='/priv/'.$uname.'/'.$thisdisfn;
                   4121: 	    $formaction=~s/\/+/\//g;
                   4122: 
1.359     albertel 4123: 	    my $parentpath = '';
                   4124: 	    my $lastitem = '';
                   4125: 	    if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
                   4126: 		$parentpath = $1;
                   4127: 		$lastitem = $2;
                   4128: 	    } else {
                   4129: 		$lastitem = $thisdisfn;
                   4130: 	    }
                   4131: 	    $titleinfo = 
1.640     bisitz   4132: 		&Apache::loncommon::help_open_menu('','',3,'Authoring')
                   4133: 		.'<b>'.&mt('Construction Space').'</b>:&nbsp;'
                   4134: 		.'<form name="dirs" method="post" action="'.$formaction
1.359     albertel 4135: 		.'" target="_top"><tt><b>'
                   4136: 		.&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."<font size=\"+1\">$lastitem</font></b></tt><br />"
                   4137: 		.&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
                   4138: 		.'</form>'
                   4139: 		.&Apache::lonmenu::constspaceform();
1.235     raeburn  4140:         }
1.359     albertel 4141: 
1.337     albertel 4142:         my $titletable;
1.338     albertel 4143: 	if (!$notitle) {
1.337     albertel 4144: 	    $titletable =
1.359     albertel 4145: 		'<table id="LC_title_bar">'.
                   4146:                          "<tr><td> $titleinfo $dc_info</td>".$roleinfo.
                   4147: 			 '</tr></table>';
1.337     albertel 4148: 	}
1.359     albertel 4149: 	if ($notopbar) {
                   4150: 	    $bodytag .= $titletable;
                   4151: 	} else {
                   4152: 	    if ($env{'request.state'} eq 'construct') {
1.337     albertel 4153:                 $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg,
                   4154: 							  $titletable);
1.272     raeburn  4155:             } else {
1.336     albertel 4156:                 $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg).
1.359     albertel 4157: 		    $titletable;
1.272     raeburn  4158:             }
1.235     raeburn  4159:         }
                   4160:         return $bodytag;
1.94      www      4161:     }
1.95      www      4162: 
1.93      www      4163: #
1.95      www      4164: # Top frame rendering, Remote is up
1.93      www      4165: #
1.359     albertel 4166: 
1.517     raeburn  4167:     my $imgsrc = $img;
                   4168:     if ($img =~ /^\/adm/) {
1.575     albertel 4169:         $imgsrc = &lonhttpdurl($img);
1.517     raeburn  4170:     }
                   4171:     my $upperleft='<img src="'.$imgsrc.'" alt="'.$function.'" />';
1.359     albertel 4172: 
1.305     www      4173:     # Explicit link to get inline menu
1.361     albertel 4174:     my $menu= ($no_inline_link?''
                   4175: 	       :'<br /><a href="/adm/remote?action=collapse">'.&mt('Switch to Inline Menu Mode').'</a>');
1.245     matthew  4176:     #
1.338     albertel 4177:     if ($notitle) {
1.337     albertel 4178: 	return $bodytag;
                   4179:     }
1.94      www      4180:     return(<<ENDBODY);
1.60      matthew  4181: $bodytag
1.359     albertel 4182: <table id="LC_title_bar" class="LC_with_remote">
1.368     albertel 4183: <tr><td class="LC_title_bar_role_logo">$upperleft</td>
1.359     albertel 4184:     <td class="LC_title_bar_domain_logo">$messages&nbsp;</td>
1.54      www      4185: </tr>
1.359     albertel 4186: <tr><td>$titleinfo $dc_info $menu</td>
                   4187: $roleinfo
1.368     albertel 4188: </tr>
1.356     albertel 4189: </table>
1.54      www      4190: ENDBODY
1.182     matthew  4191: }
                   4192: 
1.330     albertel 4193: sub make_attr_string {
                   4194:     my ($register,$attr_ref) = @_;
                   4195: 
                   4196:     if ($attr_ref && !ref($attr_ref)) {
                   4197: 	die("addentries Must be a hash ref ".
                   4198: 	    join(':',caller(1))." ".
                   4199: 	    join(':',caller(0))." ");
                   4200:     }
                   4201: 
                   4202:     if ($register) {
1.339     albertel 4203: 	my ($on_load,$on_unload);
                   4204: 	foreach my $key (keys(%{$attr_ref})) {
                   4205: 	    if      (lc($key) eq 'onload') {
                   4206: 		$on_load.=$attr_ref->{$key}.';';
                   4207: 		delete($attr_ref->{$key});
                   4208: 
                   4209: 	    } elsif (lc($key) eq 'onunload') {
                   4210: 		$on_unload.=$attr_ref->{$key}.';';
                   4211: 		delete($attr_ref->{$key});
                   4212: 	    }
                   4213: 	}
                   4214: 	$attr_ref->{'onload'}  =
                   4215: 	    &Apache::lonmenu::loadevents().  $on_load;
                   4216: 	$attr_ref->{'onunload'}=
                   4217: 	    &Apache::lonmenu::unloadevents().$on_unload;
                   4218:     }
                   4219: 
                   4220: # Accessibility font enhance
                   4221:     if ($env{'browser.fontenhance'} eq 'on') {
                   4222: 	my $style;
                   4223: 	foreach my $key (keys(%{$attr_ref})) {
                   4224: 	    if (lc($key) eq 'style') {
                   4225: 		$style.=$attr_ref->{$key}.';';
                   4226: 		delete($attr_ref->{$key});
                   4227: 	    }
                   4228: 	}
                   4229: 	$attr_ref->{'style'}=$style.'; font-size: x-large;';
1.330     albertel 4230:     }
1.339     albertel 4231: 
                   4232:     if ($env{'browser.blackwhite'} eq 'on') {
                   4233: 	delete($attr_ref->{'font'});
                   4234: 	delete($attr_ref->{'link'});
                   4235: 	delete($attr_ref->{'alink'});
                   4236: 	delete($attr_ref->{'vlink'});
                   4237: 	delete($attr_ref->{'bgcolor'});
                   4238: 	delete($attr_ref->{'background'});
                   4239:     }
                   4240: 
1.330     albertel 4241:     my $attr_string;
                   4242:     foreach my $attr (keys(%$attr_ref)) {
                   4243: 	$attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
                   4244:     }
                   4245:     return $attr_string;
                   4246: }
                   4247: 
                   4248: 
1.182     matthew  4249: ###############################################
1.251     albertel 4250: ###############################################
                   4251: 
                   4252: =pod
                   4253: 
                   4254: =item * &endbodytag()
                   4255: 
                   4256: Returns a uniform footer for LON-CAPA web pages.
                   4257: 
1.635     raeburn  4258: Inputs: 1 - optional reference to an args hash
                   4259: If in the hash, key for noredirectlink has a value which evaluates to true,
                   4260: a 'Continue' link is not displayed if the page contains an
                   4261: internal redirect in the <head></head> section,
                   4262: i.e., $env{'internal.head.redirect'} exists   
1.251     albertel 4263: 
                   4264: =cut
                   4265: 
                   4266: sub endbodytag {
1.635     raeburn  4267:     my ($args) = @_;
1.251     albertel 4268:     my $endbodytag='</body>';
1.269     albertel 4269:     $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
1.315     albertel 4270:     if ( exists( $env{'internal.head.redirect'} ) ) {
1.635     raeburn  4271:         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
                   4272: 	    $endbodytag=
                   4273: 	        "<br /><a href=\"$env{'internal.head.redirect'}\">".
                   4274: 	        &mt('Continue').'</a>'.
                   4275: 	        $endbodytag;
                   4276:         }
1.315     albertel 4277:     }
1.251     albertel 4278:     return $endbodytag;
                   4279: }
                   4280: 
1.352     albertel 4281: =pod
                   4282: 
                   4283: =item * &standard_css()
                   4284: 
                   4285: Returns a style sheet
                   4286: 
                   4287: Inputs: (all optional)
                   4288:             domain         -> force to color decorate a page for a specific
                   4289:                                domain
                   4290:             function       -> force usage of a specific rolish color scheme
                   4291:             bgcolor        -> override the default page bgcolor
                   4292: 
                   4293: =cut
                   4294: 
1.343     albertel 4295: sub standard_css {
1.345     albertel 4296:     my ($function,$domain,$bgcolor) = @_;
1.352     albertel 4297:     $function  = &get_users_function() if (!$function);
                   4298:     my $img    = &designparm($function.'.img',   $domain);
                   4299:     my $tabbg  = &designparm($function.'.tabbg', $domain);
                   4300:     my $font   = &designparm($function.'.font',  $domain);
1.345     albertel 4301:     my $sidebg = &designparm($function.'.sidebg',$domain);
1.382     albertel 4302:     my $pgbg_or_bgcolor =
                   4303: 	         $bgcolor ||
1.352     albertel 4304: 	         &designparm($function.'.pgbg',  $domain);
1.382     albertel 4305:     my $pgbg   = &designparm($function.'.pgbg',  $domain);
1.352     albertel 4306:     my $alink  = &designparm($function.'.alink', $domain);
                   4307:     my $vlink  = &designparm($function.'.vlink', $domain);
                   4308:     my $link   = &designparm($function.'.link',  $domain);
                   4309: 
1.602     albertel 4310:     my $sans                 = 'Verdana,Arial,Helvetica,sans-serif';
1.395     albertel 4311:     my $mono                 = 'monospace';
1.352     albertel 4312:     my $data_table_head      = $tabbg;
                   4313:     my $data_table_light     = '#EEEEEE';
1.470     banghart 4314:     my $data_table_dark      = '#DDDDDD';
                   4315:     my $data_table_darker    = '#CCCCCC';
1.349     albertel 4316:     my $data_table_highlight = '#FFFF00';
1.352     albertel 4317:     my $mail_new             = '#FFBB77';
                   4318:     my $mail_new_hover       = '#DD9955';
                   4319:     my $mail_read            = '#BBBB77';
                   4320:     my $mail_read_hover      = '#999944';
                   4321:     my $mail_replied         = '#AAAA88';
                   4322:     my $mail_replied_hover   = '#888855';
                   4323:     my $mail_other           = '#99BBBB';
                   4324:     my $mail_other_hover     = '#669999';
1.391     albertel 4325:     my $table_header         = '#DDDDDD';
1.489     raeburn  4326:     my $feedback_link_bg     = '#BBBBBB';
1.392     albertel 4327: 
1.608     albertel 4328:     my $border = ($env{'browser.type'} eq 'explorer' ||
                   4329: 		  $env{'browser.type'} eq 'safari'     ) ? '0px 2px 0px 2px'
                   4330: 	                                                 : '0px 3px 0px 4px';
1.448     albertel 4331: 
1.523     albertel 4332: 
1.343     albertel 4333:     return <<END;
1.345     albertel 4334: h1, h2, h3, th { font-family: $sans }
1.343     albertel 4335: a:focus { color: red; background: yellow } 
1.510     albertel 4336: table.thinborder,
1.523     albertel 4337: 
1.510     albertel 4338: table.thinborder tr th {
                   4339:   border-style: solid;
                   4340:   border-width: 1px;
                   4341:   background: $tabbg;
                   4342: }
1.523     albertel 4343: table.thinborder tr td {
1.510     albertel 4344:   border-style: solid;
                   4345:   border-width: 1px
                   4346: }
1.426     albertel 4347: 
1.343     albertel 4348: form, .inline { display: inline; }
                   4349: .center { text-align: center; }
1.593     albertel 4350: .LC_filename {font-family: $mono; white-space:pre;}
1.350     albertel 4351: .LC_error {
                   4352:   color: red;
                   4353:   font-size: larger;
                   4354: }
1.457     albertel 4355: .LC_warning,
                   4356: .LC_diff_removed {
1.394     albertel 4357:   color: red;
                   4358: }
1.532     albertel 4359: 
                   4360: .LC_info,
1.457     albertel 4361: .LC_success,
                   4362: .LC_diff_added {
1.350     albertel 4363:   color: green;
                   4364: }
1.543     albertel 4365: .LC_unknown {
                   4366:   color: yellow;
                   4367: }
                   4368: 
1.440     albertel 4369: .LC_icon {
                   4370:   border: 0px;
                   4371: }
1.539     albertel 4372: .LC_indexer_icon {
                   4373:   border: 0px;
                   4374:   height: 22px;
                   4375: }
1.543     albertel 4376: .LC_docs_spacer {
                   4377:   width: 25px;
                   4378:   height: 1px;
                   4379:   border: 0px;
                   4380: }
1.346     albertel 4381: 
1.532     albertel 4382: .LC_internal_info {
                   4383:   color: #999;
                   4384: }
                   4385: 
1.458     albertel 4386: table.LC_pastsubmission {
                   4387:   border: 1px solid black;
                   4388:   margin: 2px;
                   4389: }
                   4390: 
1.606     albertel 4391: table#LC_top_nav, table#LC_menubuttons,table#LC_nav_location {
1.345     albertel 4392:   width: 100%;
                   4393:   background: $pgbg;
1.392     albertel 4394:   border: 2px;
1.402     albertel 4395:   border-collapse: separate;
1.403     albertel 4396:   padding: 0px;
1.345     albertel 4397: }
1.392     albertel 4398: 
1.606     albertel 4399: table#LC_title_bar, table.LC_breadcrumbs, 
1.393     albertel 4400: table#LC_title_bar.LC_with_remote {
1.359     albertel 4401:   width: 100%;
1.392     albertel 4402:   border-color: $pgbg;
                   4403:   border-style: solid;
                   4404:   border-width: $border;
                   4405: 
1.379     albertel 4406:   background: $pgbg;
                   4407:   font-family: $sans;
1.392     albertel 4408:   border-collapse: collapse;
1.403     albertel 4409:   padding: 0px;
1.359     albertel 4410: }
1.392     albertel 4411: 
1.409     albertel 4412: table.LC_docs_path {
                   4413:   width: 100%;
                   4414:   border: 0;
                   4415:   background: $pgbg;
                   4416:   font-family: $sans;
                   4417:   border-collapse: collapse;
                   4418:   padding: 0px;
                   4419: }
                   4420: 
1.359     albertel 4421: table#LC_title_bar td {
                   4422:   background: $tabbg;
                   4423: }
                   4424: table#LC_title_bar td.LC_title_bar_who {
                   4425:   background: $tabbg;
                   4426:   color: $font;
1.427     albertel 4427:   font: small $sans;
1.359     albertel 4428:   text-align: right;
                   4429: }
1.469     banghart 4430: span.LC_metadata {
                   4431:     font-family: $sans;
                   4432: }
1.359     albertel 4433: span.LC_title_bar_title {
1.416     albertel 4434:   font: bold x-large $sans;
1.359     albertel 4435: }
                   4436: table#LC_title_bar td.LC_title_bar_domain_logo {
                   4437:   background: $sidebg;
                   4438:   text-align: right;
1.368     albertel 4439:   padding: 0px;
                   4440: }
                   4441: table#LC_title_bar td.LC_title_bar_role_logo {
                   4442:   background: $sidebg;
                   4443:   padding: 0px;
1.359     albertel 4444: }
                   4445: 
1.346     albertel 4446: table#LC_menubuttons_mainmenu {
1.526     www      4447:   width: 100%;
1.346     albertel 4448:   border: 0px;
                   4449:   border-spacing: 1px;
1.372     albertel 4450:   padding: 0px 1px;
1.346     albertel 4451:   margin: 0px;
                   4452:   border-collapse: separate;
                   4453: }
                   4454: table#LC_menubuttons img, table#LC_menubuttons_mainmenu img {
                   4455:   border: 0px;
                   4456: }
1.345     albertel 4457: table#LC_top_nav td {
                   4458:   background: $tabbg;
1.392     albertel 4459:   border: 0px;
1.407     albertel 4460:   font-size: small;
1.345     albertel 4461: }
                   4462: table#LC_top_nav td a, div#LC_top_nav a {
                   4463:   color: $font;
                   4464:   font-family: $sans;
                   4465: }
1.364     albertel 4466: table#LC_top_nav td.LC_top_nav_logo {
                   4467:   background: $tabbg;
1.432     albertel 4468:   text-align: left;
1.408     albertel 4469:   white-space: nowrap;
1.432     albertel 4470:   width: 31px;
1.408     albertel 4471: }
                   4472: table#LC_top_nav td.LC_top_nav_logo img {
1.432     albertel 4473:   border: 0px;
1.408     albertel 4474:   vertical-align: bottom;
1.364     albertel 4475: }
1.432     albertel 4476: table#LC_top_nav td.LC_top_nav_exit,
                   4477: table#LC_top_nav td.LC_top_nav_help {
                   4478:   width: 2.0em;
                   4479: }
1.442     albertel 4480: table#LC_top_nav td.LC_top_nav_login {
                   4481:   width: 4.0em;
                   4482:   text-align: center;
                   4483: }
1.409     albertel 4484: table.LC_breadcrumbs td, table.LC_docs_path td  {
1.357     albertel 4485:   background: $tabbg;
                   4486:   color: $font;
                   4487:   font-family: $sans;
1.358     albertel 4488:   font-size: smaller;
1.357     albertel 4489: }
1.411     albertel 4490: table.LC_breadcrumbs td.LC_breadcrumbs_component,
1.409     albertel 4491: table.LC_docs_path td.LC_docs_path_component {
1.357     albertel 4492:   background: $tabbg;
                   4493:   color: $font;
                   4494:   font-family: $sans;
                   4495:   font-size: larger;
                   4496:   text-align: right;
                   4497: }
1.383     albertel 4498: td.LC_table_cell_checkbox {
                   4499:   text-align: center;
                   4500: }
                   4501: 
1.522     albertel 4502: table#LC_mainmenu td.LC_mainmenu_column {
                   4503:     vertical-align: top;
                   4504: }
                   4505: 
1.346     albertel 4506: .LC_menubuttons_inline_text {
                   4507:   color: $font;
                   4508:   font-family: $sans;
                   4509:   font-size: smaller;
                   4510: }
                   4511: 
1.526     www      4512: .LC_menubuttons_link {
                   4513:   text-decoration: none;
                   4514: }
                   4515: 
1.522     albertel 4516: .LC_menubuttons_category {
1.521     www      4517:   color: $font;
1.526     www      4518:   background: $pgbg;
1.521     www      4519:   font-family: $sans;
                   4520:   font-size: larger;
                   4521:   font-weight: bold;
                   4522: }
                   4523: 
1.346     albertel 4524: td.LC_menubuttons_text {
1.526     www      4525:   width: 90%;
1.346     albertel 4526:   color: $font;
                   4527:   font-family: $sans;
                   4528: }
1.526     www      4529: 
1.346     albertel 4530: td.LC_menubuttons_img {
                   4531: }
1.526     www      4532: 
1.346     albertel 4533: .LC_current_location {
                   4534:   font-family: $sans;
                   4535:   background: $tabbg;
                   4536: }
                   4537: .LC_new_mail {
                   4538:   font-family: $sans;
1.634     www      4539:   background: $tabbg;
1.346     albertel 4540:   font-weight: bold;
                   4541: }
1.347     albertel 4542: 
1.526     www      4543: .LC_rolesmenu_is {
                   4544:   font-family: $sans;
                   4545: }
                   4546: 
                   4547: .LC_rolesmenu_selected {
                   4548:   font-family: $sans;
                   4549: }
                   4550: 
                   4551: .LC_rolesmenu_future {
                   4552:   font-family: $sans;
                   4553: }
                   4554: 
                   4555: 
                   4556: .LC_rolesmenu_will {
                   4557:   font-family: $sans;
                   4558: }
                   4559: 
                   4560: .LC_rolesmenu_will_not {
                   4561:   font-family: $sans;
                   4562: }
                   4563: 
                   4564: .LC_rolesmenu_expired {
                   4565:   font-family: $sans;
                   4566: }
                   4567: 
                   4568: .LC_rolesinfo {
                   4569:   font-family: $sans;
                   4570: }
                   4571: 
1.527     www      4572: .LC_dropadd_labeltext {
                   4573:   font-family: $sans;
                   4574:   text-align: right;
                   4575: }
                   4576: 
                   4577: .LC_preferences_labeltext {
                   4578:   font-family: $sans;
                   4579:   text-align: right;
                   4580: }
                   4581: 
1.440     albertel 4582: table.LC_aboutme_port {
                   4583:   border: 0px;
                   4584:   border-collapse: collapse;
                   4585:   border-spacing: 0px;
                   4586: }
1.349     albertel 4587: table.LC_data_table, table.LC_mail_list {
1.347     albertel 4588:   border: 1px solid #000000;
1.402     albertel 4589:   border-collapse: separate;
1.426     albertel 4590:   border-spacing: 1px;
1.610     albertel 4591:   background: $pgbg;
1.347     albertel 4592: }
1.422     albertel 4593: .LC_data_table_dense {
                   4594:   font-size: small;
                   4595: }
1.507     raeburn  4596: table.LC_nested_outer {
                   4597:   border: 1px solid #000000;
1.589     raeburn  4598:   border-collapse: collapse;
1.507     raeburn  4599:   border-spacing: 0px;
                   4600:   width: 100%;
                   4601: }
                   4602: table.LC_nested {
                   4603:   border: 0px;
1.589     raeburn  4604:   border-collapse: collapse;
1.507     raeburn  4605:   border-spacing: 0px;
                   4606:   width: 100%;
                   4607: }
1.523     albertel 4608: table.LC_data_table tr th, table.LC_calendar tr th, table.LC_mail_list tr th,
                   4609: table.LC_prior_tries tr th {
1.349     albertel 4610:   font-weight: bold;
                   4611:   background-color: $data_table_head;
1.421     albertel 4612:   font-size: smaller;
1.347     albertel 4613: }
1.610     albertel 4614: table.LC_data_table tr.LC_odd_row > td, 
1.440     albertel 4615: table.LC_aboutme_port tr td {
1.349     albertel 4616:   background-color: $data_table_light;
1.425     albertel 4617:   padding: 2px;
1.347     albertel 4618: }
1.610     albertel 4619: table.LC_data_table tr.LC_even_row > td,
1.440     albertel 4620: table.LC_aboutme_port tr.LC_even_row td {
1.349     albertel 4621:   background-color: $data_table_dark;
1.347     albertel 4622: }
1.425     albertel 4623: table.LC_data_table tr.LC_data_table_highlight td {
                   4624:   background-color: $data_table_darker;
                   4625: }
1.639     raeburn  4626: table.LC_data_table tr td.LC_leftcol_header {
                   4627:   background-color: $data_table_head;
                   4628:   font-weight: bold;
                   4629: }
1.451     albertel 4630: table.LC_data_table tr.LC_empty_row td,
1.507     raeburn  4631: table.LC_nested tr.LC_empty_row td {
1.347     albertel 4632:   background-color: #FFFFFF;
1.421     albertel 4633:   font-weight: bold;
                   4634:   font-style: italic;
                   4635:   text-align: center;
                   4636:   padding: 8px;
1.347     albertel 4637: }
1.507     raeburn  4638: table.LC_nested tr.LC_empty_row td {
1.465     albertel 4639:   padding: 4ex
                   4640: }
1.507     raeburn  4641: table.LC_nested_outer tr th {
                   4642:   font-weight: bold;
                   4643:   background-color: $data_table_head;
                   4644:   font-size: smaller;
                   4645:   border-bottom: 1px solid #000000;
                   4646: }
                   4647: table.LC_nested_outer tr td.LC_subheader {
                   4648:   background-color: $data_table_head;
                   4649:   font-weight: bold;
                   4650:   font-size: small;
                   4651:   border-bottom: 1px solid #000000;
                   4652:   text-align: right;
1.451     albertel 4653: }
1.507     raeburn  4654: table.LC_nested tr.LC_info_row td {
1.451     albertel 4655:   background-color: #CCC;
                   4656:   font-weight: bold;
                   4657:   font-size: small;
1.507     raeburn  4658:   text-align: center;
                   4659: }
1.589     raeburn  4660: table.LC_nested tr.LC_info_row td.LC_left_item,
                   4661: table.LC_nested_outer tr th.LC_left_item {
1.507     raeburn  4662:   text-align: left;
1.451     albertel 4663: }
1.507     raeburn  4664: table.LC_nested td {
1.451     albertel 4665:   background-color: #FFF;
                   4666:   font-size: small;
1.507     raeburn  4667: }
                   4668: table.LC_nested_outer tr th.LC_right_item,
                   4669: table.LC_nested tr.LC_info_row td.LC_right_item,
                   4670: table.LC_nested tr.LC_odd_row td.LC_right_item,
                   4671: table.LC_nested tr td.LC_right_item {
1.451     albertel 4672:   text-align: right;
                   4673: }
                   4674: 
1.507     raeburn  4675: table.LC_nested tr.LC_odd_row td {
1.451     albertel 4676:   background-color: #EEE;
                   4677: }
                   4678: 
1.473     raeburn  4679: table.LC_createuser {
                   4680: }
                   4681: 
                   4682: table.LC_createuser tr.LC_section_row td {
                   4683:   font-size: smaller;
                   4684: }
                   4685: 
                   4686: table.LC_createuser tr.LC_info_row td  {
                   4687:   background-color: #CCC;
                   4688:   font-weight: bold;
                   4689:   text-align: center;
                   4690: }
                   4691: 
1.349     albertel 4692: table.LC_calendar {
                   4693:   border: 1px solid #000000;
                   4694:   border-collapse: collapse;
                   4695: }
                   4696: table.LC_calendar_pickdate {
                   4697:   font-size: xx-small;
                   4698: }
                   4699: table.LC_calendar tr td {
                   4700:   border: 1px solid #000000;
                   4701:   vertical-align: top;
                   4702: }
                   4703: table.LC_calendar tr td.LC_calendar_day_empty {
                   4704:   background-color: $data_table_dark;
                   4705: }
                   4706: table.LC_calendar tr td.LC_calendar_day_current {
                   4707:   background-color: $data_table_highlight;
                   4708: }
                   4709: 
                   4710: table.LC_mail_list tr.LC_mail_new {
                   4711:   background-color: $mail_new;
                   4712: }
                   4713: table.LC_mail_list tr.LC_mail_new:hover {
                   4714:   background-color: $mail_new_hover;
                   4715: }
                   4716: table.LC_mail_list tr.LC_mail_read {
                   4717:   background-color: $mail_read;
                   4718: }
                   4719: table.LC_mail_list tr.LC_mail_read:hover {
                   4720:   background-color: $mail_read_hover;
                   4721: }
                   4722: table.LC_mail_list tr.LC_mail_replied {
                   4723:   background-color: $mail_replied;
                   4724: }
                   4725: table.LC_mail_list tr.LC_mail_replied:hover {
                   4726:   background-color: $mail_replied_hover;
                   4727: }
                   4728: table.LC_mail_list tr.LC_mail_other {
                   4729:   background-color: $mail_other;
                   4730: }
                   4731: table.LC_mail_list tr.LC_mail_other:hover {
                   4732:   background-color: $mail_other_hover;
                   4733: }
1.494     raeburn  4734: table.LC_mail_list tr.LC_mail_even {
                   4735: }
                   4736: table.LC_mail_list tr.LC_mail_odd {
                   4737: }
                   4738: 
1.385     albertel 4739: 
1.386     albertel 4740: table#LC_portfolio_actions {
                   4741:   width: auto;
                   4742:   background: $pgbg;
                   4743:   border: 0px;
                   4744:   border-spacing: 2px 2px;
                   4745:   padding: 0px;
                   4746:   margin: 0px;
                   4747:   border-collapse: separate;
                   4748: }
                   4749: table#LC_portfolio_actions td.LC_label {
                   4750:   background: $tabbg;
                   4751:   text-align: right;
                   4752: }
                   4753: table#LC_portfolio_actions td.LC_value {
                   4754:   background: $tabbg;
                   4755: }
1.385     albertel 4756: 
1.391     albertel 4757: table#LC_cstr_controls {
                   4758:   width: 100%;
                   4759:   border-collapse: collapse;
                   4760: }
                   4761: table#LC_cstr_controls tr td {
                   4762:   border: 4px solid $pgbg;
                   4763:   padding: 4px;
                   4764:   text-align: center;
                   4765:   background: $tabbg;
                   4766: }
                   4767: table#LC_cstr_controls tr th {
                   4768:   border: 4px solid $pgbg;
                   4769:   background: $table_header;
                   4770:   text-align: center;
                   4771:   font-family: $sans;
                   4772:   font-size: smaller;
                   4773: }
                   4774: 
1.389     albertel 4775: table#LC_browser {
                   4776:  
                   4777: }
                   4778: table#LC_browser tr th {
1.391     albertel 4779:   background: $table_header;
1.389     albertel 4780: }
1.390     albertel 4781: table#LC_browser tr td {
                   4782:   padding: 2px;
                   4783: }
1.389     albertel 4784: table#LC_browser tr.LC_browser_file,
                   4785: table#LC_browser tr.LC_browser_file_published {
                   4786:   background: #CCFF88;
                   4787: }
                   4788: table#LC_browser tr.LC_browser_file_locked,
                   4789: table#LC_browser tr.LC_browser_file_unpublished {
                   4790:   background: #FFAA99;
1.387     albertel 4791: }
1.389     albertel 4792: table#LC_browser tr.LC_browser_file_obsolete {
                   4793:   background: #AAAAAA;
1.387     albertel 4794: }
1.455     albertel 4795: table#LC_browser tr.LC_browser_file_modified,
                   4796: table#LC_browser tr.LC_browser_file_metamodified {
1.389     albertel 4797:   background: #FFFF77;
1.387     albertel 4798: }
1.389     albertel 4799: table#LC_browser tr.LC_browser_folder {
                   4800:   background: #CCCCFF;
1.387     albertel 4801: }
1.388     albertel 4802: span.LC_current_location {
                   4803:   font-size: x-large;
                   4804:   background: $pgbg;
                   4805: }
1.387     albertel 4806: 
1.395     albertel 4807: span.LC_parm_menu_item {
                   4808:   font-size: larger;
                   4809:   font-family: $sans;
                   4810: }
                   4811: span.LC_parm_scope_all {
                   4812:   color: red;
                   4813: }
                   4814: span.LC_parm_scope_folder {
                   4815:   color: green;
                   4816: }
                   4817: span.LC_parm_scope_resource {
                   4818:   color: orange;
                   4819: }
                   4820: span.LC_parm_part {
                   4821:   color: blue;
                   4822: }
                   4823: span.LC_parm_folder, span.LC_parm_symb {
                   4824:   font-size: x-small;
                   4825:   font-family: $mono;
                   4826:   color: #AAAAAA;
                   4827: }
                   4828: 
1.396     albertel 4829: td.LC_parm_overview_level_menu, td.LC_parm_overview_map_menu,
                   4830: td.LC_parm_overview_parm_selectors, td.LC_parm_overview_parm_restrictions {
                   4831:   border: 1px solid black;
                   4832:   border-collapse: collapse;
                   4833: }
                   4834: table.LC_parm_overview_restrictions td {
                   4835:   border-width: 1px 4px 1px 4px;
                   4836:   border-style: solid;
                   4837:   border-color: $pgbg;
                   4838:   text-align: center;
                   4839: }
                   4840: table.LC_parm_overview_restrictions th {
                   4841:   background: $tabbg;
                   4842:   border-width: 1px 4px 1px 4px;
                   4843:   border-style: solid;
                   4844:   border-color: $pgbg;
                   4845: }
1.398     albertel 4846: table#LC_helpmenu {
                   4847:   border: 0px;
                   4848:   height: 55px;
                   4849:   border-spacing: 0px;
                   4850: }
                   4851: 
                   4852: table#LC_helpmenu fieldset legend {
                   4853:   font-size: larger;
                   4854:   font-weight: bold;
                   4855: }
1.397     albertel 4856: table#LC_helpmenu_links {
                   4857:   width: 100%;
                   4858:   border: 1px solid black;
                   4859:   background: $pgbg;
                   4860:   padding: 0px;
                   4861:   border-spacing: 1px;
                   4862: }
                   4863: table#LC_helpmenu_links tr td {
                   4864:   padding: 1px;
                   4865:   background: $tabbg;
1.399     albertel 4866:   text-align: center;
                   4867:   font-weight: bold;
1.397     albertel 4868: }
1.396     albertel 4869: 
1.397     albertel 4870: table#LC_helpmenu_links a:link, table#LC_helpmenu_links a:visited,
                   4871: table#LC_helpmenu_links a:active {
                   4872:   text-decoration: none;
                   4873:   color: $font;
                   4874: }
                   4875: table#LC_helpmenu_links a:hover {
                   4876:   text-decoration: underline;
                   4877:   color: $vlink;
                   4878: }
1.396     albertel 4879: 
1.417     albertel 4880: .LC_chrt_popup_exists {
                   4881:   border: 1px solid #339933;
                   4882:   margin: -1px;
                   4883: }
                   4884: .LC_chrt_popup_up {
                   4885:   border: 1px solid yellow;
                   4886:   margin: -1px;
                   4887: }
                   4888: .LC_chrt_popup {
                   4889:   border: 1px solid #8888FF;
                   4890:   background: #CCCCFF;
                   4891: }
1.421     albertel 4892: table.LC_pick_box {
                   4893:   border-collapse: separate;
                   4894:   background: white;
                   4895:   border: 1px solid black;
                   4896:   border-spacing: 1px;
                   4897: }
                   4898: table.LC_pick_box td.LC_pick_box_title {
                   4899:   background: $tabbg;
                   4900:   font-weight: bold;
                   4901:   text-align: right;
                   4902:   width: 184px;
                   4903:   padding: 8px;
                   4904: }
1.645     raeburn  4905: table.LC_pick_box td.LC_selfenroll_pick_box_title {
                   4906:   background: $tabbg;
                   4907:   font-weight: bold;
                   4908:   text-align: right;
                   4909:   width: 350px;
                   4910:   padding: 8px;
                   4911: }
                   4912: 
1.579     raeburn  4913: table.LC_pick_box td.LC_pick_box_value {
                   4914:   text-align: left;
                   4915:   padding: 8px;
                   4916: }
                   4917: table.LC_pick_box td.LC_pick_box_select {
                   4918:   text-align: left;
                   4919:   padding: 8px;
                   4920: }
1.424     albertel 4921: table.LC_pick_box td.LC_pick_box_separator {
1.421     albertel 4922:   padding: 0px;
                   4923:   height: 1px;
                   4924:   background: black;
                   4925: }
                   4926: table.LC_pick_box td.LC_pick_box_submit {
                   4927:   text-align: right;
                   4928: }
1.579     raeburn  4929: table.LC_pick_box td.LC_evenrow_value {
                   4930:   text-align: left;
                   4931:   padding: 8px;
                   4932:   background-color: $data_table_light;
                   4933: }
                   4934: table.LC_pick_box td.LC_oddrow_value {
                   4935:   text-align: left;
                   4936:   padding: 8px;
                   4937:   background-color: $data_table_light;
                   4938: }
                   4939: table.LC_helpform_receipt {
                   4940:   width: 620px;
                   4941:   border-collapse: separate;
                   4942:   background: white;
                   4943:   border: 1px solid black;
                   4944:   border-spacing: 1px;
                   4945: }
                   4946: table.LC_helpform_receipt td.LC_pick_box_title {
                   4947:   background: $tabbg;
                   4948:   font-weight: bold;
                   4949:   text-align: right;
                   4950:   width: 184px;
                   4951:   padding: 8px;
                   4952: }
                   4953: table.LC_helpform_receipt td.LC_evenrow_value {
                   4954:   text-align: left;
                   4955:   padding: 8px;
                   4956:   background-color: $data_table_light;
                   4957: }
                   4958: table.LC_helpform_receipt td.LC_oddrow_value {
                   4959:   text-align: left;
                   4960:   padding: 8px;
                   4961:   background-color: $data_table_light;
                   4962: }
                   4963: table.LC_helpform_receipt td.LC_pick_box_separator {
                   4964:   padding: 0px;
                   4965:   height: 1px;
                   4966:   background: black;
                   4967: }
                   4968: span.LC_helpform_receipt_cat {
                   4969:   font-weight: bold;
                   4970: }
1.424     albertel 4971: table.LC_group_priv_box {
                   4972:   background: white;
                   4973:   border: 1px solid black;
                   4974:   border-spacing: 1px;
                   4975: }
                   4976: table.LC_group_priv_box td.LC_pick_box_title {
                   4977:   background: $tabbg;
                   4978:   font-weight: bold;
                   4979:   text-align: right;
                   4980:   width: 184px;
                   4981: }
                   4982: table.LC_group_priv_box td.LC_groups_fixed {
                   4983:   background: $data_table_light;
                   4984:   text-align: center;
                   4985: }
                   4986: table.LC_group_priv_box td.LC_groups_optional {
                   4987:   background: $data_table_dark;
                   4988:   text-align: center;
                   4989: }
                   4990: table.LC_group_priv_box td.LC_groups_functionality {
                   4991:   background: $data_table_darker;
                   4992:   text-align: center;
                   4993:   font-weight: bold;
                   4994: }
                   4995: table.LC_group_priv td {
                   4996:   text-align: left;
                   4997:   padding: 0px;
                   4998: }
                   4999: 
1.421     albertel 5000: table.LC_notify_front_page {
                   5001:   background: white;
                   5002:   border: 1px solid black;
                   5003:   padding: 8px;
                   5004: }
                   5005: table.LC_notify_front_page td {
                   5006:   padding: 8px;
                   5007: }
1.424     albertel 5008: .LC_navbuttons {
                   5009:   margin: 2ex 0ex 2ex 0ex;
                   5010: }
1.423     albertel 5011: .LC_topic_bar {
                   5012:   font-family: $sans;
                   5013:   font-weight: bold;
                   5014:   width: 100%;
                   5015:   background: $tabbg;
                   5016:   vertical-align: middle;
                   5017:   margin: 2ex 0ex 2ex 0ex;
                   5018: }
                   5019: .LC_topic_bar span {
                   5020:   vertical-align: middle;
                   5021: }
                   5022: .LC_topic_bar img {
                   5023:   vertical-align: bottom;
                   5024: }
                   5025: table.LC_course_group_status {
                   5026:   margin: 20px;
                   5027: }
                   5028: table.LC_status_selector td {
                   5029:   vertical-align: top;
                   5030:   text-align: center;
1.424     albertel 5031:   padding: 4px;
                   5032: }
                   5033: table.LC_descriptive_input td.LC_description {
                   5034:   vertical-align: top;
                   5035:   text-align: right;
                   5036:   font-weight: bold;
1.423     albertel 5037: }
1.599     albertel 5038: div.LC_feedback_link {
1.616     albertel 5039:   clear: both;
1.599     albertel 5040:   background: white;
                   5041:   width: 100%;  
1.489     raeburn  5042: }
                   5043: span.LC_feedback_link {
1.599     albertel 5044:   background: $feedback_link_bg;
                   5045:   font-size: larger;
                   5046: }
                   5047: span.LC_message_link {
                   5048:   background: $feedback_link_bg;
                   5049:   font-size: larger;
                   5050:   position: absolute;
                   5051:   right: 1em;
1.489     raeburn  5052: }
1.421     albertel 5053: 
1.515     albertel 5054: table.LC_prior_tries {
1.524     albertel 5055:   border: 1px solid #000000;
                   5056:   border-collapse: separate;
                   5057:   border-spacing: 1px;
1.515     albertel 5058: }
1.523     albertel 5059: 
1.515     albertel 5060: table.LC_prior_tries td {
1.524     albertel 5061:   padding: 2px;
1.515     albertel 5062: }
1.523     albertel 5063: 
                   5064: .LC_answer_correct {
                   5065:   background: #AAFFAA;
                   5066:   color: black;
                   5067: }
                   5068: .LC_answer_charged_try {
                   5069:   background: #FFAAAA ! important;
                   5070:   color: black;
                   5071: }
                   5072: .LC_answer_not_charged_try, 
                   5073: .LC_answer_no_grade,
                   5074: .LC_answer_late {
                   5075:   background: #FFFFAA;
                   5076:   color: black;
                   5077: }
                   5078: .LC_answer_previous {
                   5079:   background: #AAAAFF;
                   5080:   color: black;
                   5081: }
                   5082: .LC_answer_no_message {
                   5083:   background: #FFFFFF;
                   5084:   color: black;
                   5085: }
                   5086: .LC_answer_unknown {
                   5087:   background: orange;
                   5088:   color: black;
                   5089: }
                   5090: 
                   5091: 
1.529     albertel 5092: span.LC_prior_numerical,
                   5093: span.LC_prior_string,
                   5094: span.LC_prior_custom,
                   5095: span.LC_prior_reaction,
                   5096: span.LC_prior_math {
1.523     albertel 5097:   font-family: monospace;
                   5098:   white-space: pre;
                   5099: }
                   5100: 
1.525     albertel 5101: span.LC_prior_string {
                   5102:   font-family: monospace;
                   5103:   white-space: pre;
                   5104: }
                   5105: 
1.523     albertel 5106: table.LC_prior_option {
                   5107:   width: 100%;
                   5108:   border-collapse: collapse;
                   5109: }
1.528     albertel 5110: table.LC_prior_rank, table.LC_prior_match {
                   5111:   border-collapse: collapse;
                   5112: }
                   5113: table.LC_prior_option tr td,
                   5114: table.LC_prior_rank tr td,
                   5115: table.LC_prior_match tr td {
1.524     albertel 5116:   border: 1px solid #000000;
1.515     albertel 5117: }
                   5118: 
1.519     raeburn  5119: span.LC_nobreak {
1.544     albertel 5120:   white-space: nowrap;
1.519     raeburn  5121: }
                   5122: 
1.576     raeburn  5123: span.LC_cusr_emph {
                   5124:   font-style: italic;
                   5125: }
                   5126: 
1.633     raeburn  5127: span.LC_cusr_subheading {
                   5128:   font-weight: normal;
                   5129:   font-size: 85%;
                   5130: }
                   5131: 
1.545     albertel 5132: table.LC_docs_documents {
                   5133:   background: #BBBBBB;
1.547     albertel 5134:   border-width: 0px;
1.545     albertel 5135:   border-collapse: collapse;
                   5136: }
                   5137: 
                   5138: table.LC_docs_documents td.LC_docs_document {
                   5139:   border: 2px solid black;
                   5140:   padding: 4px;
                   5141: }
                   5142: 
                   5143: .LC_docs_course_commands div {
                   5144:   float: left;
                   5145:   border: 4px solid #AAAAAA;
                   5146:   padding: 4px;
                   5147:   background: #DDDDCC;
                   5148: }
                   5149: 
                   5150: .LC_docs_entry_move {
                   5151:   border: 0px;
                   5152:   border-collapse: collapse;
1.544     albertel 5153: }
                   5154: 
1.545     albertel 5155: .LC_docs_entry_move td {
                   5156:   border: 2px solid #BBBBBB;
                   5157:   background: #DDDDDD;
                   5158: }
                   5159: 
                   5160: .LC_docs_editor td.LC_docs_entry_commands {
                   5161:   background: #DDDDDD;
                   5162:   font-size: x-small;
                   5163: }
1.544     albertel 5164: .LC_docs_copy {
1.545     albertel 5165:   color: #000099;
1.544     albertel 5166: }
                   5167: .LC_docs_cut {
1.545     albertel 5168:   color: #550044;
1.544     albertel 5169: }
                   5170: .LC_docs_rename {
1.545     albertel 5171:   color: #009900;
1.544     albertel 5172: }
                   5173: .LC_docs_remove {
1.545     albertel 5174:   color: #990000;
                   5175: }
                   5176: 
1.547     albertel 5177: .LC_docs_reinit_warn,
                   5178: .LC_docs_ext_edit {
                   5179:   font-size: x-small;
                   5180: }
                   5181: 
1.545     albertel 5182: .LC_docs_editor td.LC_docs_entry_title,
                   5183: .LC_docs_editor td.LC_docs_entry_icon {
                   5184:   background: #FFFFBB;
                   5185: }
                   5186: .LC_docs_editor td.LC_docs_entry_parameter {
                   5187:   background: #BBBBFF;
                   5188:   font-size: x-small;
                   5189:   white-space: nowrap;
                   5190: }
                   5191: 
                   5192: table.LC_docs_adddocs td,
                   5193: table.LC_docs_adddocs th {
                   5194:   border: 1px solid #BBBBBB;
                   5195:   padding: 4px;
                   5196:   background: #DDDDDD;
1.543     albertel 5197: }
                   5198: 
1.584     albertel 5199: table.LC_sty_begin {
                   5200:   background: #BBFFBB;
                   5201: }
                   5202: table.LC_sty_end {
                   5203:   background: #FFBBBB;
                   5204: }
                   5205: 
1.589     raeburn  5206: table.LC_double_column {
                   5207:   border-width: 0px;
                   5208:   border-collapse: collapse;
                   5209:   width: 100%;
                   5210:   padding: 2px;
                   5211: }
                   5212: 
                   5213: table.LC_double_column tr td.LC_left_col {
1.590     raeburn  5214:   top: 2px;
1.589     raeburn  5215:   left: 2px;
                   5216:   width: 47%;
                   5217:   vertical-align: top;
                   5218: }
                   5219: 
                   5220: table.LC_double_column tr td.LC_right_col {
                   5221:   top: 2px;
                   5222:   right: 2px; 
                   5223:   width: 47%;
                   5224:   vertical-align: top;
                   5225: }
                   5226: 
1.594     raeburn  5227: span.LC_role_level {
                   5228:   font-weight: bold;
                   5229: }
                   5230: 
1.591     raeburn  5231: div.LC_left_float {
                   5232:   float: left;
                   5233:   padding-right: 5%;
1.597     albertel 5234:   padding-bottom: 4px;
1.591     raeburn  5235: }
                   5236: 
                   5237: div.LC_clear_float_header {
1.597     albertel 5238:   padding-bottom: 2px;
1.591     raeburn  5239: }
                   5240: 
                   5241: div.LC_clear_float_footer {
1.597     albertel 5242:   padding-top: 10px;
1.591     raeburn  5243:   clear: both;
                   5244: }
                   5245: 
1.597     albertel 5246: 
1.601     albertel 5247: div.LC_grade_select_mode {
1.604     albertel 5248:   font-family: $sans;
1.601     albertel 5249: }
                   5250: div.LC_grade_select_mode div div {
                   5251:   margin: 5px;
                   5252: }
                   5253: div.LC_grade_select_mode_selector {
                   5254:   margin: 5px;
                   5255:   float: left;
                   5256: }
                   5257: div.LC_grade_select_mode_selector_header {
                   5258:   font: bold medium $sans;
                   5259: }
                   5260: div.LC_grade_select_mode_type {
                   5261:   clear: left;
                   5262: }
                   5263: 
1.597     albertel 5264: div.LC_grade_show_user {
                   5265:   margin-top: 20px;
                   5266:   border: 1px solid black;
                   5267: }
                   5268: div.LC_grade_user_name {
                   5269:   background: #DDDDEE;
                   5270:   border-bottom: 1px solid black;
                   5271:   font: bold large $sans;
                   5272: }
                   5273: div.LC_grade_show_user_odd_row div.LC_grade_user_name {
                   5274:   background: #DDEEDD;
                   5275: }
                   5276: 
                   5277: div.LC_grade_show_problem,
                   5278: div.LC_grade_submissions,
                   5279: div.LC_grade_message_center,
                   5280: div.LC_grade_info_links,
                   5281: div.LC_grade_assign {
                   5282:   margin: 5px;
                   5283:   width: 99%;
                   5284:   background: #FFFFFF;
                   5285: }
                   5286: div.LC_grade_show_problem_header,
                   5287: div.LC_grade_submissions_header,
                   5288: div.LC_grade_message_center_header,
                   5289: div.LC_grade_assign_header {
                   5290:   font: bold large $sans;
                   5291: }
                   5292: div.LC_grade_show_problem_problem,
                   5293: div.LC_grade_submissions_body,
                   5294: div.LC_grade_message_center_body,
                   5295: div.LC_grade_assign_body {
                   5296:   border: 1px solid black;
                   5297:   width: 99%;
                   5298:   background: #FFFFFF;
                   5299: }
1.598     albertel 5300: span.LC_grade_check_note {
                   5301:   font: normal medium $sans;
                   5302:   display: inline;
                   5303:   position: absolute;
                   5304:   right: 1em;
                   5305: }
1.597     albertel 5306: 
1.613     albertel 5307: table.LC_scantron_action {
                   5308:   width: 100%;
                   5309: }
                   5310: table.LC_scantron_action tr th {
                   5311:   font: normal bold $sans;
                   5312: }
1.600     albertel 5313: 
1.614     albertel 5314: div.LC_edit_problem_header, 
                   5315: div.LC_edit_problem_footer {
1.600     albertel 5316:   font: normal medium $sans;
1.602     albertel 5317:   margin: 2px;
1.600     albertel 5318: }
                   5319: div.LC_edit_problem_header,
1.602     albertel 5320: div.LC_edit_problem_header div,
1.614     albertel 5321: div.LC_edit_problem_footer,
                   5322: div.LC_edit_problem_footer div,
1.602     albertel 5323: div.LC_edit_problem_editxml_header,
                   5324: div.LC_edit_problem_editxml_header div {
1.600     albertel 5325:   margin-top: 5px;
                   5326: }
1.602     albertel 5327: div.LC_edit_problem_header_edit_row {
                   5328:   background: $tabbg;
                   5329:   padding: 3px;
                   5330:   margin-bottom: 5px;
                   5331: }
1.600     albertel 5332: div.LC_edit_problem_header_title {
1.602     albertel 5333:   font: larger bold $sans;
                   5334:   background: $tabbg;
                   5335:   padding: 3px;
                   5336: }
                   5337: table.LC_edit_problem_header_title {
                   5338:   font: larger bold $sans;
                   5339:   width: 100%;
                   5340:   border-color: $pgbg;
                   5341:   border-style: solid;
                   5342:   border-width: $border;
                   5343: 
1.600     albertel 5344:   background: $tabbg;
1.602     albertel 5345:   border-collapse: collapse;
                   5346:   padding: 0px
                   5347: }
                   5348: 
                   5349: div.LC_edit_problem_discards {
                   5350:   float: left;
                   5351:   padding-bottom: 5px;
                   5352: }
                   5353: div.LC_edit_problem_saves {
                   5354:   float: right;
                   5355:   padding-bottom: 5px;
1.600     albertel 5356: }
                   5357: hr.LC_edit_problem_divide {
1.602     albertel 5358:   clear: both;
1.600     albertel 5359:   color: $tabbg;
                   5360:   background-color: $tabbg;
                   5361:   height: 3px;
                   5362:   border: 0px;
                   5363: }
1.343     albertel 5364: END
                   5365: }
                   5366: 
1.306     albertel 5367: =pod
                   5368: 
                   5369: =item * &headtag()
                   5370: 
                   5371: Returns a uniform footer for LON-CAPA web pages.
                   5372: 
1.307     albertel 5373: Inputs: $title - optional title for the head
                   5374:         $head_extra - optional extra HTML to put inside the <head>
1.315     albertel 5375:         $args - optional arguments
1.319     albertel 5376:             force_register - if is true call registerurl so the remote is 
                   5377:                              informed
1.415     albertel 5378:             redirect       -> array ref of
                   5379:                                    1- seconds before redirect occurs
                   5380:                                    2- url to redirect to
                   5381:                                    3- whether the side effect should occur
1.315     albertel 5382:                            (side effect of setting 
                   5383:                                $env{'internal.head.redirect'} to the url 
                   5384:                                redirected too)
1.352     albertel 5385:             domain         -> force to color decorate a page for a specific
                   5386:                                domain
                   5387:             function       -> force usage of a specific rolish color scheme
                   5388:             bgcolor        -> override the default page bgcolor
1.460     albertel 5389:             no_auto_mt_title
                   5390:                            -> prevent &mt()ing the title arg
1.464     albertel 5391: 
1.306     albertel 5392: =cut
                   5393: 
                   5394: sub headtag {
1.313     albertel 5395:     my ($title,$head_extra,$args) = @_;
1.306     albertel 5396:     
1.363     albertel 5397:     my $function = $args->{'function'} || &get_users_function();
                   5398:     my $domain   = $args->{'domain'}   || &determinedomain();
                   5399:     my $bgcolor  = $args->{'bgcolor'}  || &designparm($function.'.pgbg',$domain);
1.418     albertel 5400:     my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458     albertel 5401: 		   $Apache::lonnet::perlvar{'lonVersion'},
1.531     albertel 5402: 		   #time(),
1.418     albertel 5403: 		   $env{'environment.color.timestamp'},
1.363     albertel 5404: 		   $function,$domain,$bgcolor);
                   5405: 
1.369     www      5406:     $url = '/adm/css/'.&escape($url).'.css';
1.363     albertel 5407: 
1.308     albertel 5408:     my $result =
                   5409: 	'<head>'.
1.461     albertel 5410: 	&font_settings();
1.319     albertel 5411: 
1.461     albertel 5412:     if (!$args->{'frameset'}) {
                   5413: 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
                   5414:     }
1.319     albertel 5415:     if ($args->{'force_register'}) {
                   5416: 	$result .= &Apache::lonmenu::registerurl(1);
                   5417:     }
1.436     albertel 5418:     if (!$args->{'no_nav_bar'} 
                   5419: 	&& !$args->{'only_body'}
                   5420: 	&& !$args->{'frameset'}) {
                   5421: 	$result .= &help_menu_js();
                   5422:     }
1.319     albertel 5423: 
1.314     albertel 5424:     if (ref($args->{'redirect'})) {
1.414     albertel 5425: 	my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315     albertel 5426: 	$url = &Apache::lonenc::check_encrypt($url);
1.414     albertel 5427: 	if (!$inhibit_continue) {
                   5428: 	    $env{'internal.head.redirect'} = $url;
                   5429: 	}
1.313     albertel 5430: 	$result.=<<ADDMETA
                   5431: <meta http-equiv="pragma" content="no-cache" />
1.344     albertel 5432: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313     albertel 5433: ADDMETA
                   5434:     }
1.306     albertel 5435:     if (!defined($title)) {
                   5436: 	$title = 'The LearningOnline Network with CAPA';
                   5437:     }
1.460     albertel 5438:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
                   5439:     $result .= '<title> LON-CAPA '.$title.'</title>'
1.414     albertel 5440: 	.'<link rel="stylesheet" type="text/css" href="'.$url.'" />'
                   5441: 	.$head_extra;
1.306     albertel 5442:     return $result;
                   5443: }
                   5444: 
                   5445: =pod
                   5446: 
1.340     albertel 5447: =item * &font_settings()
                   5448: 
                   5449: Returns neccessary <meta> to set the proper encoding
                   5450: 
                   5451: Inputs: none
                   5452: 
                   5453: =cut
                   5454: 
                   5455: sub font_settings {
                   5456:     my $headerstring='';
1.647     www      5457:     if (!$env{'browser.mathml'} && $env{'browser.unicode'}) {
1.340     albertel 5458: 	$headerstring.=
                   5459: 	    '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
                   5460:     }
                   5461:     return $headerstring;
                   5462: }
                   5463: 
1.341     albertel 5464: =pod
                   5465: 
                   5466: =item * &xml_begin()
                   5467: 
                   5468: Returns the needed doctype and <html>
                   5469: 
                   5470: Inputs: none
                   5471: 
                   5472: =cut
                   5473: 
                   5474: sub xml_begin {
                   5475:     my $output='';
                   5476: 
1.592     albertel 5477:     if ($env{'internal.start_page'}==1) {
                   5478: 	&Apache::lonhtmlcommon::init_htmlareafields();
                   5479:     }
1.342     albertel 5480: 
1.341     albertel 5481:     if ($env{'browser.mathml'}) {
                   5482: 	$output='<?xml version="1.0"?>'
                   5483:             #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
                   5484: #            .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
                   5485:             
                   5486: #	    .'<!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">] >'
                   5487: 	    .'<!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">'
                   5488:             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" ' 
                   5489: 	    .'xmlns="http://www.w3.org/1999/xhtml">';
                   5490:     } else {
                   5491: 	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>';
                   5492:     }
                   5493:     return $output;
                   5494: }
1.340     albertel 5495: 
                   5496: =pod
                   5497: 
1.306     albertel 5498: =item * &endheadtag()
                   5499: 
                   5500: Returns a uniform </head> for LON-CAPA web pages.
                   5501: 
                   5502: Inputs: none
                   5503: 
                   5504: =cut
                   5505: 
                   5506: sub endheadtag {
                   5507:     return '</head>';
                   5508: }
                   5509: 
                   5510: =pod
                   5511: 
                   5512: =item * &head()
                   5513: 
                   5514: Returns a uniform complete <head>..</head> section for LON-CAPA web pages.
                   5515: 
1.648     raeburn  5516: Inputs:
                   5517: 
                   5518: =over 4
                   5519: 
                   5520: $title - optional title for the page
                   5521: 
                   5522: $head_extra - optional extra HTML to put inside the <head>
                   5523: 
                   5524: =back
1.405     albertel 5525: 
1.306     albertel 5526: =cut
                   5527: 
                   5528: sub head {
1.325     albertel 5529:     my ($title,$head_extra,$args) = @_;
                   5530:     return &headtag($title,$head_extra,$args).&endheadtag();
1.306     albertel 5531: }
                   5532: 
                   5533: =pod
                   5534: 
                   5535: =item * &start_page()
                   5536: 
                   5537: Returns a complete <html> .. <body> section for LON-CAPA web pages.
                   5538: 
1.648     raeburn  5539: Inputs:
                   5540: 
                   5541: =over 4
                   5542: 
                   5543: $title - optional title for the page
                   5544: 
                   5545: $head_extra - optional extra HTML to incude inside the <head>
                   5546: 
                   5547: $args - additional optional args supported are:
                   5548: 
                   5549: =over 8
                   5550: 
                   5551:              only_body      -> is true will set &bodytag() onlybodytag
1.317     albertel 5552:                                     arg on
1.648     raeburn  5553:              no_nav_bar     -> is true will set &bodytag() notopbar arg on
                   5554:              add_entries    -> additional attributes to add to the  <body>
                   5555:              domain         -> force to color decorate a page for a 
1.317     albertel 5556:                                     specific domain
1.648     raeburn  5557:              function       -> force usage of a specific rolish color
1.317     albertel 5558:                                     scheme
1.648     raeburn  5559:              redirect       -> see &headtag()
                   5560:              bgcolor        -> override the default page bg color
                   5561:              js_ready       -> return a string ready for being used in 
1.317     albertel 5562:                                     a javascript writeln
1.648     raeburn  5563:              html_encode    -> return a string ready for being used in 
1.320     albertel 5564:                                     a html attribute
1.648     raeburn  5565:              force_register -> if is true will turn on the &bodytag()
1.317     albertel 5566:                                     $forcereg arg
1.648     raeburn  5567:              body_title     -> alternate text to use instead of $title
1.326     albertel 5568:                                     in the title box that appears, this text
                   5569:                                     is not auto translated like the $title is
1.648     raeburn  5570:              frameset       -> if true will start with a <frameset>
1.330     albertel 5571:                                     rather than <body>
1.648     raeburn  5572:              no_title       -> if true the title bar won't be shown
                   5573:              skip_phases    -> hash ref of 
1.338     albertel 5574:                                     head -> skip the <html><head> generation
                   5575:                                     body -> skip all <body> generation
1.648     raeburn  5576:              no_inline_link -> if true and in remote mode, don't show the 
1.361     albertel 5577:                                     'Switch To Inline Menu' link
1.648     raeburn  5578:              no_auto_mt_title -> prevent &mt()ing the title arg
                   5579:              inherit_jsmath -> when creating popup window in a page,
                   5580:                                     should it have jsmath forced on by the
                   5581:                                     current page
1.361     albertel 5582: 
1.648     raeburn  5583: =back
1.460     albertel 5584: 
1.648     raeburn  5585: =back
1.562     albertel 5586: 
1.306     albertel 5587: =cut
                   5588: 
                   5589: sub start_page {
1.309     albertel 5590:     my ($title,$head_extra,$args) = @_;
1.318     albertel 5591:     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.313     albertel 5592:     my %head_args;
1.352     albertel 5593:     foreach my $arg ('redirect','force_register','domain','function',
1.460     albertel 5594: 		     'bgcolor','frameset','no_nav_bar','only_body',
                   5595: 		     'no_auto_mt_title') {
1.319     albertel 5596: 	if (defined($args->{$arg})) {
1.324     raeburn  5597: 	    $head_args{$arg} = $args->{$arg};
1.319     albertel 5598: 	}
1.313     albertel 5599:     }
1.319     albertel 5600: 
1.315     albertel 5601:     $env{'internal.start_page'}++;
1.338     albertel 5602:     my $result;
                   5603:     if (! exists($args->{'skip_phases'}{'head'}) ) {
                   5604: 	$result.=
1.341     albertel 5605: 	    &xml_begin().
1.338     albertel 5606: 	    &headtag($title,$head_extra,\%head_args).&endheadtag();
                   5607:     }
                   5608:     
                   5609:     if (! exists($args->{'skip_phases'}{'body'}) ) {
                   5610: 	if ($args->{'frameset'}) {
                   5611: 	    my $attr_string = &make_attr_string($args->{'force_register'},
                   5612: 						$args->{'add_entries'});
                   5613: 	    $result .= "\n<frameset $attr_string>\n";
                   5614: 	} else {
                   5615: 	    $result .=
                   5616: 		&bodytag($title, 
                   5617: 			 $args->{'function'},       $args->{'add_entries'},
                   5618: 			 $args->{'only_body'},      $args->{'domain'},
                   5619: 			 $args->{'force_register'}, $args->{'body_title'},
                   5620: 			 $args->{'no_nav_bar'},     $args->{'bgcolor'},
1.460     albertel 5621: 			 $args->{'no_title'},       $args->{'no_inline_link'},
                   5622: 			 $args);
1.338     albertel 5623: 	}
1.330     albertel 5624:     }
1.338     albertel 5625: 
1.315     albertel 5626:     if ($args->{'js_ready'}) {
1.317     albertel 5627: 	$result = &js_ready($result);
1.315     albertel 5628:     }
1.320     albertel 5629:     if ($args->{'html_encode'}) {
                   5630: 	$result = &html_encode($result);
                   5631:     }
1.315     albertel 5632:     return $result;
1.306     albertel 5633: }
                   5634: 
1.330     albertel 5635: 
1.306     albertel 5636: =pod
                   5637: 
                   5638: =item * &head()
                   5639: 
                   5640: Returns a complete </body></html> section for LON-CAPA web pages.
                   5641: 
1.315     albertel 5642: Inputs:         $args - additional optional args supported are:
                   5643:                  js_ready     -> return a string ready for being used in 
                   5644:                                  a javascript writeln
1.320     albertel 5645:                  html_encode  -> return a string ready for being used in 
                   5646:                                  a html attribute
1.330     albertel 5647:                  frameset     -> if true will start with a <frameset>
                   5648:                                  rather than <body>
1.493     albertel 5649:                  dicsussion   -> if true will get discussion from
                   5650:                                   lonxml::xmlend
                   5651:                                  (you can pass the target and parser arguments
                   5652:                                   through optional 'target' and 'parser' args
                   5653:                                   to this routine)
1.306     albertel 5654: 
                   5655: =cut
                   5656: 
                   5657: sub end_page {
1.315     albertel 5658:     my ($args) = @_;
                   5659:     $env{'internal.end_page'}++;
1.330     albertel 5660:     my $result;
1.335     albertel 5661:     if ($args->{'discussion'}) {
                   5662: 	my ($target,$parser);
                   5663: 	if (ref($args->{'discussion'})) {
                   5664: 	    ($target,$parser) =($args->{'discussion'}{'target'},
                   5665: 				$args->{'discussion'}{'parser'});
                   5666: 	}
                   5667: 	$result .= &Apache::lonxml::xmlend($target,$parser);
                   5668:     }
                   5669: 
1.330     albertel 5670:     if ($args->{'frameset'}) {
                   5671: 	$result .= '</frameset>';
                   5672:     } else {
1.635     raeburn  5673: 	$result .= &endbodytag($args);
1.330     albertel 5674:     }
                   5675:     $result .= "\n</html>";
                   5676: 
1.315     albertel 5677:     if ($args->{'js_ready'}) {
1.317     albertel 5678: 	$result = &js_ready($result);
1.315     albertel 5679:     }
1.335     albertel 5680: 
1.320     albertel 5681:     if ($args->{'html_encode'}) {
                   5682: 	$result = &html_encode($result);
                   5683:     }
1.335     albertel 5684: 
1.315     albertel 5685:     return $result;
                   5686: }
                   5687: 
1.320     albertel 5688: sub html_encode {
                   5689:     my ($result) = @_;
                   5690: 
1.322     albertel 5691:     $result = &HTML::Entities::encode($result,'<>&"');
1.320     albertel 5692:     
                   5693:     return $result;
                   5694: }
1.317     albertel 5695: sub js_ready {
                   5696:     my ($result) = @_;
                   5697: 
1.323     albertel 5698:     $result =~ s/[\n\r]/ /xmsg;
                   5699:     $result =~ s/\\/\\\\/xmsg;
                   5700:     $result =~ s/'/\\'/xmsg;
1.372     albertel 5701:     $result =~ s{</}{<\\/}xmsg;
1.317     albertel 5702:     
                   5703:     return $result;
                   5704: }
                   5705: 
1.315     albertel 5706: sub validate_page {
                   5707:     if (  exists($env{'internal.start_page'})
1.316     albertel 5708: 	  &&     $env{'internal.start_page'} > 1) {
                   5709: 	&Apache::lonnet::logthis('start_page called multiple times '.
1.318     albertel 5710: 				 $env{'internal.start_page'}.' '.
1.316     albertel 5711: 				 $ENV{'request.filename'});
1.315     albertel 5712:     }
                   5713:     if (  exists($env{'internal.end_page'})
1.316     albertel 5714: 	  &&     $env{'internal.end_page'} > 1) {
                   5715: 	&Apache::lonnet::logthis('end_page called multiple times '.
1.318     albertel 5716: 				 $env{'internal.end_page'}.' '.
1.316     albertel 5717: 				 $env{'request.filename'});
1.315     albertel 5718:     }
                   5719:     if (     exists($env{'internal.start_page'})
                   5720: 	&& ! exists($env{'internal.end_page'})) {
1.316     albertel 5721: 	&Apache::lonnet::logthis('start_page called without end_page '.
                   5722: 				 $env{'request.filename'});
1.315     albertel 5723:     }
                   5724:     if (   ! exists($env{'internal.start_page'})
                   5725: 	&&   exists($env{'internal.end_page'})) {
1.316     albertel 5726: 	&Apache::lonnet::logthis('end_page called without start_page'.
                   5727: 				 $env{'request.filename'});
1.315     albertel 5728:     }
1.306     albertel 5729: }
1.315     albertel 5730: 
1.318     albertel 5731: sub simple_error_page {
                   5732:     my ($r,$title,$msg) = @_;
                   5733:     my $page =
                   5734: 	&Apache::loncommon::start_page($title).
                   5735: 	&mt($msg).
                   5736: 	&Apache::loncommon::end_page();
                   5737:     if (ref($r)) {
                   5738: 	$r->print($page);
1.327     albertel 5739: 	return;
1.318     albertel 5740:     }
                   5741:     return $page;
                   5742: }
1.347     albertel 5743: 
                   5744: {
1.610     albertel 5745:     my @row_count;
1.347     albertel 5746:     sub start_data_table {
1.422     albertel 5747: 	my ($add_class) = @_;
                   5748: 	my $css_class = (join(' ','LC_data_table',$add_class));
1.610     albertel 5749: 	unshift(@row_count,0);
1.422     albertel 5750: 	return '<table class="'.$css_class.'">'."\n";
1.347     albertel 5751:     }
                   5752: 
                   5753:     sub end_data_table {
1.610     albertel 5754: 	shift(@row_count);
1.389     albertel 5755: 	return '</table>'."\n";;
1.347     albertel 5756:     }
                   5757: 
                   5758:     sub start_data_table_row {
1.422     albertel 5759: 	my ($add_class) = @_;
1.610     albertel 5760: 	$row_count[0]++;
                   5761: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.428     albertel 5762: 	$css_class = (join(' ',$css_class,$add_class));
1.422     albertel 5763: 	return  '<tr class="'.$css_class.'">'."\n";;
1.347     albertel 5764:     }
1.471     banghart 5765:     
                   5766:     sub continue_data_table_row {
                   5767: 	my ($add_class) = @_;
1.610     albertel 5768: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.471     banghart 5769: 	$css_class = (join(' ',$css_class,$add_class));
                   5770: 	return  '<tr class="'.$css_class.'">'."\n";;
                   5771:     }
1.347     albertel 5772: 
                   5773:     sub end_data_table_row {
1.389     albertel 5774: 	return '</tr>'."\n";;
1.347     albertel 5775:     }
1.367     www      5776: 
1.421     albertel 5777:     sub start_data_table_empty_row {
1.610     albertel 5778: 	$row_count[0]++;
1.421     albertel 5779: 	return  '<tr class="LC_empty_row" >'."\n";;
                   5780:     }
                   5781: 
                   5782:     sub end_data_table_empty_row {
                   5783: 	return '</tr>'."\n";;
                   5784:     }
                   5785: 
1.367     www      5786:     sub start_data_table_header_row {
1.389     albertel 5787: 	return  '<tr class="LC_header_row">'."\n";;
1.367     www      5788:     }
                   5789: 
                   5790:     sub end_data_table_header_row {
1.389     albertel 5791: 	return '</tr>'."\n";;
1.367     www      5792:     }
1.347     albertel 5793: }
                   5794: 
1.548     albertel 5795: =pod
                   5796: 
                   5797: =item * &inhibit_menu_check($arg)
                   5798: 
                   5799: Checks for a inhibitmenu state and generates output to preserve it
                   5800: 
                   5801: Inputs:         $arg - can be any of
                   5802:                      - undef - in which case the return value is a string 
                   5803:                                to add  into arguments list of a uri
                   5804:                      - 'input' - in which case the return value is a HTML
                   5805:                                  <form> <input> field of type hidden to
                   5806:                                  preserve the value
                   5807:                      - a url - in which case the return value is the url with
                   5808:                                the neccesary cgi args added to preserve the
                   5809:                                inhibitmenu state
                   5810:                      - a ref to a url - no return value, but the string is
                   5811:                                         updated to include the neccessary cgi
                   5812:                                         args to preserve the inhibitmenu state
                   5813: 
                   5814: =cut
                   5815: 
                   5816: sub inhibit_menu_check {
                   5817:     my ($arg) = @_;
                   5818:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   5819:     if ($arg eq 'input') {
                   5820: 	if ($env{'form.inhibitmenu'}) {
                   5821: 	    return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
                   5822: 	} else {
                   5823: 	    return
                   5824: 	}
                   5825:     }
                   5826:     if ($env{'form.inhibitmenu'}) {
                   5827: 	if (ref($arg)) {
                   5828: 	    $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   5829: 	} elsif ($arg eq '') {
                   5830: 	    $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
                   5831: 	} else {
                   5832: 	    $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   5833: 	}
                   5834:     }
                   5835:     if (!ref($arg)) {
                   5836: 	return $arg;
                   5837:     }
                   5838: }
                   5839: 
1.251     albertel 5840: ###############################################
1.182     matthew  5841: 
                   5842: =pod
                   5843: 
1.549     albertel 5844: =back
                   5845: 
                   5846: =head1 User Information Routines
                   5847: 
                   5848: =over 4
                   5849: 
1.405     albertel 5850: =item * &get_users_function()
1.182     matthew  5851: 
                   5852: Used by &bodytag to determine the current users primary role.
                   5853: Returns either 'student','coordinator','admin', or 'author'.
                   5854: 
                   5855: =cut
                   5856: 
                   5857: ###############################################
                   5858: sub get_users_function {
                   5859:     my $function = 'student';
1.258     albertel 5860:     if ($env{'request.role'}=~/^(cc|in|ta|ep)/) {
1.182     matthew  5861:         $function='coordinator';
                   5862:     }
1.258     albertel 5863:     if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182     matthew  5864:         $function='admin';
                   5865:     }
1.258     albertel 5866:     if (($env{'request.role'}=~/^(au|ca)/) ||
1.182     matthew  5867:         ($ENV{'REQUEST_URI'}=~/^(\/priv|\~)/)) {
                   5868:         $function='author';
                   5869:     }
                   5870:     return $function;
1.54      www      5871: }
1.99      www      5872: 
                   5873: ###############################################
                   5874: 
1.233     raeburn  5875: =pod
                   5876: 
1.542     raeburn  5877: =item * &check_user_status()
1.274     raeburn  5878: 
                   5879: Determines current status of supplied role for a
                   5880: specific user. Roles can be active, previous or future.
                   5881: 
                   5882: Inputs: 
                   5883: user's domain, user's username, course's domain,
1.375     raeburn  5884: course's number, optional section ID.
1.274     raeburn  5885: 
                   5886: Outputs:
                   5887: role status: active, previous or future. 
                   5888: 
                   5889: =cut
                   5890: 
                   5891: sub check_user_status {
1.412     raeburn  5892:     my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.274     raeburn  5893:     my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
                   5894:     my @uroles = keys %userinfo;
                   5895:     my $srchstr;
                   5896:     my $active_chk = 'none';
1.412     raeburn  5897:     my $now = time;
1.274     raeburn  5898:     if (@uroles > 0) {
1.412     raeburn  5899:         if (($role eq 'cc') || ($sec eq '') || (!defined($sec))) {
1.274     raeburn  5900:             $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
                   5901:         } else {
1.412     raeburn  5902:             $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
                   5903:         }
                   5904:         if (grep/^\Q$srchstr\E$/,@uroles) {
1.274     raeburn  5905:             my $role_end = 0;
                   5906:             my $role_start = 0;
                   5907:             $active_chk = 'active';
1.412     raeburn  5908:             if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
                   5909:                 $role_end = $1;
                   5910:                 if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
                   5911:                     $role_start = $1;
1.274     raeburn  5912:                 }
                   5913:             }
                   5914:             if ($role_start > 0) {
1.412     raeburn  5915:                 if ($now < $role_start) {
1.274     raeburn  5916:                     $active_chk = 'future';
                   5917:                 }
                   5918:             }
                   5919:             if ($role_end > 0) {
1.412     raeburn  5920:                 if ($now > $role_end) {
1.274     raeburn  5921:                     $active_chk = 'previous';
                   5922:                 }
                   5923:             }
                   5924:         }
                   5925:     }
                   5926:     return $active_chk;
                   5927: }
                   5928: 
                   5929: ###############################################
                   5930: 
                   5931: =pod
                   5932: 
1.405     albertel 5933: =item * &get_sections()
1.233     raeburn  5934: 
                   5935: Determines all the sections for a course including
                   5936: sections with students and sections containing other roles.
1.419     raeburn  5937: Incoming parameters: 
                   5938: 
                   5939: 1. domain
                   5940: 2. course number 
                   5941: 3. reference to array containing roles for which sections should 
                   5942: be gathered (optional).
                   5943: 4. reference to array containing status types for which sections 
                   5944: should be gathered (optional).
                   5945: 
                   5946: If the third argument is undefined, sections are gathered for any role. 
                   5947: If the fourth argument is undefined, sections are gathered for any status.
                   5948: Permissible values are 'active' or 'future' or 'previous'.
1.233     raeburn  5949:  
1.374     raeburn  5950: Returns section hash (keys are section IDs, values are
                   5951: number of users in each section), subject to the
1.419     raeburn  5952: optional roles filter, optional status filter 
1.233     raeburn  5953: 
                   5954: =cut
                   5955: 
                   5956: ###############################################
                   5957: sub get_sections {
1.419     raeburn  5958:     my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366     albertel 5959:     if (!defined($cdom) || !defined($cnum)) {
                   5960:         my $cid =  $env{'request.course.id'};
                   5961: 
                   5962: 	return if (!defined($cid));
                   5963: 
                   5964:         $cdom = $env{'course.'.$cid.'.domain'};
                   5965:         $cnum = $env{'course.'.$cid.'.num'};
                   5966:     }
                   5967: 
                   5968:     my %sectioncount;
1.419     raeburn  5969:     my $now = time;
1.240     albertel 5970: 
1.366     albertel 5971:     if (!defined($possible_roles) || (grep(/^st$/,@$possible_roles))) {
1.276     albertel 5972: 	my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240     albertel 5973: 	my $sec_index = &Apache::loncoursedata::CL_SECTION();
                   5974: 	my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419     raeburn  5975:         my $start_index = &Apache::loncoursedata::CL_START();
                   5976:         my $end_index = &Apache::loncoursedata::CL_END();
                   5977:         my $status;
1.366     albertel 5978: 	while (my ($student,$data) = each(%$classlist)) {
1.419     raeburn  5979: 	    my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
                   5980: 				                     $data->[$status_index],
                   5981:                                                      $data->[$start_index],
                   5982:                                                      $data->[$end_index]);
                   5983:             if ($stu_status eq 'Active') {
                   5984:                 $status = 'active';
                   5985:             } elsif ($end < $now) {
                   5986:                 $status = 'previous';
                   5987:             } elsif ($start > $now) {
                   5988:                 $status = 'future';
                   5989:             } 
                   5990: 	    if ($section ne '-1' && $section !~ /^\s*$/) {
                   5991:                 if ((!defined($possible_status)) || (($status ne '') && 
                   5992:                     (grep/^\Q$status\E$/,@{$possible_status}))) { 
                   5993: 		    $sectioncount{$section}++;
                   5994:                 }
1.240     albertel 5995: 	    }
                   5996: 	}
                   5997:     }
                   5998:     my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   5999:     foreach my $user (sort(keys(%courseroles))) {
                   6000: 	if ($user !~ /^(\w{2})/) { next; }
                   6001: 	my ($role) = ($user =~ /^(\w{2})/);
                   6002: 	if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419     raeburn  6003: 	my ($section,$status);
1.240     albertel 6004: 	if ($role eq 'cr' &&
                   6005: 	    $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
                   6006: 	    $section=$1;
                   6007: 	}
                   6008: 	if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
                   6009: 	if (!defined($section) || $section eq '-1') { next; }
1.419     raeburn  6010:         my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
                   6011:         if ($end == -1 && $start == -1) {
                   6012:             next; #deleted role
                   6013:         }
                   6014:         if (!defined($possible_status)) { 
                   6015:             $sectioncount{$section}++;
                   6016:         } else {
                   6017:             if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
                   6018:                 $status = 'active';
                   6019:             } elsif ($end < $now) {
                   6020:                 $status = 'future';
                   6021:             } elsif ($start > $now) {
                   6022:                 $status = 'previous';
                   6023:             }
                   6024:             if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
                   6025:                 $sectioncount{$section}++;
                   6026:             }
                   6027:         }
1.233     raeburn  6028:     }
1.366     albertel 6029:     return %sectioncount;
1.233     raeburn  6030: }
                   6031: 
1.274     raeburn  6032: ###############################################
1.294     raeburn  6033: 
                   6034: =pod
1.405     albertel 6035: 
                   6036: =item * &get_course_users()
                   6037: 
1.275     raeburn  6038: Retrieves usernames:domains for users in the specified course
                   6039: with specific role(s), and access status. 
                   6040: 
                   6041: Incoming parameters:
1.277     albertel 6042: 1. course domain
                   6043: 2. course number
                   6044: 3. access status: users must have - either active, 
1.275     raeburn  6045: previous, future, or all.
1.277     albertel 6046: 4. reference to array of permissible roles
1.288     raeburn  6047: 5. reference to array of section restrictions (optional)
                   6048: 6. reference to results object (hash of hashes).
                   6049: 7. reference to optional userdata hash
1.609     raeburn  6050: 8. reference to optional statushash
1.630     raeburn  6051: 9. flag if privileged users (except those set to unhide in
                   6052:    course settings) should be excluded    
1.609     raeburn  6053: Keys of top level results hash are roles.
1.275     raeburn  6054: Keys of inner hashes are username:domain, with 
                   6055: values set to access type.
1.288     raeburn  6056: Optional userdata hash returns an array with arguments in the 
                   6057: same order as loncoursedata::get_classlist() for student data.
                   6058: 
1.609     raeburn  6059: Optional statushash returns
                   6060: 
1.288     raeburn  6061: Entries for end, start, section and status are blank because
                   6062: of the possibility of multiple values for non-student roles.
                   6063: 
1.275     raeburn  6064: =cut
1.405     albertel 6065: 
1.275     raeburn  6066: ###############################################
1.405     albertel 6067: 
1.275     raeburn  6068: sub get_course_users {
1.630     raeburn  6069:     my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288     raeburn  6070:     my %idx = ();
1.419     raeburn  6071:     my %seclists;
1.288     raeburn  6072: 
                   6073:     $idx{udom} = &Apache::loncoursedata::CL_SDOM();
                   6074:     $idx{uname} =  &Apache::loncoursedata::CL_SNAME();
                   6075:     $idx{end} = &Apache::loncoursedata::CL_END();
                   6076:     $idx{start} = &Apache::loncoursedata::CL_START();
                   6077:     $idx{id} = &Apache::loncoursedata::CL_ID();
                   6078:     $idx{section} = &Apache::loncoursedata::CL_SECTION();
                   6079:     $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
                   6080:     $idx{status} = &Apache::loncoursedata::CL_STATUS();
                   6081: 
1.290     albertel 6082:     if (grep(/^st$/,@{$roles})) {
1.276     albertel 6083:         my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278     raeburn  6084:         my $now = time;
1.277     albertel 6085:         foreach my $student (keys(%{$classlist})) {
1.288     raeburn  6086:             my $match = 0;
1.412     raeburn  6087:             my $secmatch = 0;
1.419     raeburn  6088:             my $section = $$classlist{$student}[$idx{section}];
1.609     raeburn  6089:             my $status = $$classlist{$student}[$idx{status}];
1.419     raeburn  6090:             if ($section eq '') {
                   6091:                 $section = 'none';
                   6092:             }
1.291     albertel 6093:             if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 6094:                 if (grep(/^all$/,@{$sections})) {
1.412     raeburn  6095:                     $secmatch = 1;
                   6096:                 } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420     albertel 6097:                     if (grep(/^none$/,@{$sections})) {
1.412     raeburn  6098:                         $secmatch = 1;
                   6099:                     }
                   6100:                 } else {  
1.419     raeburn  6101: 		    if (grep(/^\Q$section\E$/,@{$sections})) {
1.412     raeburn  6102: 		        $secmatch = 1;
                   6103:                     }
1.290     albertel 6104: 		}
1.412     raeburn  6105:                 if (!$secmatch) {
                   6106:                     next;
                   6107:                 }
1.419     raeburn  6108:             }
1.275     raeburn  6109:             if (defined($$types{'active'})) {
1.288     raeburn  6110:                 if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275     raeburn  6111:                     push(@{$$users{st}{$student}},'active');
1.288     raeburn  6112:                     $match = 1;
1.275     raeburn  6113:                 }
                   6114:             }
                   6115:             if (defined($$types{'previous'})) {
1.609     raeburn  6116:                 if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275     raeburn  6117:                     push(@{$$users{st}{$student}},'previous');
1.288     raeburn  6118:                     $match = 1;
1.275     raeburn  6119:                 }
                   6120:             }
                   6121:             if (defined($$types{'future'})) {
1.609     raeburn  6122:                 if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275     raeburn  6123:                     push(@{$$users{st}{$student}},'future');
1.288     raeburn  6124:                     $match = 1;
1.275     raeburn  6125:                 }
                   6126:             }
1.609     raeburn  6127:             if ($match) {
                   6128:                 push(@{$seclists{$student}},$section);
                   6129:                 if (ref($userdata) eq 'HASH') {
                   6130:                     $$userdata{$student} = $$classlist{$student};
                   6131:                 }
                   6132:                 if (ref($statushash) eq 'HASH') {
                   6133:                     $statushash->{$student}{'st'}{$section} = $status;
                   6134:                 }
1.288     raeburn  6135:             }
1.275     raeburn  6136:         }
                   6137:     }
1.412     raeburn  6138:     if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439     raeburn  6139:         my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   6140:         my $now = time;
1.609     raeburn  6141:         my %displaystatus = ( previous => 'Expired',
                   6142:                               active   => 'Active',
                   6143:                               future   => 'Future',
                   6144:                             );
1.630     raeburn  6145:         my %nothide;
                   6146:         if ($hidepriv) {
                   6147:             my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
                   6148:             foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
                   6149:                 if ($user !~ /:/) {
                   6150:                     $nothide{join(':',split(/[\@]/,$user))}=1;
                   6151:                 } else {
                   6152:                     $nothide{$user} = 1;
                   6153:                 }
                   6154:             }
                   6155:         }
1.439     raeburn  6156:         foreach my $person (sort(keys(%coursepersonnel))) {
1.288     raeburn  6157:             my $match = 0;
1.412     raeburn  6158:             my $secmatch = 0;
1.439     raeburn  6159:             my $status;
1.412     raeburn  6160:             my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275     raeburn  6161:             $user =~ s/:$//;
1.439     raeburn  6162:             my ($end,$start) = split(/:/,$coursepersonnel{$person});
                   6163:             if ($end == -1 || $start == -1) {
                   6164:                 next;
                   6165:             }
                   6166:             if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
                   6167:                 (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412     raeburn  6168:                 my ($uname,$udom) = split(/:/,$user);
                   6169:                 if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 6170:                     if (grep(/^all$/,@{$sections})) {
1.412     raeburn  6171:                         $secmatch = 1;
                   6172:                     } elsif ($usec eq '') {
1.420     albertel 6173:                         if (grep(/^none$/,@{$sections})) {
1.412     raeburn  6174:                             $secmatch = 1;
                   6175:                         }
                   6176:                     } else {
                   6177:                         if (grep(/^\Q$usec\E$/,@{$sections})) {
                   6178:                             $secmatch = 1;
                   6179:                         }
                   6180:                     }
                   6181:                     if (!$secmatch) {
                   6182:                         next;
                   6183:                     }
1.288     raeburn  6184:                 }
1.419     raeburn  6185:                 if ($usec eq '') {
                   6186:                     $usec = 'none';
                   6187:                 }
1.275     raeburn  6188:                 if ($uname ne '' && $udom ne '') {
1.630     raeburn  6189:                     if ($hidepriv) {
                   6190:                         if ((&Apache::lonnet::privileged($uname,$udom)) &&
                   6191:                             (!$nothide{$uname.':'.$udom})) {
                   6192:                             next;
                   6193:                         }
                   6194:                     }
1.503     raeburn  6195:                     if ($end > 0 && $end < $now) {
1.439     raeburn  6196:                         $status = 'previous';
                   6197:                     } elsif ($start > $now) {
                   6198:                         $status = 'future';
                   6199:                     } else {
                   6200:                         $status = 'active';
                   6201:                     }
1.277     albertel 6202:                     foreach my $type (keys(%{$types})) { 
1.275     raeburn  6203:                         if ($status eq $type) {
1.420     albertel 6204:                             if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419     raeburn  6205:                                 push(@{$$users{$role}{$user}},$type);
                   6206:                             }
1.288     raeburn  6207:                             $match = 1;
                   6208:                         }
                   6209:                     }
1.419     raeburn  6210:                     if (($match) && (ref($userdata) eq 'HASH')) {
                   6211:                         if (!exists($$userdata{$uname.':'.$udom})) {
                   6212: 			    &get_user_info($udom,$uname,\%idx,$userdata);
                   6213:                         }
1.420     albertel 6214:                         if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419     raeburn  6215:                             push(@{$seclists{$uname.':'.$udom}},$usec);
                   6216:                         }
1.609     raeburn  6217:                         if (ref($statushash) eq 'HASH') {
                   6218:                             $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
                   6219:                         }
1.275     raeburn  6220:                     }
                   6221:                 }
                   6222:             }
                   6223:         }
1.290     albertel 6224:         if (grep(/^ow$/,@{$roles})) {
1.279     raeburn  6225:             if ((defined($cdom)) && (defined($cnum))) {
                   6226:                 my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
                   6227:                 if ( defined($csettings{'internal.courseowner'}) ) {
                   6228:                     my $owner = $csettings{'internal.courseowner'};
1.609     raeburn  6229:                     next if ($owner eq '');
                   6230:                     my ($ownername,$ownerdom);
                   6231:                     if ($owner =~ /^([^:]+):([^:]+)$/) {
                   6232:                         $ownername = $1;
                   6233:                         $ownerdom = $2;
                   6234:                     } else {
                   6235:                         $ownername = $owner;
                   6236:                         $ownerdom = $cdom;
                   6237:                         $owner = $ownername.':'.$ownerdom;
1.439     raeburn  6238:                     }
                   6239:                     @{$$users{'ow'}{$owner}} = 'any';
1.290     albertel 6240:                     if (defined($userdata) && 
1.609     raeburn  6241: 			!exists($$userdata{$owner})) {
                   6242: 			&get_user_info($ownerdom,$ownername,\%idx,$userdata);
                   6243:                         if (!grep(/^none$/,@{$seclists{$owner}})) {
                   6244:                             push(@{$seclists{$owner}},'none');
                   6245:                         }
                   6246:                         if (ref($statushash) eq 'HASH') {
                   6247:                             $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419     raeburn  6248:                         }
1.290     albertel 6249: 		    }
1.279     raeburn  6250:                 }
                   6251:             }
                   6252:         }
1.419     raeburn  6253:         foreach my $user (keys(%seclists)) {
                   6254:             @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
                   6255:             $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
                   6256:         }
1.275     raeburn  6257:     }
                   6258:     return;
                   6259: }
                   6260: 
1.288     raeburn  6261: sub get_user_info {
                   6262:     my ($udom,$uname,$idx,$userdata) = @_;
1.289     albertel 6263:     $$userdata{$uname.':'.$udom}[$$idx{fullname}] = 
                   6264: 	&plainname($uname,$udom,'lastname');
1.291     albertel 6265:     $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297     raeburn  6266:     $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609     raeburn  6267:     my %idhash =  &Apache::lonnet::idrget($udom,($uname));
                   6268:     $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname}; 
1.288     raeburn  6269:     return;
                   6270: }
1.275     raeburn  6271: 
1.472     raeburn  6272: ###############################################
                   6273: 
                   6274: =pod
                   6275: 
                   6276: =item * &get_user_quota()
                   6277: 
                   6278: Retrieves quota assigned for storage of portfolio files for a user  
                   6279: 
                   6280: Incoming parameters:
                   6281: 1. user's username
                   6282: 2. user's domain
                   6283: 
                   6284: Returns:
1.536     raeburn  6285: 1. Disk quota (in Mb) assigned to student.
                   6286: 2. (Optional) Type of setting: custom or default
                   6287:    (individually assigned or default for user's 
                   6288:    institutional status).
                   6289: 3. (Optional) - User's institutional status (e.g., faculty, staff
                   6290:    or student - types as defined in localenroll::inst_usertypes 
                   6291:    for user's domain, which determines default quota for user.
                   6292: 4. (Optional) - Default quota which would apply to the user.
1.472     raeburn  6293: 
                   6294: If a value has been stored in the user's environment, 
1.536     raeburn  6295: it will return that, otherwise it returns the maximal default
                   6296: defined for the user's instituional status(es) in the domain.
1.472     raeburn  6297: 
                   6298: =cut
                   6299: 
                   6300: ###############################################
                   6301: 
                   6302: 
                   6303: sub get_user_quota {
                   6304:     my ($uname,$udom) = @_;
1.536     raeburn  6305:     my ($quota,$quotatype,$settingstatus,$defquota);
1.472     raeburn  6306:     if (!defined($udom)) {
                   6307:         $udom = $env{'user.domain'};
                   6308:     }
                   6309:     if (!defined($uname)) {
                   6310:         $uname = $env{'user.name'};
                   6311:     }
                   6312:     if (($udom eq '' || $uname eq '') ||
                   6313:         ($udom eq 'public') && ($uname eq 'public')) {
                   6314:         $quota = 0;
1.536     raeburn  6315:         $quotatype = 'default';
                   6316:         $defquota = 0; 
1.472     raeburn  6317:     } else {
1.536     raeburn  6318:         my $inststatus;
1.472     raeburn  6319:         if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
                   6320:             $quota = $env{'environment.portfolioquota'};
1.536     raeburn  6321:             $inststatus = $env{'environment.inststatus'};
1.472     raeburn  6322:         } else {
1.536     raeburn  6323:             my %userenv = 
                   6324:                 &Apache::lonnet::get('environment',['portfolioquota',
                   6325:                                      'inststatus'],$udom,$uname);
1.472     raeburn  6326:             my ($tmp) = keys(%userenv);
                   6327:             if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   6328:                 $quota = $userenv{'portfolioquota'};
1.536     raeburn  6329:                 $inststatus = $userenv{'inststatus'};
1.472     raeburn  6330:             } else {
                   6331:                 undef(%userenv);
                   6332:             }
                   6333:         }
1.536     raeburn  6334:         ($defquota,$settingstatus) = &default_quota($udom,$inststatus);
1.472     raeburn  6335:         if ($quota eq '') {
1.536     raeburn  6336:             $quota = $defquota;
                   6337:             $quotatype = 'default';
                   6338:         } else {
                   6339:             $quotatype = 'custom';
1.472     raeburn  6340:         }
                   6341:     }
1.536     raeburn  6342:     if (wantarray) {
                   6343:         return ($quota,$quotatype,$settingstatus,$defquota);
                   6344:     } else {
                   6345:         return $quota;
                   6346:     }
1.472     raeburn  6347: }
                   6348: 
                   6349: ###############################################
                   6350: 
                   6351: =pod
                   6352: 
                   6353: =item * &default_quota()
                   6354: 
1.536     raeburn  6355: Retrieves default quota assigned for storage of user portfolio files,
                   6356: given an (optional) user's institutional status.
1.472     raeburn  6357: 
                   6358: Incoming parameters:
                   6359: 1. domain
1.536     raeburn  6360: 2. (Optional) institutional status(es).  This is a : separated list of 
                   6361:    status types (e.g., faculty, staff, student etc.)
                   6362:    which apply to the user for whom the default is being retrieved.
                   6363:    If the institutional status string in undefined, the domain
                   6364:    default quota will be returned. 
1.472     raeburn  6365: 
                   6366: Returns:
                   6367: 1. Default disk quota (in Mb) for user portfolios in the domain.
1.536     raeburn  6368: 2. (Optional) institutional type which determined the value of the
                   6369:    default quota.
1.472     raeburn  6370: 
                   6371: If a value has been stored in the domain's configuration db,
                   6372: it will return that, otherwise it returns 20 (for backwards 
                   6373: compatibility with domains which have not set up a configuration
                   6374: db file; the original statically defined portfolio quota was 20 Mb). 
                   6375: 
1.536     raeburn  6376: If the user's status includes multiple types (e.g., staff and student),
                   6377: the largest default quota which applies to the user determines the
                   6378: default quota returned.
                   6379: 
1.472     raeburn  6380: =cut
                   6381: 
                   6382: ###############################################
                   6383: 
                   6384: 
                   6385: sub default_quota {
1.536     raeburn  6386:     my ($udom,$inststatus) = @_;
                   6387:     my ($defquota,$settingstatus);
                   6388:     my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622     raeburn  6389:                                             ['quotas'],$udom);
                   6390:     if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536     raeburn  6391:         if ($inststatus ne '') {
                   6392:             my @statuses = split(/:/,$inststatus);
                   6393:             foreach my $item (@statuses) {
1.622     raeburn  6394:                 if ($quotahash{'quotas'}{$item} ne '') {
1.536     raeburn  6395:                     if ($defquota eq '') {
1.622     raeburn  6396:                         $defquota = $quotahash{'quotas'}{$item};
1.536     raeburn  6397:                         $settingstatus = $item;
1.622     raeburn  6398:                     } elsif ($quotahash{'quotas'}{$item} > $defquota) {
                   6399:                         $defquota = $quotahash{'quotas'}{$item};
1.536     raeburn  6400:                         $settingstatus = $item;
                   6401:                     }
                   6402:                 }
                   6403:             }
                   6404:         }
                   6405:         if ($defquota eq '') {
1.622     raeburn  6406:             $defquota = $quotahash{'quotas'}{'default'};
1.536     raeburn  6407:             $settingstatus = 'default';
                   6408:         }
                   6409:     } else {
                   6410:         $settingstatus = 'default';
                   6411:         $defquota = 20;
                   6412:     }
                   6413:     if (wantarray) {
                   6414:         return ($defquota,$settingstatus);
1.472     raeburn  6415:     } else {
1.536     raeburn  6416:         return $defquota;
1.472     raeburn  6417:     }
                   6418: }
                   6419: 
1.384     raeburn  6420: sub get_secgrprole_info {
                   6421:     my ($cdom,$cnum,$needroles,$type)  = @_;
                   6422:     my %sections_count = &get_sections($cdom,$cnum);
                   6423:     my @sections =  (sort {$a <=> $b} keys(%sections_count));
                   6424:     my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
                   6425:     my @groups = sort(keys(%curr_groups));
                   6426:     my $allroles = [];
                   6427:     my $rolehash;
                   6428:     my $accesshash = {
                   6429:                      active => 'Currently has access',
                   6430:                      future => 'Will have future access',
                   6431:                      previous => 'Previously had access',
                   6432:                   };
                   6433:     if ($needroles) {
                   6434:         $rolehash = {'all' => 'all'};
1.385     albertel 6435:         my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   6436: 	if (&Apache::lonnet::error(%user_roles)) {
                   6437: 	    undef(%user_roles);
                   6438: 	}
                   6439:         foreach my $item (keys(%user_roles)) {
1.384     raeburn  6440:             my ($role)=split(/\:/,$item,2);
                   6441:             if ($role eq 'cr') { next; }
                   6442:             if ($role =~ /^cr/) {
                   6443:                 $$rolehash{$role} = (split('/',$role))[3];
                   6444:             } else {
                   6445:                 $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
                   6446:             }
                   6447:         }
                   6448:         foreach my $key (sort(keys(%{$rolehash}))) {
                   6449:             push(@{$allroles},$key);
                   6450:         }
                   6451:         push (@{$allroles},'st');
                   6452:         $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
                   6453:     }
                   6454:     return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
                   6455: }
                   6456: 
1.555     raeburn  6457: sub user_picker {
1.627     raeburn  6458:     my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype) = @_;
1.555     raeburn  6459:     my $currdom = $dom;
                   6460:     my %curr_selected = (
                   6461:                         srchin => 'dom',
1.580     raeburn  6462:                         srchby => 'lastname',
1.555     raeburn  6463:                       );
                   6464:     my $srchterm;
1.625     raeburn  6465:     if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555     raeburn  6466:         if ($srch->{'srchby'} ne '') {
                   6467:             $curr_selected{'srchby'} = $srch->{'srchby'};
                   6468:         }
                   6469:         if ($srch->{'srchin'} ne '') {
                   6470:             $curr_selected{'srchin'} = $srch->{'srchin'};
                   6471:         }
                   6472:         if ($srch->{'srchtype'} ne '') {
                   6473:             $curr_selected{'srchtype'} = $srch->{'srchtype'};
                   6474:         }
                   6475:         if ($srch->{'srchdomain'} ne '') {
                   6476:             $currdom = $srch->{'srchdomain'};
                   6477:         }
                   6478:         $srchterm = $srch->{'srchterm'};
                   6479:     }
                   6480:     my %lt=&Apache::lonlocal::texthash(
1.573     raeburn  6481:                     'usr'       => 'Search criteria',
1.563     raeburn  6482:                     'doma'      => 'Domain/institution to search',
1.558     albertel 6483:                     'uname'     => 'username',
                   6484:                     'lastname'  => 'last name',
1.555     raeburn  6485:                     'lastfirst' => 'last name, first name',
1.558     albertel 6486:                     'crs'       => 'in this course',
1.576     raeburn  6487:                     'dom'       => 'in selected LON-CAPA domain', 
1.558     albertel 6488:                     'alc'       => 'all LON-CAPA',
1.573     raeburn  6489:                     'instd'     => 'in institutional directory for selected domain',
1.558     albertel 6490:                     'exact'     => 'is',
                   6491:                     'contains'  => 'contains',
1.569     raeburn  6492:                     'begins'    => 'begins with',
1.571     raeburn  6493:                     'youm'      => "You must include some text to search for.",
                   6494:                     'thte'      => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
                   6495:                     'thet'      => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
                   6496:                     'yomc'      => "You must choose a domain when using an institutional directory search.",
                   6497:                     'ymcd'      => "You must choose a domain when using a domain search.",
                   6498:                     'whus'      => "When using searching by last,first you must include a comma as separator between last name and first name.",
                   6499:                     'whse'      => "When searching by last,first you must include at least one character in the first name.",
                   6500:                      'thfo'     => "The following need to be corrected before the search can be run:",
1.555     raeburn  6501:                                        );
1.563     raeburn  6502:     my $domform = &select_dom_form($currdom,'srchdomain',1,1);
                   6503:     my $srchinsel = ' <select name="srchin">';
1.555     raeburn  6504: 
                   6505:     my @srchins = ('crs','dom','alc','instd');
                   6506: 
                   6507:     foreach my $option (@srchins) {
                   6508:         # FIXME 'alc' option unavailable until 
                   6509:         #       loncreateuser::print_user_query_page()
                   6510:         #       has been completed.
                   6511:         next if ($option eq 'alc');
                   6512:         next if ($option eq 'crs' && !$env{'request.course.id'});
1.563     raeburn  6513:         if ($curr_selected{'srchin'} eq $option) {
                   6514:             $srchinsel .= ' 
                   6515:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   6516:         } else {
                   6517:             $srchinsel .= '
                   6518:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   6519:         }
1.555     raeburn  6520:     }
1.563     raeburn  6521:     $srchinsel .= "\n  </select>\n";
1.555     raeburn  6522: 
                   6523:     my $srchbysel =  ' <select name="srchby">';
1.580     raeburn  6524:     foreach my $option ('lastname','lastfirst','uname') {
1.555     raeburn  6525:         if ($curr_selected{'srchby'} eq $option) {
                   6526:             $srchbysel .= '
                   6527:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   6528:         } else {
                   6529:             $srchbysel .= '
                   6530:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   6531:          }
                   6532:     }
                   6533:     $srchbysel .= "\n  </select>\n";
                   6534: 
                   6535:     my $srchtypesel = ' <select name="srchtype">';
1.580     raeburn  6536:     foreach my $option ('begins','contains','exact') {
1.555     raeburn  6537:         if ($curr_selected{'srchtype'} eq $option) {
                   6538:             $srchtypesel .= '
                   6539:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   6540:         } else {
                   6541:             $srchtypesel .= '
                   6542:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   6543:         }
                   6544:     }
                   6545:     $srchtypesel .= "\n  </select>\n";
                   6546: 
1.558     albertel 6547:     my ($newuserscript,$new_user_create);
1.556     raeburn  6548: 
                   6549:     if ($forcenewuser) {
1.576     raeburn  6550:         if (ref($srch) eq 'HASH') {
                   6551:             if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $env{'request.role.domain'}) {
1.627     raeburn  6552:                 if ($cancreate) {
                   6553:                     $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>';
                   6554:                 } else {
                   6555:                     my $helplink = ' href="javascript:helpMenu('."'display'".')"';
                   6556:                     my %usertypetext = (
                   6557:                         official   => 'institutional',
                   6558:                         unofficial => 'non-institutional',
                   6559:                     );
                   6560:                     $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 />';
                   6561:                 }
1.576     raeburn  6562:             }
                   6563:         }
                   6564: 
1.556     raeburn  6565:         $newuserscript = <<"ENDSCRIPT";
                   6566: 
1.570     raeburn  6567: function setSearch(createnew,callingForm) {
1.556     raeburn  6568:     if (createnew == 1) {
1.570     raeburn  6569:         for (var i=0; i<callingForm.srchby.length; i++) {
                   6570:             if (callingForm.srchby.options[i].value == 'uname') {
                   6571:                 callingForm.srchby.selectedIndex = i;
1.556     raeburn  6572:             }
                   6573:         }
1.570     raeburn  6574:         for (var i=0; i<callingForm.srchin.length; i++) {
                   6575:             if ( callingForm.srchin.options[i].value == 'dom') {
                   6576: 		callingForm.srchin.selectedIndex = i;
1.556     raeburn  6577:             }
                   6578:         }
1.570     raeburn  6579:         for (var i=0; i<callingForm.srchtype.length; i++) {
                   6580:             if (callingForm.srchtype.options[i].value == 'exact') {
                   6581:                 callingForm.srchtype.selectedIndex = i;
1.556     raeburn  6582:             }
                   6583:         }
1.570     raeburn  6584:         for (var i=0; i<callingForm.srchdomain.length; i++) {
                   6585:             if (callingForm.srchdomain.options[i].value == '$env{'request.role.domain'}') {
                   6586:                 callingForm.srchdomain.selectedIndex = i;
1.556     raeburn  6587:             }
                   6588:         }
                   6589:     }
                   6590: }
                   6591: ENDSCRIPT
1.558     albertel 6592: 
1.556     raeburn  6593:     }
                   6594: 
1.555     raeburn  6595:     my $output = <<"END_BLOCK";
1.556     raeburn  6596: <script type="text/javascript">
1.570     raeburn  6597: function validateEntry(callingForm) {
1.558     albertel 6598: 
1.556     raeburn  6599:     var checkok = 1;
1.558     albertel 6600:     var srchin;
1.570     raeburn  6601:     for (var i=0; i<callingForm.srchin.length; i++) {
                   6602: 	if ( callingForm.srchin[i].checked ) {
                   6603: 	    srchin = callingForm.srchin[i].value;
1.558     albertel 6604: 	}
                   6605:     }
                   6606: 
1.570     raeburn  6607:     var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
                   6608:     var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
                   6609:     var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
                   6610:     var srchterm =  callingForm.srchterm.value;
                   6611:     var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556     raeburn  6612:     var msg = "";
                   6613: 
                   6614:     if (srchterm == "") {
                   6615:         checkok = 0;
1.571     raeburn  6616:         msg += "$lt{'youm'}\\n";
1.556     raeburn  6617:     }
                   6618: 
1.569     raeburn  6619:     if (srchtype== 'begins') {
                   6620:         if (srchterm.length < 2) {
                   6621:             checkok = 0;
1.571     raeburn  6622:             msg += "$lt{'thte'}\\n";
1.569     raeburn  6623:         }
                   6624:     }
                   6625: 
1.556     raeburn  6626:     if (srchtype== 'contains') {
                   6627:         if (srchterm.length < 3) {
                   6628:             checkok = 0;
1.571     raeburn  6629:             msg += "$lt{'thet'}\\n";
1.556     raeburn  6630:         }
                   6631:     }
                   6632:     if (srchin == 'instd') {
                   6633:         if (srchdomain == '') {
                   6634:             checkok = 0;
1.571     raeburn  6635:             msg += "$lt{'yomc'}\\n";
1.556     raeburn  6636:         }
                   6637:     }
                   6638:     if (srchin == 'dom') {
                   6639:         if (srchdomain == '') {
                   6640:             checkok = 0;
1.571     raeburn  6641:             msg += "$lt{'ymcd'}\\n";
1.556     raeburn  6642:         }
                   6643:     }
                   6644:     if (srchby == 'lastfirst') {
                   6645:         if (srchterm.indexOf(",") == -1) {
                   6646:             checkok = 0;
1.571     raeburn  6647:             msg += "$lt{'whus'}\\n";
1.556     raeburn  6648:         }
                   6649:         if (srchterm.indexOf(",") == srchterm.length -1) {
                   6650:             checkok = 0;
1.571     raeburn  6651:             msg += "$lt{'whse'}\\n";
1.556     raeburn  6652:         }
                   6653:     }
                   6654:     if (checkok == 0) {
1.571     raeburn  6655:         alert("$lt{'thfo'}\\n"+msg);
1.556     raeburn  6656:         return;
                   6657:     }
                   6658:     if (checkok == 1) {
1.570     raeburn  6659:         callingForm.submit();
1.556     raeburn  6660:     }
                   6661: }
                   6662: 
                   6663: $newuserscript
                   6664: 
                   6665: </script>
1.558     albertel 6666: 
                   6667: $new_user_create
                   6668: 
1.555     raeburn  6669: <table>
1.558     albertel 6670:  <tr>
1.573     raeburn  6671:   <td>$lt{'doma'}:</td>
                   6672:   <td>$domform</td>
                   6673:   </td>
                   6674:  </tr>
                   6675:  <tr>
                   6676:   <td>$lt{'usr'}:</td>
1.563     raeburn  6677:   <td>$srchbysel
                   6678:       $srchtypesel 
                   6679:       <input type="text" size="15" name="srchterm" value="$srchterm" />
1.564     albertel 6680:       $srchinsel 
1.563     raeburn  6681:   </td>
                   6682:  </tr>
1.555     raeburn  6683: </table>
                   6684: <br />
                   6685: END_BLOCK
1.558     albertel 6686: 
1.555     raeburn  6687:     return $output;
                   6688: }
                   6689: 
1.612     raeburn  6690: sub user_rule_check {
1.615     raeburn  6691:     my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.612     raeburn  6692:     my $response;
                   6693:     if (ref($usershash) eq 'HASH') {
                   6694:         foreach my $user (keys(%{$usershash})) {
                   6695:             my ($uname,$udom) = split(/:/,$user);
                   6696:             next if ($udom eq '' || $uname eq '');
1.615     raeburn  6697:             my ($id,$newuser);
1.612     raeburn  6698:             if (ref($usershash->{$user}) eq 'HASH') {
1.615     raeburn  6699:                 $newuser = $usershash->{$user}->{'newuser'};
1.612     raeburn  6700:                 $id = $usershash->{$user}->{'id'};
                   6701:             }
                   6702:             my $inst_response;
                   6703:             if (ref($checks) eq 'HASH') {
                   6704:                 if (defined($checks->{'username'})) {
1.615     raeburn  6705:                     ($inst_response,%{$inst_results->{$user}}) = 
1.612     raeburn  6706:                         &Apache::lonnet::get_instuser($udom,$uname);
                   6707:                 } elsif (defined($checks->{'id'})) {
1.615     raeburn  6708:                     ($inst_response,%{$inst_results->{$user}}) =
1.612     raeburn  6709:                         &Apache::lonnet::get_instuser($udom,undef,$id);
                   6710:                 }
1.615     raeburn  6711:             } else {
                   6712:                 ($inst_response,%{$inst_results->{$user}}) =
                   6713:                     &Apache::lonnet::get_instuser($udom,$uname);
                   6714:                 return;
1.612     raeburn  6715:             }
1.615     raeburn  6716:             if (!$got_rules->{$udom}) {
1.612     raeburn  6717:                 my %domconfig = &Apache::lonnet::get_dom('configuration',
                   6718:                                                   ['usercreation'],$udom);
                   6719:                 if (ref($domconfig{'usercreation'}) eq 'HASH') {
1.615     raeburn  6720:                     foreach my $item ('username','id') {
1.612     raeburn  6721:                         if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
                   6722:                             $$curr_rules{$udom}{$item} = 
                   6723:                                 $domconfig{'usercreation'}{$item.'_rule'};
1.585     raeburn  6724:                         }
                   6725:                     }
                   6726:                 }
1.615     raeburn  6727:                 $got_rules->{$udom} = 1;  
1.585     raeburn  6728:             }
1.612     raeburn  6729:             foreach my $item (keys(%{$checks})) {
                   6730:                 if (ref($$curr_rules{$udom}) eq 'HASH') {
                   6731:                     if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
                   6732:                         if (@{$$curr_rules{$udom}{$item}} > 0) {
                   6733:                             my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,$$curr_rules{$udom}{$item});
                   6734:                             foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
                   6735:                                 if ($rule_check{$rule}) {
                   6736:                                     $$rulematch{$user}{$item} = $rule;
                   6737:                                     if ($inst_response eq 'ok') {
1.615     raeburn  6738:                                         if (ref($inst_results) eq 'HASH') {
                   6739:                                             if (ref($inst_results->{$user}) eq 'HASH') {
                   6740:                                                 if (keys(%{$inst_results->{$user}}) == 0) {
                   6741:                                                     $$alerts{$item}{$udom}{$uname} = 1;
                   6742:                                                 }
1.612     raeburn  6743:                                             }
                   6744:                                         }
1.615     raeburn  6745:                                     }
                   6746:                                     last;
1.585     raeburn  6747:                                 }
                   6748:                             }
                   6749:                         }
                   6750:                     }
                   6751:                 }
                   6752:             }
                   6753:         }
                   6754:     }
1.612     raeburn  6755:     return;
                   6756: }
                   6757: 
                   6758: sub user_rule_formats {
                   6759:     my ($domain,$domdesc,$curr_rules,$check) = @_;
                   6760:     my %text = ( 
                   6761:                  'username' => 'Usernames',
                   6762:                  'id'       => 'IDs',
                   6763:                );
                   6764:     my $output;
                   6765:     my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
                   6766:     if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
                   6767:         if (@{$ruleorder} > 0) {
                   6768:             $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>';
                   6769:             foreach my $rule (@{$ruleorder}) {
                   6770:                 if (ref($curr_rules) eq 'ARRAY') {
                   6771:                     if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
                   6772:                         if (ref($rules->{$rule}) eq 'HASH') {
                   6773:                             $output .= '<li>'.$rules->{$rule}{'name'}.': '.
                   6774:                                         $rules->{$rule}{'desc'}.'</li>';
                   6775:                         }
                   6776:                     }
                   6777:                 }
                   6778:             }
                   6779:             $output .= '</ul>';
                   6780:         }
                   6781:     }
                   6782:     return $output;
                   6783: }
                   6784: 
                   6785: sub instrule_disallow_msg {
1.615     raeburn  6786:     my ($checkitem,$domdesc,$count,$mode) = @_;
1.612     raeburn  6787:     my $response;
                   6788:     my %text = (
                   6789:                   item   => 'username',
                   6790:                   items  => 'usernames',
                   6791:                   match  => 'matches',
                   6792:                   do     => 'does',
                   6793:                   action => 'a username',
                   6794:                   one    => 'one',
                   6795:                );
                   6796:     if ($count > 1) {
                   6797:         $text{'item'} = 'usernames';
                   6798:         $text{'match'} ='match';
                   6799:         $text{'do'} = 'do';
                   6800:         $text{'action'} = 'usernames',
                   6801:         $text{'one'} = 'ones';
                   6802:     }
                   6803:     if ($checkitem eq 'id') {
                   6804:         $text{'items'} = 'IDs';
                   6805:         $text{'item'} = 'ID';
                   6806:         $text{'action'} = 'an ID';
1.615     raeburn  6807:         if ($count > 1) {
                   6808:             $text{'item'} = 'IDs';
                   6809:             $text{'action'} = 'IDs';
                   6810:         }
1.612     raeburn  6811:     }
                   6812:     $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  6813:     if ($mode eq 'upload') {
                   6814:         if ($checkitem eq 'username') {
                   6815:             $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'}.");
                   6816:         } elsif ($checkitem eq 'id') {
                   6817:             $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.");
                   6818:         }
                   6819:     } else {
                   6820:         if ($checkitem eq 'username') {
                   6821:             $response .= &mt("You must choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
                   6822:         } elsif ($checkitem eq 'id') {
                   6823:             $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.");
                   6824:         }
1.612     raeburn  6825:     }
                   6826:     return $response;
1.585     raeburn  6827: }
                   6828: 
1.624     raeburn  6829: sub personal_data_fieldtitles {
                   6830:     my %fieldtitles = &Apache::lonlocal::texthash (
                   6831:                         id => 'Student/Employee ID',
                   6832:                         permanentemail => 'E-mail address',
                   6833:                         lastname => 'Last Name',
                   6834:                         firstname => 'First Name',
                   6835:                         middlename => 'Middle Name',
                   6836:                         generation => 'Generation',
                   6837:                         gen => 'Generation',
                   6838:                    );
                   6839:     return %fieldtitles;
                   6840: }
                   6841: 
1.642     raeburn  6842: sub sorted_inst_types {
                   6843:     my ($dom) = @_;
                   6844:     my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
                   6845:     my $othertitle = &mt('All users');
                   6846:     if ($env{'request.course.id'}) {
                   6847:         $othertitle  = 'any';
                   6848:     }
                   6849:     my @types;
                   6850:     if (ref($order) eq 'ARRAY') {
                   6851:         @types = @{$order};
                   6852:     }
                   6853:     if (@types == 0) {
                   6854:         if (ref($usertypes) eq 'HASH') {
                   6855:             @types = sort(keys(%{$usertypes}));
                   6856:         }
                   6857:     }
                   6858:     if (keys(%{$usertypes}) > 0) {
                   6859:         $othertitle = &mt('Other users');
                   6860:         if ($env{'request.course.id'}) {
                   6861:             $othertitle = 'other';
                   6862:         }
                   6863:     }
                   6864:     return ($othertitle,$usertypes,\@types);
                   6865: }
                   6866: 
1.645     raeburn  6867: sub get_institutional_codes {
                   6868:     my ($settings,$allcourses,$LC_code) = @_;
                   6869: # Get complete list of course sections to update
                   6870:     my @currsections = ();
                   6871:     my @currxlists = ();
                   6872:     my $coursecode = $$settings{'internal.coursecode'};
                   6873: 
                   6874:     if ($$settings{'internal.sectionnums'} ne '') {
                   6875:         @currsections = split(/,/,$$settings{'internal.sectionnums'});
                   6876:     }
                   6877: 
                   6878:     if ($$settings{'internal.crosslistings'} ne '') {
                   6879:         @currxlists = split(/,/,$$settings{'internal.crosslistings'});
                   6880:     }
                   6881: 
                   6882:     if (@currxlists > 0) {
                   6883:         foreach (@currxlists) {
                   6884:             if (m/^([^:]+):(\w*)$/) {
                   6885:                 unless (grep/^$1$/,@{$allcourses}) {
                   6886:                     push @{$allcourses},$1;
                   6887:                     $$LC_code{$1} = $2;
                   6888:                 }
                   6889:             }
                   6890:         }
                   6891:     }
                   6892:  
                   6893:     if (@currsections > 0) {
                   6894:         foreach (@currsections) {
                   6895:             if (m/^(\w+):(\w*)$/) {
                   6896:                 my $sec = $coursecode.$1;
                   6897:                 my $lc_sec = $2;
                   6898:                 unless (grep/^$sec$/,@{$allcourses}) {
                   6899:                     push @{$allcourses},$sec;
                   6900:                     $$LC_code{$sec} = $lc_sec;
                   6901:                 }
                   6902:             }
                   6903:         }
                   6904:     }
                   6905:     return;
                   6906: }
                   6907: 
1.112     bowersj2 6908: =pod
                   6909: 
1.549     albertel 6910: =back
                   6911: 
                   6912: =head1 HTTP Helpers
                   6913: 
                   6914: =over 4
                   6915: 
1.648     raeburn  6916: =item * &get_unprocessed_cgi($query,$possible_names)
1.112     bowersj2 6917: 
1.258     albertel 6918: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112     bowersj2 6919: $query.  The parameters listed in $possible_names (an array reference),
1.258     albertel 6920: will be set in $env{'form.name'} if they do not already exist.
1.112     bowersj2 6921: 
                   6922: Typically called with $ENV{'QUERY_STRING'} as the first parameter.  
                   6923: $possible_names is an ref to an array of form element names.  As an example:
                   6924: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258     albertel 6925: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112     bowersj2 6926: 
                   6927: =cut
1.1       albertel 6928: 
1.6       albertel 6929: sub get_unprocessed_cgi {
1.25      albertel 6930:   my ($query,$possible_names)= @_;
1.26      matthew  6931:   # $Apache::lonxml::debug=1;
1.356     albertel 6932:   foreach my $pair (split(/&/,$query)) {
                   6933:     my ($name, $value) = split(/=/,$pair);
1.369     www      6934:     $name = &unescape($name);
1.25      albertel 6935:     if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
                   6936:       $value =~ tr/+/ /;
                   6937:       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258     albertel 6938:       unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25      albertel 6939:     }
1.16      harris41 6940:   }
1.6       albertel 6941: }
                   6942: 
1.112     bowersj2 6943: =pod
                   6944: 
1.648     raeburn  6945: =item * &cacheheader() 
1.112     bowersj2 6946: 
                   6947: returns cache-controlling header code
                   6948: 
                   6949: =cut
                   6950: 
1.7       albertel 6951: sub cacheheader {
1.258     albertel 6952:     unless ($env{'request.method'} eq 'GET') { return ''; }
1.216     albertel 6953:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
                   6954:     my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7       albertel 6955:                 <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
                   6956:                 <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216     albertel 6957:     return $output;
1.7       albertel 6958: }
                   6959: 
1.112     bowersj2 6960: =pod
                   6961: 
1.648     raeburn  6962: =item * &no_cache($r) 
1.112     bowersj2 6963: 
                   6964: specifies header code to not have cache
                   6965: 
                   6966: =cut
                   6967: 
1.9       albertel 6968: sub no_cache {
1.216     albertel 6969:     my ($r) = @_;
                   6970:     if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258     albertel 6971: 	$env{'request.method'} ne 'GET') { return ''; }
1.216     albertel 6972:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
                   6973:     $r->no_cache(1);
                   6974:     $r->header_out("Expires" => $date);
                   6975:     $r->header_out("Pragma" => "no-cache");
1.123     www      6976: }
                   6977: 
                   6978: sub content_type {
1.181     albertel 6979:     my ($r,$type,$charset) = @_;
1.299     foxr     6980:     if ($r) {
                   6981: 	#  Note that printout.pl calls this with undef for $r.
                   6982: 	&no_cache($r);
                   6983:     }
1.258     albertel 6984:     if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181     albertel 6985:     unless ($charset) {
                   6986: 	$charset=&Apache::lonlocal::current_encoding;
                   6987:     }
                   6988:     if ($charset) { $type.='; charset='.$charset; }
                   6989:     if ($r) {
                   6990: 	$r->content_type($type);
                   6991:     } else {
                   6992: 	print("Content-type: $type\n\n");
                   6993:     }
1.9       albertel 6994: }
1.25      albertel 6995: 
1.112     bowersj2 6996: =pod
                   6997: 
1.648     raeburn  6998: =item * &add_to_env($name,$value) 
1.112     bowersj2 6999: 
1.258     albertel 7000: adds $name to the %env hash with value
1.112     bowersj2 7001: $value, if $name already exists, the entry is converted to an array
                   7002: reference and $value is added to the array.
                   7003: 
                   7004: =cut
                   7005: 
1.25      albertel 7006: sub add_to_env {
                   7007:   my ($name,$value)=@_;
1.258     albertel 7008:   if (defined($env{$name})) {
                   7009:     if (ref($env{$name})) {
1.25      albertel 7010:       #already have multiple values
1.258     albertel 7011:       push(@{ $env{$name} },$value);
1.25      albertel 7012:     } else {
                   7013:       #first time seeing multiple values, convert hash entry to an arrayref
1.258     albertel 7014:       my $first=$env{$name};
                   7015:       undef($env{$name});
                   7016:       push(@{ $env{$name} },$first,$value);
1.25      albertel 7017:     }
                   7018:   } else {
1.258     albertel 7019:     $env{$name}=$value;
1.25      albertel 7020:   }
1.31      albertel 7021: }
1.149     albertel 7022: 
                   7023: =pod
                   7024: 
1.648     raeburn  7025: =item * &get_env_multiple($name) 
1.149     albertel 7026: 
1.258     albertel 7027: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149     albertel 7028: values may be defined and end up as an array ref.
                   7029: 
                   7030: returns an array of values
                   7031: 
                   7032: =cut
                   7033: 
                   7034: sub get_env_multiple {
                   7035:     my ($name) = @_;
                   7036:     my @values;
1.258     albertel 7037:     if (defined($env{$name})) {
1.149     albertel 7038:         # exists is it an array
1.258     albertel 7039:         if (ref($env{$name})) {
                   7040:             @values=@{ $env{$name} };
1.149     albertel 7041:         } else {
1.258     albertel 7042:             $values[0]=$env{$name};
1.149     albertel 7043:         }
                   7044:     }
                   7045:     return(@values);
                   7046: }
                   7047: 
1.31      albertel 7048: 
1.41      ng       7049: =pod
1.45      matthew  7050: 
1.464     albertel 7051: =back
1.41      ng       7052: 
1.112     bowersj2 7053: =head1 CSV Upload/Handling functions
1.38      albertel 7054: 
1.41      ng       7055: =over 4
                   7056: 
1.648     raeburn  7057: =item * &upfile_store($r)
1.41      ng       7058: 
                   7059: Store uploaded file, $r should be the HTTP Request object,
1.258     albertel 7060: needs $env{'form.upfile'}
1.41      ng       7061: returns $datatoken to be put into hidden field
                   7062: 
                   7063: =cut
1.31      albertel 7064: 
                   7065: sub upfile_store {
                   7066:     my $r=shift;
1.258     albertel 7067:     $env{'form.upfile'}=~s/\r/\n/gs;
                   7068:     $env{'form.upfile'}=~s/\f/\n/gs;
                   7069:     $env{'form.upfile'}=~s/\n+/\n/gs;
                   7070:     $env{'form.upfile'}=~s/\n+$//gs;
1.31      albertel 7071: 
1.258     albertel 7072:     my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
                   7073: 	'_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31      albertel 7074:     {
1.158     raeburn  7075:         my $datafile = $r->dir_config('lonDaemons').
                   7076:                            '/tmp/'.$datatoken.'.tmp';
                   7077:         if ( open(my $fh,">$datafile") ) {
1.258     albertel 7078:             print $fh $env{'form.upfile'};
1.158     raeburn  7079:             close($fh);
                   7080:         }
1.31      albertel 7081:     }
                   7082:     return $datatoken;
                   7083: }
                   7084: 
1.56      matthew  7085: =pod
                   7086: 
1.648     raeburn  7087: =item * &load_tmp_file($r)
1.41      ng       7088: 
                   7089: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258     albertel 7090: needs $env{'form.datatoken'},
                   7091: sets $env{'form.upfile'} to the contents of the file
1.41      ng       7092: 
                   7093: =cut
1.31      albertel 7094: 
                   7095: sub load_tmp_file {
                   7096:     my $r=shift;
                   7097:     my @studentdata=();
                   7098:     {
1.158     raeburn  7099:         my $studentfile = $r->dir_config('lonDaemons').
1.258     albertel 7100:                               '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158     raeburn  7101:         if ( open(my $fh,"<$studentfile") ) {
                   7102:             @studentdata=<$fh>;
                   7103:             close($fh);
                   7104:         }
1.31      albertel 7105:     }
1.258     albertel 7106:     $env{'form.upfile'}=join('',@studentdata);
1.31      albertel 7107: }
                   7108: 
1.56      matthew  7109: =pod
                   7110: 
1.648     raeburn  7111: =item * &upfile_record_sep()
1.41      ng       7112: 
                   7113: Separate uploaded file into records
                   7114: returns array of records,
1.258     albertel 7115: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41      ng       7116: 
                   7117: =cut
1.31      albertel 7118: 
                   7119: sub upfile_record_sep {
1.258     albertel 7120:     if ($env{'form.upfiletype'} eq 'xml') {
1.31      albertel 7121:     } else {
1.248     albertel 7122: 	my @records;
1.258     albertel 7123: 	foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248     albertel 7124: 	    if ($line=~/^\s*$/) { next; }
                   7125: 	    push(@records,$line);
                   7126: 	}
                   7127: 	return @records;
1.31      albertel 7128:     }
                   7129: }
                   7130: 
1.56      matthew  7131: =pod
                   7132: 
1.648     raeburn  7133: =item * &record_sep($record)
1.41      ng       7134: 
1.258     albertel 7135: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41      ng       7136: 
                   7137: =cut
                   7138: 
1.263     www      7139: sub takeleft {
                   7140:     my $index=shift;
                   7141:     return substr('0000'.$index,-4,4);
                   7142: }
                   7143: 
1.31      albertel 7144: sub record_sep {
                   7145:     my $record=shift;
                   7146:     my %components=();
1.258     albertel 7147:     if ($env{'form.upfiletype'} eq 'xml') {
                   7148:     } elsif ($env{'form.upfiletype'} eq 'space') {
1.31      albertel 7149:         my $i=0;
1.356     albertel 7150:         foreach my $field (split(/\s+/,$record)) {
1.31      albertel 7151:             $field=~s/^(\"|\')//;
                   7152:             $field=~s/(\"|\')$//;
1.263     www      7153:             $components{&takeleft($i)}=$field;
1.31      albertel 7154:             $i++;
                   7155:         }
1.258     albertel 7156:     } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31      albertel 7157:         my $i=0;
1.356     albertel 7158:         foreach my $field (split(/\t/,$record)) {
1.31      albertel 7159:             $field=~s/^(\"|\')//;
                   7160:             $field=~s/(\"|\')$//;
1.263     www      7161:             $components{&takeleft($i)}=$field;
1.31      albertel 7162:             $i++;
                   7163:         }
                   7164:     } else {
1.561     www      7165:         my $separator=',';
1.480     banghart 7166:         if ($env{'form.upfiletype'} eq 'semisv') {
1.561     www      7167:             $separator=';';
1.480     banghart 7168:         }
1.31      albertel 7169:         my $i=0;
1.561     www      7170: # the character we are looking for to indicate the end of a quote or a record 
                   7171:         my $looking_for=$separator;
                   7172: # do not add the characters to the fields
                   7173:         my $ignore=0;
                   7174: # we just encountered a separator (or the beginning of the record)
                   7175:         my $just_found_separator=1;
                   7176: # store the field we are working on here
                   7177:         my $field='';
                   7178: # work our way through all characters in record
                   7179:         foreach my $character ($record=~/(.)/g) {
                   7180:             if ($character eq $looking_for) {
                   7181:                if ($character ne $separator) {
                   7182: # Found the end of a quote, again looking for separator
                   7183:                   $looking_for=$separator;
                   7184:                   $ignore=1;
                   7185:                } else {
                   7186: # Found a separator, store away what we got
                   7187:                   $components{&takeleft($i)}=$field;
                   7188: 	          $i++;
                   7189:                   $just_found_separator=1;
                   7190:                   $ignore=0;
                   7191:                   $field='';
                   7192:                }
                   7193:                next;
                   7194:             }
                   7195: # single or double quotation marks after a separator indicate beginning of a quote
                   7196: # we are now looking for the end of the quote and need to ignore separators
                   7197:             if ((($character eq '"') || ($character eq "'")) && ($just_found_separator))  {
                   7198:                $looking_for=$character;
                   7199:                next;
                   7200:             }
                   7201: # ignore would be true after we reached the end of a quote
                   7202:             if ($ignore) { next; }
                   7203:             if (($just_found_separator) && ($character=~/\s/)) { next; }
                   7204:             $field.=$character;
                   7205:             $just_found_separator=0; 
1.31      albertel 7206:         }
1.561     www      7207: # catch the very last entry, since we never encountered the separator
                   7208:         $components{&takeleft($i)}=$field;
1.31      albertel 7209:     }
                   7210:     return %components;
                   7211: }
                   7212: 
1.144     matthew  7213: ######################################################
                   7214: ######################################################
                   7215: 
1.56      matthew  7216: =pod
                   7217: 
1.648     raeburn  7218: =item * &upfile_select_html()
1.41      ng       7219: 
1.144     matthew  7220: Return HTML code to select a file from the users machine and specify 
                   7221: the file type.
1.41      ng       7222: 
                   7223: =cut
                   7224: 
1.144     matthew  7225: ######################################################
                   7226: ######################################################
1.31      albertel 7227: sub upfile_select_html {
1.144     matthew  7228:     my %Types = (
                   7229:                  csv   => &mt('CSV (comma separated values, spreadsheet)'),
1.480     banghart 7230:                  semisv => &mt('Semicolon separated values'),
1.144     matthew  7231:                  space => &mt('Space separated'),
                   7232:                  tab   => &mt('Tabulator separated'),
                   7233: #                 xml   => &mt('HTML/XML'),
                   7234:                  );
                   7235:     my $Str = '<input type="file" name="upfile" size="50" />'.
                   7236:         '<br />Type: <select name="upfiletype">';
                   7237:     foreach my $type (sort(keys(%Types))) {
                   7238:         $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
                   7239:     }
                   7240:     $Str .= "</select>\n";
                   7241:     return $Str;
1.31      albertel 7242: }
                   7243: 
1.301     albertel 7244: sub get_samples {
                   7245:     my ($records,$toget) = @_;
                   7246:     my @samples=({});
                   7247:     my $got=0;
                   7248:     foreach my $rec (@$records) {
                   7249: 	my %temp = &record_sep($rec);
                   7250: 	if (! grep(/\S/, values(%temp))) { next; }
                   7251: 	if (%temp) {
                   7252: 	    $samples[$got]=\%temp;
                   7253: 	    $got++;
                   7254: 	    if ($got == $toget) { last; }
                   7255: 	}
                   7256:     }
                   7257:     return \@samples;
                   7258: }
                   7259: 
1.144     matthew  7260: ######################################################
                   7261: ######################################################
                   7262: 
1.56      matthew  7263: =pod
                   7264: 
1.648     raeburn  7265: =item * &csv_print_samples($r,$records)
1.41      ng       7266: 
                   7267: Prints a table of sample values from each column uploaded $r is an
                   7268: Apache Request ref, $records is an arrayref from
                   7269: &Apache::loncommon::upfile_record_sep
                   7270: 
                   7271: =cut
                   7272: 
1.144     matthew  7273: ######################################################
                   7274: ######################################################
1.31      albertel 7275: sub csv_print_samples {
                   7276:     my ($r,$records) = @_;
1.301     albertel 7277:     my $samples = &get_samples($records,3);
                   7278: 
1.594     raeburn  7279:     $r->print(&mt('Samples').'<br />'.&start_data_table().
                   7280:               &start_data_table_header_row());
1.356     albertel 7281:     foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { 
                   7282:         $r->print('<th>'.&mt('Column&nbsp;[_1]',($sample+1)).'</th>'); }
1.594     raeburn  7283:     $r->print(&end_data_table_header_row());
1.301     albertel 7284:     foreach my $hash (@$samples) {
1.594     raeburn  7285: 	$r->print(&start_data_table_row());
1.356     albertel 7286: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31      albertel 7287: 	    $r->print('<td>');
1.356     albertel 7288: 	    if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31      albertel 7289: 	    $r->print('</td>');
                   7290: 	}
1.594     raeburn  7291: 	$r->print(&end_data_table_row());
1.31      albertel 7292:     }
1.594     raeburn  7293:     $r->print(&end_data_table().'<br />'."\n");
1.31      albertel 7294: }
                   7295: 
1.144     matthew  7296: ######################################################
                   7297: ######################################################
                   7298: 
1.56      matthew  7299: =pod
                   7300: 
1.648     raeburn  7301: =item * &csv_print_select_table($r,$records,$d)
1.41      ng       7302: 
                   7303: Prints a table to create associations between values and table columns.
1.144     matthew  7304: 
1.41      ng       7305: $r is an Apache Request ref,
                   7306: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174     matthew  7307: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41      ng       7308: 
                   7309: =cut
                   7310: 
1.144     matthew  7311: ######################################################
                   7312: ######################################################
1.31      albertel 7313: sub csv_print_select_table {
                   7314:     my ($r,$records,$d) = @_;
1.301     albertel 7315:     my $i=0;
                   7316:     my $samples = &get_samples($records,1);
1.144     matthew  7317:     $r->print(&mt('Associate columns with student attributes.')."\n".
1.594     raeburn  7318: 	      &start_data_table().&start_data_table_header_row().
1.144     matthew  7319:               '<th>'.&mt('Attribute').'</th>'.
1.594     raeburn  7320:               '<th>'.&mt('Column').'</th>'.
                   7321:               &end_data_table_header_row()."\n");
1.356     albertel 7322:     foreach my $array_ref (@$d) {
                   7323: 	my ($value,$display,$defaultcol)=@{ $array_ref };
1.594     raeburn  7324: 	$r->print(&start_data_table_row().'<tr><td>'.$display.'</td>');
1.31      albertel 7325: 
                   7326: 	$r->print('<td><select name=f'.$i.
1.32      matthew  7327: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 7328: 	$r->print('<option value="none"></option>');
1.356     albertel 7329: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
                   7330: 	    $r->print('<option value="'.$sample.'"'.
                   7331:                       ($sample eq $defaultcol ? ' selected="selected" ' : '').
                   7332:                       '>Column '.($sample+1).'</option>');
1.31      albertel 7333: 	}
1.594     raeburn  7334: 	$r->print('</select></td>'.&end_data_table_row()."\n");
1.31      albertel 7335: 	$i++;
                   7336:     }
1.594     raeburn  7337:     $r->print(&end_data_table());
1.31      albertel 7338:     $i--;
                   7339:     return $i;
                   7340: }
1.56      matthew  7341: 
1.144     matthew  7342: ######################################################
                   7343: ######################################################
                   7344: 
1.56      matthew  7345: =pod
1.31      albertel 7346: 
1.648     raeburn  7347: =item * &csv_samples_select_table($r,$records,$d)
1.41      ng       7348: 
                   7349: Prints a table of sample values from the upload and can make associate samples to internal names.
                   7350: 
                   7351: $r is an Apache Request ref,
                   7352: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
                   7353: $d is an array of 2 element arrays (internal name, displayed name)
                   7354: 
                   7355: =cut
                   7356: 
1.144     matthew  7357: ######################################################
                   7358: ######################################################
1.31      albertel 7359: sub csv_samples_select_table {
                   7360:     my ($r,$records,$d) = @_;
                   7361:     my $i=0;
1.144     matthew  7362:     #
1.301     albertel 7363:     my $samples = &get_samples($records,3);
1.594     raeburn  7364:     $r->print(&start_data_table().
                   7365:               &start_data_table_header_row().'<th>'.
                   7366:               &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
                   7367:               &end_data_table_header_row());
1.301     albertel 7368: 
                   7369:     foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594     raeburn  7370: 	$r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32      matthew  7371: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.301     albertel 7372: 	foreach my $option (@$d) {
                   7373: 	    my ($value,$display,$defaultcol)=@{ $option };
1.174     matthew  7374: 	    $r->print('<option value="'.$value.'"'.
1.253     albertel 7375:                       ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174     matthew  7376:                       $display.'</option>');
1.31      albertel 7377: 	}
                   7378: 	$r->print('</select></td><td>');
1.301     albertel 7379: 	foreach my $line (0..2) {
                   7380: 	    if (defined($samples->[$line]{$key})) { 
                   7381: 		$r->print($samples->[$line]{$key}."<br />\n"); 
                   7382: 	    }
                   7383: 	}
1.594     raeburn  7384: 	$r->print('</td>'.&end_data_table_row());
1.31      albertel 7385: 	$i++;
                   7386:     }
1.594     raeburn  7387:     $r->print(&end_data_table());
1.31      albertel 7388:     $i--;
                   7389:     return($i);
1.115     matthew  7390: }
                   7391: 
1.144     matthew  7392: ######################################################
                   7393: ######################################################
                   7394: 
1.115     matthew  7395: =pod
                   7396: 
1.648     raeburn  7397: =item * &clean_excel_name($name)
1.115     matthew  7398: 
                   7399: Returns a replacement for $name which does not contain any illegal characters.
                   7400: 
                   7401: =cut
                   7402: 
1.144     matthew  7403: ######################################################
                   7404: ######################################################
1.115     matthew  7405: sub clean_excel_name {
                   7406:     my ($name) = @_;
                   7407:     $name =~ s/[:\*\?\/\\]//g;
                   7408:     if (length($name) > 31) {
                   7409:         $name = substr($name,0,31);
                   7410:     }
                   7411:     return $name;
1.25      albertel 7412: }
1.84      albertel 7413: 
1.85      albertel 7414: =pod
                   7415: 
1.648     raeburn  7416: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85      albertel 7417: 
                   7418: Returns either 1 or undef
                   7419: 
                   7420: 1 if the part is to be hidden, undef if it is to be shown
                   7421: 
                   7422: Arguments are:
                   7423: 
                   7424: $id the id of the part to be checked
                   7425: $symb, optional the symb of the resource to check
                   7426: $udom, optional the domain of the user to check for
                   7427: $uname, optional the username of the user to check for
                   7428: 
                   7429: =cut
1.84      albertel 7430: 
                   7431: sub check_if_partid_hidden {
                   7432:     my ($id,$symb,$udom,$uname) = @_;
1.133     albertel 7433:     my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84      albertel 7434: 					 $symb,$udom,$uname);
1.141     albertel 7435:     my $truth=1;
                   7436:     #if the string starts with !, then the list is the list to show not hide
                   7437:     if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84      albertel 7438:     my @hiddenlist=split(/,/,$hiddenparts);
                   7439:     foreach my $checkid (@hiddenlist) {
1.141     albertel 7440: 	if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84      albertel 7441:     }
1.141     albertel 7442:     return !$truth;
1.84      albertel 7443: }
1.127     matthew  7444: 
1.138     matthew  7445: 
                   7446: ############################################################
                   7447: ############################################################
                   7448: 
                   7449: =pod
                   7450: 
1.157     matthew  7451: =back 
                   7452: 
1.138     matthew  7453: =head1 cgi-bin script and graphing routines
                   7454: 
1.157     matthew  7455: =over 4
                   7456: 
1.648     raeburn  7457: =item * &get_cgi_id()
1.138     matthew  7458: 
                   7459: Inputs: none
                   7460: 
                   7461: Returns an id which can be used to pass environment variables
                   7462: to various cgi-bin scripts.  These environment variables will
                   7463: be removed from the users environment after a given time by
                   7464: the routine &Apache::lonnet::transfer_profile_to_env.
                   7465: 
                   7466: =cut
                   7467: 
                   7468: ############################################################
                   7469: ############################################################
1.152     albertel 7470: my $uniq=0;
1.136     matthew  7471: sub get_cgi_id {
1.154     albertel 7472:     $uniq=($uniq+1)%100000;
1.280     albertel 7473:     return (time.'_'.$$.'_'.$uniq);
1.136     matthew  7474: }
                   7475: 
1.127     matthew  7476: ############################################################
                   7477: ############################################################
                   7478: 
                   7479: =pod
                   7480: 
1.648     raeburn  7481: =item * &DrawBarGraph()
1.127     matthew  7482: 
1.138     matthew  7483: Facilitates the plotting of data in a (stacked) bar graph.
                   7484: Puts plot definition data into the users environment in order for 
                   7485: graph.png to plot it.  Returns an <img> tag for the plot.
                   7486: The bars on the plot are labeled '1','2',...,'n'.
                   7487: 
                   7488: Inputs:
                   7489: 
                   7490: =over 4
                   7491: 
                   7492: =item $Title: string, the title of the plot
                   7493: 
                   7494: =item $xlabel: string, text describing the X-axis of the plot
                   7495: 
                   7496: =item $ylabel: string, text describing the Y-axis of the plot
                   7497: 
                   7498: =item $Max: scalar, the maximum Y value to use in the plot
                   7499: If $Max is < any data point, the graph will not be rendered.
                   7500: 
1.140     matthew  7501: =item $colors: array ref holding the colors to be used for the data sets when
1.138     matthew  7502: they are plotted.  If undefined, default values will be used.
                   7503: 
1.178     matthew  7504: =item $labels: array ref holding the labels to use on the x-axis for the bars.
                   7505: 
1.138     matthew  7506: =item @Values: An array of array references.  Each array reference holds data
                   7507: to be plotted in a stacked bar chart.
                   7508: 
1.239     matthew  7509: =item If the final element of @Values is a hash reference the key/value
                   7510: pairs will be added to the graph definition.
                   7511: 
1.138     matthew  7512: =back
                   7513: 
                   7514: Returns:
                   7515: 
                   7516: An <img> tag which references graph.png and the appropriate identifying
                   7517: information for the plot.
                   7518: 
1.127     matthew  7519: =cut
                   7520: 
                   7521: ############################################################
                   7522: ############################################################
1.134     matthew  7523: sub DrawBarGraph {
1.178     matthew  7524:     my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134     matthew  7525:     #
                   7526:     if (! defined($colors)) {
                   7527:         $colors = ['#33ff00', 
                   7528:                   '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
                   7529:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                   7530:                   ]; 
                   7531:     }
1.228     matthew  7532:     my $extra_settings = {};
                   7533:     if (ref($Values[-1]) eq 'HASH') {
                   7534:         $extra_settings = pop(@Values);
                   7535:     }
1.127     matthew  7536:     #
1.136     matthew  7537:     my $identifier = &get_cgi_id();
                   7538:     my $id = 'cgi.'.$identifier;        
1.129     matthew  7539:     if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127     matthew  7540:         return '';
                   7541:     }
1.225     matthew  7542:     #
                   7543:     my @Labels;
                   7544:     if (defined($labels)) {
                   7545:         @Labels = @$labels;
                   7546:     } else {
                   7547:         for (my $i=0;$i<@{$Values[0]};$i++) {
                   7548:             push (@Labels,$i+1);
                   7549:         }
                   7550:     }
                   7551:     #
1.129     matthew  7552:     my $NumBars = scalar(@{$Values[0]});
1.225     matthew  7553:     if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129     matthew  7554:     my %ValuesHash;
                   7555:     my $NumSets=1;
                   7556:     foreach my $array (@Values) {
                   7557:         next if (! ref($array));
1.136     matthew  7558:         $ValuesHash{$id.'.data.'.$NumSets++} = 
1.132     matthew  7559:             join(',',@$array);
1.129     matthew  7560:     }
1.127     matthew  7561:     #
1.136     matthew  7562:     my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225     matthew  7563:     if ($NumBars < 3) {
                   7564:         $width = 120+$NumBars*32;
1.220     matthew  7565:         $xskip = 1;
1.225     matthew  7566:         $bar_width = 30;
                   7567:     } elsif ($NumBars < 5) {
                   7568:         $width = 120+$NumBars*20;
                   7569:         $xskip = 1;
                   7570:         $bar_width = 20;
1.220     matthew  7571:     } elsif ($NumBars < 10) {
1.136     matthew  7572:         $width = 120+$NumBars*15;
                   7573:         $xskip = 1;
                   7574:         $bar_width = 15;
                   7575:     } elsif ($NumBars <= 25) {
                   7576:         $width = 120+$NumBars*11;
                   7577:         $xskip = 5;
                   7578:         $bar_width = 8;
                   7579:     } elsif ($NumBars <= 50) {
                   7580:         $width = 120+$NumBars*8;
                   7581:         $xskip = 5;
                   7582:         $bar_width = 4;
                   7583:     } else {
                   7584:         $width = 120+$NumBars*8;
                   7585:         $xskip = 5;
                   7586:         $bar_width = 4;
                   7587:     }
                   7588:     #
1.137     matthew  7589:     $Max = 1 if ($Max < 1);
                   7590:     if ( int($Max) < $Max ) {
                   7591:         $Max++;
                   7592:         $Max = int($Max);
                   7593:     }
1.127     matthew  7594:     $Title  = '' if (! defined($Title));
                   7595:     $xlabel = '' if (! defined($xlabel));
                   7596:     $ylabel = '' if (! defined($ylabel));
1.369     www      7597:     $ValuesHash{$id.'.title'}    = &escape($Title);
                   7598:     $ValuesHash{$id.'.xlabel'}   = &escape($xlabel);
                   7599:     $ValuesHash{$id.'.ylabel'}   = &escape($ylabel);
1.137     matthew  7600:     $ValuesHash{$id.'.y_max_value'} = $Max;
1.136     matthew  7601:     $ValuesHash{$id.'.NumBars'}  = $NumBars;
                   7602:     $ValuesHash{$id.'.NumSets'}  = $NumSets;
                   7603:     $ValuesHash{$id.'.PlotType'} = 'bar';
                   7604:     $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   7605:     $ValuesHash{$id.'.height'}   = $height;
                   7606:     $ValuesHash{$id.'.width'}    = $width;
                   7607:     $ValuesHash{$id.'.xskip'}    = $xskip;
                   7608:     $ValuesHash{$id.'.bar_width'} = $bar_width;
                   7609:     $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127     matthew  7610:     #
1.228     matthew  7611:     # Deal with other parameters
                   7612:     while (my ($key,$value) = each(%$extra_settings)) {
                   7613:         $ValuesHash{$id.'.'.$key} = $value;
                   7614:     }
                   7615:     #
1.646     raeburn  7616:     &Apache::lonnet::appenv(\%ValuesHash);
1.137     matthew  7617:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   7618: }
                   7619: 
                   7620: ############################################################
                   7621: ############################################################
                   7622: 
                   7623: =pod
                   7624: 
1.648     raeburn  7625: =item * &DrawXYGraph()
1.137     matthew  7626: 
1.138     matthew  7627: Facilitates the plotting of data in an XY graph.
                   7628: Puts plot definition data into the users environment in order for 
                   7629: graph.png to plot it.  Returns an <img> tag for the plot.
                   7630: 
                   7631: Inputs:
                   7632: 
                   7633: =over 4
                   7634: 
                   7635: =item $Title: string, the title of the plot
                   7636: 
                   7637: =item $xlabel: string, text describing the X-axis of the plot
                   7638: 
                   7639: =item $ylabel: string, text describing the Y-axis of the plot
                   7640: 
                   7641: =item $Max: scalar, the maximum Y value to use in the plot
                   7642: If $Max is < any data point, the graph will not be rendered.
                   7643: 
                   7644: =item $colors: Array ref containing the hex color codes for the data to be 
                   7645: plotted in.  If undefined, default values will be used.
                   7646: 
                   7647: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   7648: 
                   7649: =item $Ydata: Array ref containing Array refs.  
1.185     www      7650: Each of the contained arrays will be plotted as a separate curve.
1.138     matthew  7651: 
                   7652: =item %Values: hash indicating or overriding any default values which are 
                   7653: passed to graph.png.  
                   7654: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   7655: 
                   7656: =back
                   7657: 
                   7658: Returns:
                   7659: 
                   7660: An <img> tag which references graph.png and the appropriate identifying
                   7661: information for the plot.
                   7662: 
1.137     matthew  7663: =cut
                   7664: 
                   7665: ############################################################
                   7666: ############################################################
                   7667: sub DrawXYGraph {
                   7668:     my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
                   7669:     #
                   7670:     # Create the identifier for the graph
                   7671:     my $identifier = &get_cgi_id();
                   7672:     my $id = 'cgi.'.$identifier;
                   7673:     #
                   7674:     $Title  = '' if (! defined($Title));
                   7675:     $xlabel = '' if (! defined($xlabel));
                   7676:     $ylabel = '' if (! defined($ylabel));
                   7677:     my %ValuesHash = 
                   7678:         (
1.369     www      7679:          $id.'.title'  => &escape($Title),
                   7680:          $id.'.xlabel' => &escape($xlabel),
                   7681:          $id.'.ylabel' => &escape($ylabel),
1.137     matthew  7682:          $id.'.y_max_value'=> $Max,
                   7683:          $id.'.labels'     => join(',',@$Xlabels),
                   7684:          $id.'.PlotType'   => 'XY',
                   7685:          );
                   7686:     #
                   7687:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   7688:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   7689:     }
                   7690:     #
                   7691:     if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
                   7692:         return '';
                   7693:     }
                   7694:     my $NumSets=1;
1.138     matthew  7695:     foreach my $array (@{$Ydata}){
1.137     matthew  7696:         next if (! ref($array));
                   7697:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
                   7698:     }
1.138     matthew  7699:     $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137     matthew  7700:     #
                   7701:     # Deal with other parameters
                   7702:     while (my ($key,$value) = each(%Values)) {
                   7703:         $ValuesHash{$id.'.'.$key} = $value;
1.127     matthew  7704:     }
                   7705:     #
1.646     raeburn  7706:     &Apache::lonnet::appenv(\%ValuesHash);
1.136     matthew  7707:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   7708: }
                   7709: 
                   7710: ############################################################
                   7711: ############################################################
                   7712: 
                   7713: =pod
                   7714: 
1.648     raeburn  7715: =item * &DrawXYYGraph()
1.138     matthew  7716: 
                   7717: Facilitates the plotting of data in an XY graph with two Y axes.
                   7718: Puts plot definition data into the users environment in order for 
                   7719: graph.png to plot it.  Returns an <img> tag for the plot.
                   7720: 
                   7721: Inputs:
                   7722: 
                   7723: =over 4
                   7724: 
                   7725: =item $Title: string, the title of the plot
                   7726: 
                   7727: =item $xlabel: string, text describing the X-axis of the plot
                   7728: 
                   7729: =item $ylabel: string, text describing the Y-axis of the plot
                   7730: 
                   7731: =item $colors: Array ref containing the hex color codes for the data to be 
                   7732: plotted in.  If undefined, default values will be used.
                   7733: 
                   7734: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   7735: 
                   7736: =item $Ydata1: The first data set
                   7737: 
                   7738: =item $Min1: The minimum value of the left Y-axis
                   7739: 
                   7740: =item $Max1: The maximum value of the left Y-axis
                   7741: 
                   7742: =item $Ydata2: The second data set
                   7743: 
                   7744: =item $Min2: The minimum value of the right Y-axis
                   7745: 
                   7746: =item $Max2: The maximum value of the left Y-axis
                   7747: 
                   7748: =item %Values: hash indicating or overriding any default values which are 
                   7749: passed to graph.png.  
                   7750: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   7751: 
                   7752: =back
                   7753: 
                   7754: Returns:
                   7755: 
                   7756: An <img> tag which references graph.png and the appropriate identifying
                   7757: information for the plot.
1.136     matthew  7758: 
                   7759: =cut
                   7760: 
                   7761: ############################################################
                   7762: ############################################################
1.137     matthew  7763: sub DrawXYYGraph {
                   7764:     my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
                   7765:                                         $Ydata2,$Min2,$Max2,%Values)=@_;
1.136     matthew  7766:     #
                   7767:     # Create the identifier for the graph
                   7768:     my $identifier = &get_cgi_id();
                   7769:     my $id = 'cgi.'.$identifier;
                   7770:     #
                   7771:     $Title  = '' if (! defined($Title));
                   7772:     $xlabel = '' if (! defined($xlabel));
                   7773:     $ylabel = '' if (! defined($ylabel));
                   7774:     my %ValuesHash = 
                   7775:         (
1.369     www      7776:          $id.'.title'  => &escape($Title),
                   7777:          $id.'.xlabel' => &escape($xlabel),
                   7778:          $id.'.ylabel' => &escape($ylabel),
1.136     matthew  7779:          $id.'.labels' => join(',',@$Xlabels),
                   7780:          $id.'.PlotType' => 'XY',
                   7781:          $id.'.NumSets' => 2,
1.137     matthew  7782:          $id.'.two_axes' => 1,
                   7783:          $id.'.y1_max_value' => $Max1,
                   7784:          $id.'.y1_min_value' => $Min1,
                   7785:          $id.'.y2_max_value' => $Max2,
                   7786:          $id.'.y2_min_value' => $Min2,
1.136     matthew  7787:          );
                   7788:     #
1.137     matthew  7789:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   7790:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   7791:     }
                   7792:     #
                   7793:     if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
                   7794:         ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136     matthew  7795:         return '';
                   7796:     }
                   7797:     my $NumSets=1;
1.137     matthew  7798:     foreach my $array ($Ydata1,$Ydata2){
1.136     matthew  7799:         next if (! ref($array));
                   7800:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137     matthew  7801:     }
                   7802:     #
                   7803:     # Deal with other parameters
                   7804:     while (my ($key,$value) = each(%Values)) {
                   7805:         $ValuesHash{$id.'.'.$key} = $value;
1.136     matthew  7806:     }
                   7807:     #
1.646     raeburn  7808:     &Apache::lonnet::appenv(\%ValuesHash);
1.130     albertel 7809:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139     matthew  7810: }
                   7811: 
                   7812: ############################################################
                   7813: ############################################################
                   7814: 
                   7815: =pod
                   7816: 
1.157     matthew  7817: =back 
                   7818: 
1.139     matthew  7819: =head1 Statistics helper routines?  
                   7820: 
                   7821: Bad place for them but what the hell.
                   7822: 
1.157     matthew  7823: =over 4
                   7824: 
1.648     raeburn  7825: =item * &chartlink()
1.139     matthew  7826: 
                   7827: Returns a link to the chart for a specific student.  
                   7828: 
                   7829: Inputs:
                   7830: 
                   7831: =over 4
                   7832: 
                   7833: =item $linktext: The text of the link
                   7834: 
                   7835: =item $sname: The students username
                   7836: 
                   7837: =item $sdomain: The students domain
                   7838: 
                   7839: =back
                   7840: 
1.157     matthew  7841: =back
                   7842: 
1.139     matthew  7843: =cut
                   7844: 
                   7845: ############################################################
                   7846: ############################################################
                   7847: sub chartlink {
                   7848:     my ($linktext, $sname, $sdomain) = @_;
                   7849:     my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369     www      7850:         '&amp;SelectedStudent='.&escape($sname.':'.$sdomain).
1.219     albertel 7851:         '&amp;chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139     matthew  7852:        '">'.$linktext.'</a>';
1.153     matthew  7853: }
                   7854: 
                   7855: #######################################################
                   7856: #######################################################
                   7857: 
                   7858: =pod
                   7859: 
                   7860: =head1 Course Environment Routines
1.157     matthew  7861: 
                   7862: =over 4
1.153     matthew  7863: 
1.648     raeburn  7864: =item * &restore_course_settings()
1.153     matthew  7865: 
1.648     raeburn  7866: =item * &store_course_settings()
1.153     matthew  7867: 
                   7868: Restores/Store indicated form parameters from the course environment.
                   7869: Will not overwrite existing values of the form parameters.
                   7870: 
                   7871: Inputs: 
                   7872: a scalar describing the data (e.g. 'chart', 'problem_analysis')
                   7873: 
                   7874: a hash ref describing the data to be stored.  For example:
                   7875:    
                   7876: %Save_Parameters = ('Status' => 'scalar',
                   7877:     'chartoutputmode' => 'scalar',
                   7878:     'chartoutputdata' => 'scalar',
                   7879:     'Section' => 'array',
1.373     raeburn  7880:     'Group' => 'array',
1.153     matthew  7881:     'StudentData' => 'array',
                   7882:     'Maps' => 'array');
                   7883: 
                   7884: Returns: both routines return nothing
                   7885: 
1.631     raeburn  7886: =back
                   7887: 
1.153     matthew  7888: =cut
                   7889: 
                   7890: #######################################################
                   7891: #######################################################
                   7892: sub store_course_settings {
1.496     albertel 7893:     return &store_settings($env{'request.course.id'},@_);
                   7894: }
                   7895: 
                   7896: sub store_settings {
1.153     matthew  7897:     # save to the environment
                   7898:     # appenv the same items, just to be safe
1.300     albertel 7899:     my $udom  = $env{'user.domain'};
                   7900:     my $uname = $env{'user.name'};
1.496     albertel 7901:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  7902:     my %SaveHash;
                   7903:     my %AppHash;
                   7904:     while (my ($setting,$type) = each(%$Settings)) {
1.496     albertel 7905:         my $basename = join('.','internal',$context,$prefix,$setting);
1.300     albertel 7906:         my $envname = 'environment.'.$basename;
1.258     albertel 7907:         if (exists($env{'form.'.$setting})) {
1.153     matthew  7908:             # Save this value away
                   7909:             if ($type eq 'scalar' &&
1.258     albertel 7910:                 (! exists($env{$envname}) || 
                   7911:                  $env{$envname} ne $env{'form.'.$setting})) {
                   7912:                 $SaveHash{$basename} = $env{'form.'.$setting};
                   7913:                 $AppHash{$envname}   = $env{'form.'.$setting};
1.153     matthew  7914:             } elsif ($type eq 'array') {
                   7915:                 my $stored_form;
1.258     albertel 7916:                 if (ref($env{'form.'.$setting})) {
1.153     matthew  7917:                     $stored_form = join(',',
                   7918:                                         map {
1.369     www      7919:                                             &escape($_);
1.258     albertel 7920:                                         } sort(@{$env{'form.'.$setting}}));
1.153     matthew  7921:                 } else {
                   7922:                     $stored_form = 
1.369     www      7923:                         &escape($env{'form.'.$setting});
1.153     matthew  7924:                 }
                   7925:                 # Determine if the array contents are the same.
1.258     albertel 7926:                 if ($stored_form ne $env{$envname}) {
1.153     matthew  7927:                     $SaveHash{$basename} = $stored_form;
                   7928:                     $AppHash{$envname}   = $stored_form;
                   7929:                 }
                   7930:             }
                   7931:         }
                   7932:     }
                   7933:     my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300     albertel 7934:                                           $udom,$uname);
1.153     matthew  7935:     if ($put_result !~ /^(ok|delayed)/) {
                   7936:         &Apache::lonnet::logthis('unable to save form parameters, '.
                   7937:                                  'got error:'.$put_result);
                   7938:     }
                   7939:     # Make sure these settings stick around in this session, too
1.646     raeburn  7940:     &Apache::lonnet::appenv(\%AppHash);
1.153     matthew  7941:     return;
                   7942: }
                   7943: 
                   7944: sub restore_course_settings {
1.499     albertel 7945:     return &restore_settings($env{'request.course.id'},@_);
1.496     albertel 7946: }
                   7947: 
                   7948: sub restore_settings {
                   7949:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  7950:     while (my ($setting,$type) = each(%$Settings)) {
1.258     albertel 7951:         next if (exists($env{'form.'.$setting}));
1.496     albertel 7952:         my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153     matthew  7953:             '.'.$setting;
1.258     albertel 7954:         if (exists($env{$envname})) {
1.153     matthew  7955:             if ($type eq 'scalar') {
1.258     albertel 7956:                 $env{'form.'.$setting} = $env{$envname};
1.153     matthew  7957:             } elsif ($type eq 'array') {
1.258     albertel 7958:                 $env{'form.'.$setting} = [ 
1.153     matthew  7959:                                            map { 
1.369     www      7960:                                                &unescape($_); 
1.258     albertel 7961:                                            } split(',',$env{$envname})
1.153     matthew  7962:                                            ];
                   7963:             }
                   7964:         }
                   7965:     }
1.127     matthew  7966: }
                   7967: 
1.618     raeburn  7968: #######################################################
                   7969: #######################################################
                   7970: 
                   7971: =pod
                   7972: 
                   7973: =head1 Domain E-mail Routines  
                   7974: 
                   7975: =over 4
                   7976: 
1.648     raeburn  7977: =item * &build_recipient_list()
1.618     raeburn  7978: 
                   7979: Build recipient lists for three types of e-mail:
                   7980: (a) Error Reports, (b) Package Updates, (c) Help requests, generated by
1.619     raeburn  7981: lonerrorhandler.pm, CHECKRPMS and lonsupportreq.pm respectively.
1.618     raeburn  7982: 
                   7983: Inputs:
1.619     raeburn  7984: defmail (scalar - email address of default recipient), 
1.618     raeburn  7985: mailing type (scalar - errormail, packagesmail, or helpdeskmail), 
1.619     raeburn  7986: defdom (domain for which to retrieve configuration settings),
                   7987: origmail (scalar - email address of recipient from loncapa.conf, 
                   7988: i.e., predates configuration by DC via domainprefs.pm 
1.618     raeburn  7989: 
1.655     raeburn  7990: Returns: comma separated list of addresses to which to send e-mail.
                   7991: 
                   7992: =back
1.618     raeburn  7993: 
                   7994: =cut
                   7995: 
                   7996: ############################################################
                   7997: ############################################################
                   7998: sub build_recipient_list {
1.619     raeburn  7999:     my ($defmail,$mailing,$defdom,$origmail) = @_;
1.618     raeburn  8000:     my @recipients;
                   8001:     my $otheremails;
                   8002:     my %domconfig =
                   8003:          &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
                   8004:     if (ref($domconfig{'contacts'}) eq 'HASH') {
                   8005:         if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
                   8006:             my @contacts = ('adminemail','supportemail');
                   8007:             foreach my $item (@contacts) {
                   8008:                 if ($domconfig{'contacts'}{$mailing}{$item}) {
1.619     raeburn  8009:                     my $addr = $domconfig{'contacts'}{$item}; 
                   8010:                     if (!grep(/^\Q$addr\E$/,@recipients)) {
                   8011:                         push(@recipients,$addr);
                   8012:                     }
1.618     raeburn  8013:                 }
                   8014:                 $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
                   8015:             }
                   8016:         }
1.619     raeburn  8017:     } elsif ($origmail ne '') {
                   8018:         push(@recipients,$origmail);
1.618     raeburn  8019:     }
                   8020:     if ($defmail ne '') {
                   8021:         push(@recipients,$defmail);
                   8022:     }
                   8023:     if ($otheremails) {
1.619     raeburn  8024:         my @others;
                   8025:         if ($otheremails =~ /,/) {
                   8026:             @others = split(/,/,$otheremails);
1.618     raeburn  8027:         } else {
1.619     raeburn  8028:             push(@others,$otheremails);
                   8029:         }
                   8030:         foreach my $addr (@others) {
                   8031:             if (!grep(/^\Q$addr\E$/,@recipients)) {
                   8032:                 push(@recipients,$addr);
                   8033:             }
1.618     raeburn  8034:         }
                   8035:     }
1.619     raeburn  8036:     my $recipientlist = join(',',@recipients); 
1.618     raeburn  8037:     return $recipientlist;
                   8038: }
                   8039: 
1.127     matthew  8040: ############################################################
                   8041: ############################################################
1.154     albertel 8042: 
1.655     raeburn  8043: =pod
                   8044: 
                   8045: =head1 Course Catalog Routines
                   8046: 
                   8047: =over 4
                   8048: 
                   8049: =item * &gather_categories()
                   8050: 
                   8051: Converts category definitions - keys of categories hash stored in  
                   8052: coursecategories in configuration.db on the primary library server in a 
                   8053: domain - to an array.  Also generates javascript and idx hash used to 
                   8054: generate Domain Coordinator interface for editing Course Categories.
                   8055: 
                   8056: Inputs:
                   8057: categories (reference to hash of category definitions).
                   8058: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   8059:       categories and subcategories).
                   8060: idx (reference to hash of counters used in Domain Coordinator interface for 
                   8061:       editing Course Categories).
                   8062: jsarray (reference to array of categories used to create Javascript arrays for
                   8063:          Domain Coordinator interface for editing Course Categories).
                   8064: 
                   8065: Returns: nothing
                   8066: 
                   8067: Side effects: populates cats, idx and jsarray. 
                   8068: 
                   8069: =cut
                   8070: 
                   8071: sub gather_categories {
                   8072:     my ($categories,$cats,$idx,$jsarray) = @_;
                   8073:     my %counters;
                   8074:     my $num = 0;
                   8075:     foreach my $item (keys(%{$categories})) {
                   8076:         my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
                   8077:         if ($container eq '' && $depth == 0) {
                   8078:             $cats->[$depth][$categories->{$item}] = $cat;
                   8079:         } else {
                   8080:             $cats->[$depth]{$container}[$categories->{$item}] = $cat;
                   8081:         }
                   8082:         my ($escitem,$tail) = split(/:/,$item,2);
                   8083:         if ($counters{$tail} eq '') {
                   8084:             $counters{$tail} = $num;
                   8085:             $num ++;
                   8086:         }
                   8087:         if (ref($idx) eq 'HASH') {
                   8088:             $idx->{$item} = $counters{$tail};
                   8089:         }
                   8090:         if (ref($jsarray) eq 'ARRAY') {
                   8091:             push(@{$jsarray->[$counters{$tail}]},$item);
                   8092:         }
                   8093:     }
                   8094:     return;
                   8095: }
                   8096: 
                   8097: =pod
                   8098: 
                   8099: =item * &extract_categories()
                   8100: 
                   8101: Used to generate breadcrumb trails for course categories.
                   8102: 
                   8103: Inputs:
                   8104: categories (reference to hash of category definitions).
                   8105: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   8106:       categories and subcategories).
                   8107: trails (reference to array of breacrumb trails for each category).
                   8108: allitems (reference to hash - key is category key 
                   8109:          (format: escaped(name):escaped(parent category):depth in hierarchy).
                   8110: idx (reference to hash of counters used in Domain Coordinator interface for
                   8111:       editing Course Categories).
                   8112: jsarray (reference to array of categories used to create Javascript arrays for
                   8113:          Domain Coordinator interface for editing Course Categories).
                   8114: 
                   8115: Returns: nothing
                   8116: 
                   8117: Side effects: populates trails and allitems hash references.
                   8118: 
                   8119: =cut
                   8120: 
                   8121: sub extract_categories {
                   8122:     my ($categories,$cats,$trails,$allitems,$idx,$jsarray) = @_;
                   8123:     if (ref($categories) eq 'HASH') {
                   8124:         &gather_categories($categories,$cats,$idx,$jsarray);
                   8125:         if (ref($cats->[0]) eq 'ARRAY') {
                   8126:             for (my $i=0; $i<@{$cats->[0]}; $i++) {
                   8127:                 my $name = $cats->[0][$i];
                   8128:                 my $item = &escape($name).'::0';
                   8129:                 my $trailstr;
                   8130:                 if ($name eq 'instcode') {
                   8131:                     $trailstr = &mt('Official courses (with institutional codes)');
                   8132:                 } else {
                   8133:                     $trailstr = $name;
                   8134:                 }
                   8135:                 if ($allitems->{$item} eq '') {
                   8136:                     push(@{$trails},$trailstr);
                   8137:                     $allitems->{$item} = scalar(@{$trails})-1;
                   8138:                 }
                   8139:                 my @parents = ($name);
                   8140:                 if (ref($cats->[1]{$name}) eq 'ARRAY') {
                   8141:                     for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
                   8142:                         my $category = $cats->[1]{$name}[$j];
                   8143:                         &recurse_categories($cats,2,$category,$trails,$allitems,\@parents);
                   8144:                     }
                   8145:                 }
                   8146:             }
                   8147:         }
                   8148:     }
                   8149:     return;
                   8150: }
                   8151: 
                   8152: =pod
                   8153: 
                   8154: =item *&recurse_categories()
                   8155: 
                   8156: Recursively used to generate breadcrumb trails for course categories.
                   8157: 
                   8158: Inputs:
                   8159: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   8160:       categories and subcategories).
                   8161: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
                   8162: category (current course category, for which breadcrumb trail is being generated).   
                   8163: trails (reference to array of breacrumb trails for each category).
                   8164: allitems (reference to hash - key is category key
                   8165:          (format: escaped(name):escaped(parent category):depth in hierarchy).
                   8166: parents (array containing containers directories for current category, 
                   8167:          back to top level). 
                   8168: 
                   8169: Returns: nothing
                   8170: 
                   8171: Side effects: populates trails and allitems hash references
                   8172: 
                   8173: =back
                   8174: 
                   8175: =cut
                   8176: 
                   8177: sub recurse_categories {
                   8178:     my ($cats,$depth,$category,$trails,$allitems,$parents) = @_;
                   8179:     my $shallower = $depth - 1;
                   8180:     if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
                   8181:         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
                   8182:             my $name = $cats->[$depth]{$category}[$k];
                   8183:             my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   8184:             my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   8185:             if ($allitems->{$item} eq '') {
                   8186:                 push(@{$trails},$trailstr);
                   8187:                 $allitems->{$item} = scalar(@{$trails})-1;
                   8188:             }
                   8189:             my $deeper = $depth+1;
                   8190:             push(@{$parents},$category);
                   8191:             &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents);
                   8192:             pop(@{$parents});
                   8193:         }
                   8194:     } else {
                   8195:         my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   8196:         my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   8197:         if ($allitems->{$item} eq '') {
                   8198:             push(@{$trails},$trailstr);
                   8199:             $allitems->{$item} = scalar(@{$trails})-1;
                   8200:         }
                   8201:     }
                   8202:     return;
                   8203: }
                   8204: 
                   8205: ############################################################
                   8206: ############################################################
                   8207: 
                   8208: 
1.443     albertel 8209: sub commit_customrole {
                   8210:     my ($udom,$uname,$url,$three,$four,$five,$start,$end) = @_;
1.630     raeburn  8211:     my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443     albertel 8212:                          ($start?', '.&mt('starting').' '.localtime($start):'').
                   8213:                          ($end?', ending '.localtime($end):'').': <b>'.
                   8214:               &Apache::lonnet::assigncustomrole(
                   8215:                  $udom,$uname,$url,$three,$four,$five,$end,$start).
                   8216:                  '</b><br />';
                   8217:     return $output;
                   8218: }
                   8219: 
                   8220: sub commit_standardrole {
1.541     raeburn  8221:     my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
                   8222:     my ($output,$logmsg,$linefeed);
                   8223:     if ($context eq 'auto') {
                   8224:         $linefeed = "\n";
                   8225:     } else {
                   8226:         $linefeed = "<br />\n";
                   8227:     }  
1.443     albertel 8228:     if ($three eq 'st') {
1.541     raeburn  8229:         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
                   8230:                                          $one,$two,$sec,$context);
                   8231:         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
1.626     raeburn  8232:             ($result eq 'unknown_course') || ($result eq 'refused')) {
                   8233:             $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
1.443     albertel 8234:         } else {
1.541     raeburn  8235:             $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443     albertel 8236:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  8237:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
                   8238:             if ($context eq 'auto') {
                   8239:                 $output .= $result.$linefeed.&mt('Add to classlist').': ok';
                   8240:             } else {
                   8241:                $output .= '<b>'.$result.'</b>'.$linefeed.
                   8242:                &mt('Add to classlist').': <b>ok</b>';
                   8243:             }
                   8244:             $output .= $linefeed;
1.443     albertel 8245:         }
                   8246:     } else {
                   8247:         $output = &mt('Assigning').' '.$three.' in '.$url.
                   8248:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  8249:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652     raeburn  8250:         my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541     raeburn  8251:         if ($context eq 'auto') {
                   8252:             $output .= $result.$linefeed;
                   8253:         } else {
                   8254:             $output .= '<b>'.$result.'</b>'.$linefeed;
                   8255:         }
1.443     albertel 8256:     }
                   8257:     return $output;
                   8258: }
                   8259: 
                   8260: sub commit_studentrole {
1.541     raeburn  8261:     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
1.626     raeburn  8262:     my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541     raeburn  8263:     if ($context eq 'auto') {
                   8264:         $linefeed = "\n";
                   8265:     } else {
                   8266:         $linefeed = '<br />'."\n";
                   8267:     }
1.443     albertel 8268:     if (defined($one) && defined($two)) {
                   8269:         my $cid=$one.'_'.$two;
                   8270:         my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
                   8271:         my $secchange = 0;
                   8272:         my $expire_role_result;
                   8273:         my $modify_section_result;
1.628     raeburn  8274:         if ($oldsec ne '-1') { 
                   8275:             if ($oldsec ne $sec) {
1.443     albertel 8276:                 $secchange = 1;
1.628     raeburn  8277:                 my $now = time;
1.443     albertel 8278:                 my $uurl='/'.$cid;
                   8279:                 $uurl=~s/\_/\//g;
                   8280:                 if ($oldsec) {
                   8281:                     $uurl.='/'.$oldsec;
                   8282:                 }
1.626     raeburn  8283:                 $oldsecurl = $uurl;
1.628     raeburn  8284:                 $expire_role_result = 
1.652     raeburn  8285:                     &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628     raeburn  8286:                 if ($env{'request.course.sec'} ne '') { 
                   8287:                     if ($expire_role_result eq 'refused') {
                   8288:                         my @roles = ('st');
                   8289:                         my @statuses = ('previous');
                   8290:                         my @roledoms = ($one);
                   8291:                         my $withsec = 1;
                   8292:                         my %roleshash = 
                   8293:                             &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
                   8294:                                               \@statuses,\@roles,\@roledoms,$withsec);
                   8295:                         if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
                   8296:                             my ($oldstart,$oldend) = 
                   8297:                                 split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
                   8298:                             if ($oldend > 0 && $oldend <= $now) {
                   8299:                                 $expire_role_result = 'ok';
                   8300:                             }
                   8301:                         }
                   8302:                     }
                   8303:                 }
1.443     albertel 8304:                 $result = $expire_role_result;
                   8305:             }
                   8306:         }
                   8307:         if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.652     raeburn  8308:             $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid,'',$context);
1.443     albertel 8309:             if ($modify_section_result =~ /^ok/) {
                   8310:                 if ($secchange == 1) {
1.628     raeburn  8311:                     if ($sec eq '') {
                   8312:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
                   8313:                     } else {
                   8314:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
                   8315:                     }
1.443     albertel 8316:                 } elsif ($oldsec eq '-1') {
1.628     raeburn  8317:                     if ($sec eq '') {
                   8318:                         $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
                   8319:                     } else {
                   8320:                         $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   8321:                     }
1.443     albertel 8322:                 } else {
1.628     raeburn  8323:                     if ($sec eq '') {
                   8324:                         $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
                   8325:                     } else {
                   8326:                         $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   8327:                     }
1.443     albertel 8328:                 }
                   8329:             } else {
1.628     raeburn  8330:                 if ($secchange) {       
                   8331:                     $$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;
                   8332:                 } else {
                   8333:                     $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
                   8334:                 }
1.443     albertel 8335:             }
                   8336:             $result = $modify_section_result;
                   8337:         } elsif ($secchange == 1) {
1.628     raeburn  8338:             if ($oldsec eq '') {
                   8339:                 $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_3] -error: ',$uname,$cid).' '.$expire_role_result.$linefeed;
                   8340:             } else {
                   8341:                 $$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;
                   8342:             }
1.626     raeburn  8343:             if ($expire_role_result eq 'refused') {
                   8344:                 my $newsecurl = '/'.$cid;
                   8345:                 $newsecurl =~ s/\_/\//g;
                   8346:                 if ($sec ne '') {
                   8347:                     $newsecurl.='/'.$sec;
                   8348:                 }
                   8349:                 if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
                   8350:                     if ($sec eq '') {
                   8351:                         $$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;
                   8352:                     } else {
                   8353:                         $$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;
                   8354:                     }
                   8355:                 }
                   8356:             }
1.443     albertel 8357:         }
                   8358:     } else {
1.626     raeburn  8359:         $$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 8360:         $result = "error: incomplete course id\n";
                   8361:     }
                   8362:     return $result;
                   8363: }
                   8364: 
                   8365: ############################################################
                   8366: ############################################################
                   8367: 
1.566     albertel 8368: sub check_clone {
1.578     raeburn  8369:     my ($args,$linefeed) = @_;
1.566     albertel 8370:     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
                   8371:     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
                   8372:     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
                   8373:     my $clonemsg;
                   8374:     my $can_clone = 0;
                   8375: 
                   8376:     if ($clonehome eq 'no_host') {
1.578     raeburn  8377:         $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 8378:     } else {
                   8379: 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.568     albertel 8380: 	if ($env{'request.role.domain'} eq $args->{'clonedomain'}) {
1.566     albertel 8381: 	    $can_clone = 1;
                   8382: 	} else {
                   8383: 	    my %clonehash = &Apache::lonnet::get('environment',['cloners'],
                   8384: 						 $args->{'clonedomain'},$args->{'clonecourse'});
                   8385: 	    my @cloners = split(/,/,$clonehash{'cloners'});
1.578     raeburn  8386:             if (grep(/^\*$/,@cloners)) {
                   8387:                 $can_clone = 1;
                   8388:             } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
                   8389:                 $can_clone = 1;
                   8390:             } else {
                   8391: 	        my %roleshash =
                   8392: 		    &Apache::lonnet::get_my_roles($args->{'ccuname'},
                   8393: 					 $args->{'ccdomain'},
                   8394:                                          'userroles',['active'],['cc'],
                   8395: 					 [$args->{'clonedomain'}]);
                   8396: 	        if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':cc'}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
                   8397: 		    $can_clone = 1;
                   8398: 	        } else {
                   8399:                     $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'});
                   8400: 	        }
1.566     albertel 8401: 	    }
1.578     raeburn  8402:         }
1.566     albertel 8403:     }
                   8404:     return ($can_clone, $clonemsg, $cloneid, $clonehome);
                   8405: }
                   8406: 
1.444     albertel 8407: sub construct_course {
1.541     raeburn  8408:     my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context) = @_;
1.444     albertel 8409:     my $outcome;
1.541     raeburn  8410:     my $linefeed =  '<br />'."\n";
                   8411:     if ($context eq 'auto') {
                   8412:         $linefeed = "\n";
                   8413:     }
1.566     albertel 8414: 
                   8415: #
                   8416: # Are we cloning?
                   8417: #
                   8418:     my ($can_clone, $clonemsg, $cloneid, $clonehome);
                   8419:     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578     raeburn  8420: 	($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566     albertel 8421: 	if ($context ne 'auto') {
1.578     raeburn  8422:             if ($clonemsg ne '') {
                   8423: 	        $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
                   8424:             }
1.566     albertel 8425: 	}
                   8426: 	$outcome .= $clonemsg.$linefeed;
                   8427: 
                   8428:         if (!$can_clone) {
                   8429: 	    return (0,$outcome);
                   8430: 	}
                   8431:     }
                   8432: 
1.444     albertel 8433: #
                   8434: # Open course
                   8435: #
                   8436:     my $crstype = lc($args->{'crstype'});
                   8437:     my %cenv=();
                   8438:     $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
                   8439:                                              $args->{'cdescr'},
                   8440:                                              $args->{'curl'},
                   8441:                                              $args->{'course_home'},
                   8442:                                              $args->{'nonstandard'},
                   8443:                                              $args->{'crscode'},
                   8444:                                              $args->{'ccuname'}.':'.
                   8445:                                              $args->{'ccdomain'},
                   8446:                                              $args->{'crstype'});
                   8447: 
                   8448:     # Note: The testing routines depend on this being output; see 
                   8449:     # Utils::Course. This needs to at least be output as a comment
                   8450:     # if anyone ever decides to not show this, and Utils::Course::new
                   8451:     # will need to be suitably modified.
1.541     raeburn  8452:     $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
1.444     albertel 8453: #
                   8454: # Check if created correctly
                   8455: #
1.479     albertel 8456:     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444     albertel 8457:     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.541     raeburn  8458:     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566     albertel 8459: 
1.444     albertel 8460: #
1.566     albertel 8461: # Do the cloning
                   8462: #   
                   8463:     if ($can_clone && $cloneid) {
                   8464: 	$clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
                   8465: 	if ($context ne 'auto') {
                   8466: 	    $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
                   8467: 	}
                   8468: 	$outcome .= $clonemsg.$linefeed;
                   8469: 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444     albertel 8470: # Copy all files
1.637     www      8471: 	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
1.444     albertel 8472: # Restore URL
1.566     albertel 8473: 	$cenv{'url'}=$oldcenv{'url'};
1.444     albertel 8474: # Restore title
1.566     albertel 8475: 	$cenv{'description'}=$oldcenv{'description'};
1.444     albertel 8476: # Mark as cloned
1.566     albertel 8477: 	$cenv{'clonedfrom'}=$cloneid;
1.638     www      8478: # Need to clone grading mode
                   8479:         my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
                   8480:         $cenv{'grading'}=$newenv{'grading'};
                   8481: # Do not clone these environment entries
                   8482:         &Apache::lonnet::del('environment',
                   8483:                   ['default_enrollment_start_date',
                   8484:                    'default_enrollment_end_date',
                   8485:                    'question.email',
                   8486:                    'policy.email',
                   8487:                    'comment.email',
                   8488:                    'pch.users.denied',
                   8489:                    'plc.users.denied'],
                   8490:                    $$crsudom,$$crsunum);
1.444     albertel 8491:     }
1.566     albertel 8492: 
1.444     albertel 8493: #
                   8494: # Set environment (will override cloned, if existing)
                   8495: #
                   8496:     my @sections = ();
                   8497:     my @xlists = ();
                   8498:     if ($args->{'crstype'}) {
                   8499:         $cenv{'type'}=$args->{'crstype'};
                   8500:     }
                   8501:     if ($args->{'crsid'}) {
                   8502:         $cenv{'courseid'}=$args->{'crsid'};
                   8503:     }
                   8504:     if ($args->{'crscode'}) {
                   8505:         $cenv{'internal.coursecode'}=$args->{'crscode'};
                   8506:     }
                   8507:     if ($args->{'crsquota'} ne '') {
                   8508:         $cenv{'internal.coursequota'}=$args->{'crsquota'};
                   8509:     } else {
                   8510:         $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
                   8511:     }
                   8512:     if ($args->{'ccuname'}) {
                   8513:         $cenv{'internal.courseowner'} = $args->{'ccuname'}.
                   8514:                                         ':'.$args->{'ccdomain'};
                   8515:     } else {
                   8516:         $cenv{'internal.courseowner'} = $args->{'curruser'};
                   8517:     }
                   8518:     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
                   8519:     if ($args->{'crssections'}) {
                   8520:         $cenv{'internal.sectionnums'} = '';
                   8521:         if ($args->{'crssections'} =~ m/,/) {
                   8522:             @sections = split/,/,$args->{'crssections'};
                   8523:         } else {
                   8524:             $sections[0] = $args->{'crssections'};
                   8525:         }
                   8526:         if (@sections > 0) {
                   8527:             foreach my $item (@sections) {
                   8528:                 my ($sec,$gp) = split/:/,$item;
                   8529:                 my $class = $args->{'crscode'}.$sec;
                   8530:                 my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
                   8531:                 $cenv{'internal.sectionnums'} .= $item.',';
                   8532:                 unless ($addcheck eq 'ok') {
                   8533:                     push @badclasses, $class;
                   8534:                 }
                   8535:             }
                   8536:             $cenv{'internal.sectionnums'} =~ s/,$//;
                   8537:         }
                   8538:     }
                   8539: # do not hide course coordinator from staff listing, 
                   8540: # even if privileged
                   8541:     $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   8542: # add crosslistings
                   8543:     if ($args->{'crsxlist'}) {
                   8544:         $cenv{'internal.crosslistings'}='';
                   8545:         if ($args->{'crsxlist'} =~ m/,/) {
                   8546:             @xlists = split/,/,$args->{'crsxlist'};
                   8547:         } else {
                   8548:             $xlists[0] = $args->{'crsxlist'};
                   8549:         }
                   8550:         if (@xlists > 0) {
                   8551:             foreach my $item (@xlists) {
                   8552:                 my ($xl,$gp) = split/:/,$item;
                   8553:                 my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
                   8554:                 $cenv{'internal.crosslistings'} .= $item.',';
                   8555:                 unless ($addcheck eq 'ok') {
                   8556:                     push @badclasses, $xl;
                   8557:                 }
                   8558:             }
                   8559:             $cenv{'internal.crosslistings'} =~ s/,$//;
                   8560:         }
                   8561:     }
                   8562:     if ($args->{'autoadds'}) {
                   8563:         $cenv{'internal.autoadds'}=$args->{'autoadds'};
                   8564:     }
                   8565:     if ($args->{'autodrops'}) {
                   8566:         $cenv{'internal.autodrops'}=$args->{'autodrops'};
                   8567:     }
                   8568: # check for notification of enrollment changes
                   8569:     my @notified = ();
                   8570:     if ($args->{'notify_owner'}) {
                   8571:         if ($args->{'ccuname'} ne '') {
                   8572:             push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
                   8573:         }
                   8574:     }
                   8575:     if ($args->{'notify_dc'}) {
                   8576:         if ($uname ne '') { 
1.630     raeburn  8577:             push(@notified,$uname.':'.$udom);
1.444     albertel 8578:         }
                   8579:     }
                   8580:     if (@notified > 0) {
                   8581:         my $notifylist;
                   8582:         if (@notified > 1) {
                   8583:             $notifylist = join(',',@notified);
                   8584:         } else {
                   8585:             $notifylist = $notified[0];
                   8586:         }
                   8587:         $cenv{'internal.notifylist'} = $notifylist;
                   8588:     }
                   8589:     if (@badclasses > 0) {
                   8590:         my %lt=&Apache::lonlocal::texthash(
                   8591:                 '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',
                   8592:                 'dnhr' => 'does not have rights to access enrollment in these classes',
                   8593:                 'adby' => 'as determined by the policies of your institution on access to official classlists'
                   8594:         );
1.541     raeburn  8595:         my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
                   8596:                            ' ('.$lt{'adby'}.')';
                   8597:         if ($context eq 'auto') {
                   8598:             $outcome .= $badclass_msg.$linefeed;
1.566     albertel 8599:             $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.541     raeburn  8600:             foreach my $item (@badclasses) {
                   8601:                 if ($context eq 'auto') {
                   8602:                     $outcome .= " - $item\n";
                   8603:                 } else {
                   8604:                     $outcome .= "<li>$item</li>\n";
                   8605:                 }
                   8606:             }
                   8607:             if ($context eq 'auto') {
                   8608:                 $outcome .= $linefeed;
                   8609:             } else {
1.566     albertel 8610:                 $outcome .= "</ul><br /><br /></div>\n";
1.541     raeburn  8611:             }
                   8612:         } 
1.444     albertel 8613:     }
                   8614:     if ($args->{'no_end_date'}) {
                   8615:         $args->{'endaccess'} = 0;
                   8616:     }
                   8617:     $cenv{'internal.autostart'}=$args->{'enrollstart'};
                   8618:     $cenv{'internal.autoend'}=$args->{'enrollend'};
                   8619:     $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
                   8620:     $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
                   8621:     if ($args->{'showphotos'}) {
                   8622:       $cenv{'internal.showphotos'}=$args->{'showphotos'};
                   8623:     }
                   8624:     $cenv{'internal.authtype'} = $args->{'authtype'};
                   8625:     $cenv{'internal.autharg'} = $args->{'autharg'}; 
                   8626:     if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
                   8627:         if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'}  eq '') {
1.541     raeburn  8628:             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'); 
                   8629:             if ($context eq 'auto') {
                   8630:                 $outcome .= $krb_msg;
                   8631:             } else {
1.566     albertel 8632:                 $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541     raeburn  8633:             }
                   8634:             $outcome .= $linefeed;
1.444     albertel 8635:         }
                   8636:     }
                   8637:     if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
                   8638:        if ($args->{'setpolicy'}) {
                   8639:            $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   8640:        }
                   8641:        if ($args->{'setcontent'}) {
                   8642:            $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   8643:        }
                   8644:     }
                   8645:     if ($args->{'reshome'}) {
                   8646: 	$cenv{'reshome'}=$args->{'reshome'}.'/';
                   8647: 	$cenv{'reshome'}=~s/\/+$/\//;
                   8648:     }
                   8649: #
                   8650: # course has keyed access
                   8651: #
                   8652:     if ($args->{'setkeys'}) {
                   8653:        $cenv{'keyaccess'}='yes';
                   8654:     }
                   8655: # if specified, key authority is not course, but user
                   8656: # only active if keyaccess is yes
                   8657:     if ($args->{'keyauth'}) {
1.487     albertel 8658: 	my ($user,$domain) = split(':',$args->{'keyauth'});
                   8659: 	$user = &LONCAPA::clean_username($user);
                   8660: 	$domain = &LONCAPA::clean_username($domain);
1.488     foxr     8661: 	if ($user ne '' && $domain ne '') {
1.487     albertel 8662: 	    $cenv{'keyauth'}=$user.':'.$domain;
1.444     albertel 8663: 	}
                   8664:     }
                   8665: 
                   8666:     if ($args->{'disresdis'}) {
                   8667:         $cenv{'pch.roles.denied'}='st';
                   8668:     }
                   8669:     if ($args->{'disablechat'}) {
                   8670:         $cenv{'plc.roles.denied'}='st';
                   8671:     }
                   8672: 
                   8673:     # Record we've not yet viewed the Course Initialization Helper for this 
                   8674:     # course
                   8675:     $cenv{'course.helper.not.run'} = 1;
                   8676:     #
                   8677:     # Use new Randomseed
                   8678:     #
                   8679:     $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
                   8680:     $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
                   8681:     #
                   8682:     # The encryption code and receipt prefix for this course
                   8683:     #
                   8684:     $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
                   8685:     $cenv{'internal.encpref'}=100+int(9*rand(99));
                   8686:     #
                   8687:     # By default, use standard grading
                   8688:     if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
                   8689: 
1.541     raeburn  8690:     $outcome .= $linefeed.&mt('Setting environment').': '.                 
                   8691:           &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 8692: #
                   8693: # Open all assignments
                   8694: #
                   8695:     if ($args->{'openall'}) {
                   8696:        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
                   8697:        my %storecontent = ($storeunder         => time,
                   8698:                            $storeunder.'.type' => 'date_start');
                   8699:        
                   8700:        $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541     raeburn  8701:                  ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 8702:    }
                   8703: #
                   8704: # Set first page
                   8705: #
                   8706:     unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
                   8707: 	    || ($cloneid)) {
1.445     albertel 8708: 	use LONCAPA::map;
1.444     albertel 8709: 	$outcome .= &mt('Setting first resource').': ';
1.445     albertel 8710: 
                   8711: 	my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
                   8712:         my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
                   8713: 
1.444     albertel 8714:         $outcome .= ($fatal?$errtext:'read ok').' - ';
                   8715:         my $title; my $url;
                   8716:         if ($args->{'firstres'} eq 'syl') {
                   8717: 	    $title='Syllabus';
                   8718:             $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
                   8719:         } else {
                   8720:             $title='Navigate Contents';
                   8721:             $url='/adm/navmaps';
                   8722:         }
1.445     albertel 8723: 
                   8724:         $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
                   8725: 	(my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
                   8726: 
                   8727: 	if ($errtext) { $fatal=2; }
1.541     raeburn  8728:         $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444     albertel 8729:     }
1.566     albertel 8730: 
                   8731:     return (1,$outcome);
1.444     albertel 8732: }
                   8733: 
                   8734: ############################################################
                   8735: ############################################################
                   8736: 
1.378     raeburn  8737: sub course_type {
                   8738:     my ($cid) = @_;
                   8739:     if (!defined($cid)) {
                   8740:         $cid = $env{'request.course.id'};
                   8741:     }
1.404     albertel 8742:     if (defined($env{'course.'.$cid.'.type'})) {
                   8743:         return $env{'course.'.$cid.'.type'};
1.378     raeburn  8744:     } else {
                   8745:         return 'Course';
1.377     raeburn  8746:     }
                   8747: }
1.156     albertel 8748: 
1.406     raeburn  8749: sub group_term {
                   8750:     my $crstype = &course_type();
                   8751:     my %names = (
                   8752:                   'Course' => 'group',
                   8753:                   'Group' => 'team',
                   8754:                 );
                   8755:     return $names{$crstype};
                   8756: }
                   8757: 
1.156     albertel 8758: sub icon {
                   8759:     my ($file)=@_;
1.505     albertel 8760:     my $curfext = lc((split(/\./,$file))[-1]);
1.168     albertel 8761:     my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156     albertel 8762:     my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168     albertel 8763:     if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
                   8764: 	if (-e  $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                   8765: 	          $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   8766: 	            $curfext.".gif") {
                   8767: 	    $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   8768: 		$curfext.".gif";
                   8769: 	}
                   8770:     }
1.249     albertel 8771:     return &lonhttpdurl($iconname);
1.154     albertel 8772: } 
1.84      albertel 8773: 
1.575     albertel 8774: sub lonhttpd_port {
1.215     albertel 8775:     my $lonhttpd_port=$Apache::lonnet::perlvar{'lonhttpdPort'};
                   8776:     if (!defined($lonhttpd_port)) { $lonhttpd_port='8080'; }
1.574     albertel 8777:     # IE doesn't like a secure page getting images from a non-secure
                   8778:     # port (when logging we haven't parsed the browser type so default
                   8779:     # back to secure
                   8780:     if ((!exists($env{'browser.type'}) || $env{'browser.type'} eq 'explorer')
                   8781: 	&& $ENV{'SERVER_PORT'} == 443) {
1.575     albertel 8782: 	return 443;
                   8783:     }
                   8784:     return $lonhttpd_port;
                   8785: 
                   8786: }
                   8787: 
                   8788: sub lonhttpdurl {
                   8789:     my ($url)=@_;
                   8790: 
                   8791:     my $lonhttpd_port = &lonhttpd_port();
                   8792:     if ($lonhttpd_port == 443) {
1.574     albertel 8793: 	return 'https://'.$ENV{'SERVER_NAME'}.$url;
                   8794:     }
1.215     albertel 8795:     return 'http://'.$ENV{'SERVER_NAME'}.':'.$lonhttpd_port.$url;
                   8796: }
                   8797: 
1.213     albertel 8798: sub connection_aborted {
                   8799:     my ($r)=@_;
                   8800:     $r->print(" ");$r->rflush();
                   8801:     my $c = $r->connection;
                   8802:     return $c->aborted();
                   8803: }
                   8804: 
1.221     foxr     8805: #    Escapes strings that may have embedded 's that will be put into
1.222     foxr     8806: #    strings as 'strings'.
                   8807: sub escape_single {
1.221     foxr     8808:     my ($input) = @_;
1.223     albertel 8809:     $input =~ s/\\/\\\\/g;	# Escape the \'s..(must be first)>
1.221     foxr     8810:     $input =~ s/\'/\\\'/g;	# Esacpe the 's....
                   8811:     return $input;
                   8812: }
1.223     albertel 8813: 
1.222     foxr     8814: #  Same as escape_single, but escape's "'s  This 
                   8815: #  can be used for  "strings"
                   8816: sub escape_double {
                   8817:     my ($input) = @_;
                   8818:     $input =~ s/\\/\\\\/g;	# Escape the /'s..(must be first)>
                   8819:     $input =~ s/\"/\\\"/g;	# Esacpe the "s....
                   8820:     return $input;
                   8821: }
1.223     albertel 8822:  
1.222     foxr     8823: #   Escapes the last element of a full URL.
                   8824: sub escape_url {
                   8825:     my ($url)   = @_;
1.238     raeburn  8826:     my @urlslices = split(/\//, $url,-1);
1.369     www      8827:     my $lastitem = &escape(pop(@urlslices));
1.223     albertel 8828:     return join('/',@urlslices).'/'.$lastitem;
1.222     foxr     8829: }
1.462     albertel 8830: 
                   8831: # -------------------------------------------------------- Initliaze user login
                   8832: sub init_user_environment {
1.463     albertel 8833:     my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462     albertel 8834:     my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
                   8835: 
                   8836:     my $public=($username eq 'public' && $domain eq 'public');
                   8837: 
                   8838: # See if old ID present, if so, remove
                   8839: 
                   8840:     my ($filename,$cookie,$userroles);
                   8841:     my $now=time;
                   8842: 
                   8843:     if ($public) {
                   8844: 	my $max_public=100;
                   8845: 	my $oldest;
                   8846: 	my $oldest_time=0;
                   8847: 	for(my $next=1;$next<=$max_public;$next++) {
                   8848: 	    if (-e $lonids."/publicuser_$next.id") {
                   8849: 		my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
                   8850: 		if ($mtime<$oldest_time || !$oldest_time) {
                   8851: 		    $oldest_time=$mtime;
                   8852: 		    $oldest=$next;
                   8853: 		}
                   8854: 	    } else {
                   8855: 		$cookie="publicuser_$next";
                   8856: 		last;
                   8857: 	    }
                   8858: 	}
                   8859: 	if (!$cookie) { $cookie="publicuser_$oldest"; }
                   8860:     } else {
1.463     albertel 8861: 	# if this isn't a robot, kill any existing non-robot sessions
                   8862: 	if (!$args->{'robot'}) {
                   8863: 	    opendir(DIR,$lonids);
                   8864: 	    while ($filename=readdir(DIR)) {
                   8865: 		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
                   8866: 		    unlink($lonids.'/'.$filename);
                   8867: 		}
1.462     albertel 8868: 	    }
1.463     albertel 8869: 	    closedir(DIR);
1.462     albertel 8870: 	}
                   8871: # Give them a new cookie
1.463     albertel 8872: 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
                   8873: 		                   : $now);
                   8874: 	$cookie="$username\_$id\_$domain\_$authhost";
1.462     albertel 8875:     
                   8876: # Initialize roles
                   8877: 
                   8878: 	$userroles=&Apache::lonnet::rolesinit($domain,$username,$authhost);
                   8879:     }
                   8880: # ------------------------------------ Check browser type and MathML capability
                   8881: 
                   8882:     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
                   8883:         $clientunicode,$clientos) = &decode_user_agent($r);
                   8884: 
                   8885: # -------------------------------------- Any accessibility options to remember?
                   8886:     if (($form->{'interface'}) && ($form->{'remember'} eq 'true')) {
                   8887: 	foreach my $option ('imagesuppress','appletsuppress',
                   8888: 			    'embedsuppress','fontenhance','blackwhite') {
                   8889: 	    if ($form->{$option} eq 'true') {
                   8890: 		&Apache::lonnet::put('environment',{$option => 'on'},
                   8891: 				     $domain,$username);
                   8892: 	    } else {
                   8893: 		&Apache::lonnet::del('environment',[$option],
                   8894: 				     $domain,$username);
                   8895: 	    }
                   8896: 	}
                   8897:     }
                   8898: # ------------------------------------------------------------- Get environment
                   8899: 
                   8900:     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
                   8901:     my ($tmp) = keys(%userenv);
                   8902:     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   8903: 	# default remote control to off
                   8904: 	if ($userenv{'remote'} ne 'on') { $userenv{'remote'} = 'off'; }
                   8905:     } else {
                   8906: 	undef(%userenv);
                   8907:     }
                   8908:     if (($userenv{'interface'}) && (!$form->{'interface'})) {
                   8909: 	$form->{'interface'}=$userenv{'interface'};
                   8910:     }
                   8911:     $env{'environment.remote'}=$userenv{'remote'};
                   8912:     if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
                   8913: 
                   8914: # --------------- Do not trust query string to be put directly into environment
                   8915:     foreach my $option ('imagesuppress','appletsuppress',
                   8916: 			'embedsuppress','fontenhance','blackwhite',
                   8917: 			'interface','localpath','localres') {
                   8918: 	$form->{$option}=~s/[\n\r\=]//gs;
                   8919:     }
                   8920: # --------------------------------------------------------- Write first profile
                   8921: 
                   8922:     {
                   8923: 	my %initial_env = 
                   8924: 	    ("user.name"          => $username,
                   8925: 	     "user.domain"        => $domain,
                   8926: 	     "user.home"          => $authhost,
                   8927: 	     "browser.type"       => $clientbrowser,
                   8928: 	     "browser.version"    => $clientversion,
                   8929: 	     "browser.mathml"     => $clientmathml,
                   8930: 	     "browser.unicode"    => $clientunicode,
                   8931: 	     "browser.os"         => $clientos,
                   8932: 	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
                   8933: 	     "request.course.fn"  => '',
                   8934: 	     "request.course.uri" => '',
                   8935: 	     "request.course.sec" => '',
                   8936: 	     "request.role"       => 'cm',
                   8937: 	     "request.role.adv"   => $env{'user.adv'},
                   8938: 	     "request.host"       => $ENV{'REMOTE_ADDR'},);
                   8939: 
                   8940:         if ($form->{'localpath'}) {
                   8941: 	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
                   8942: 	    $initial_env{"browser.localres"}   = $form->{'localres'};
                   8943:         }
                   8944: 	
                   8945: 	if ($public) {
                   8946: 	    $initial_env{"environment.remote"} = "off";
                   8947: 	}
                   8948: 	if ($form->{'interface'}) {
                   8949: 	    $form->{'interface'}=~s/\W//gs;
                   8950: 	    $initial_env{"browser.interface"} = $form->{'interface'};
                   8951: 	    $env{'browser.interface'}=$form->{'interface'};
                   8952: 	    foreach my $option ('imagesuppress','appletsuppress',
                   8953: 				'embedsuppress','fontenhance','blackwhite') {
                   8954: 		if (($form->{$option} eq 'true') ||
                   8955: 		    ($userenv{$option} eq 'on')) {
                   8956: 		    $initial_env{"browser.$option"} = "on";
                   8957: 		}
                   8958: 	    }
                   8959: 	}
                   8960: 
                   8961: 	$env{'user.environment'} = "$lonids/$cookie.id";
                   8962: 	
                   8963: 	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
                   8964: 		 &GDBM_WRCREAT(),0640)) {
                   8965: 	    &_add_to_env(\%disk_env,\%initial_env);
                   8966: 	    &_add_to_env(\%disk_env,\%userenv,'environment.');
                   8967: 	    &_add_to_env(\%disk_env,$userroles);
1.463     albertel 8968: 	    if (ref($args->{'extra_env'})) {
                   8969: 		&_add_to_env(\%disk_env,$args->{'extra_env'});
                   8970: 	    }
1.462     albertel 8971: 	    untie(%disk_env);
                   8972: 	} else {
                   8973: 	    &Apache::lonnet::logthis("<font color=\"blue\">WARNING: ".
                   8974: 			   'Could not create environment storage in lonauth: '.$!.'</font>');
                   8975: 	    return 'error: '.$!;
                   8976: 	}
                   8977:     }
                   8978:     $env{'request.role'}='cm';
                   8979:     $env{'request.role.adv'}=$env{'user.adv'};
                   8980:     $env{'browser.type'}=$clientbrowser;
                   8981: 
                   8982:     return $cookie;
                   8983: 
                   8984: }
                   8985: 
                   8986: sub _add_to_env {
                   8987:     my ($idf,$env_data,$prefix) = @_;
                   8988:     while (my ($key,$value) = each(%$env_data)) {
                   8989: 	$idf->{$prefix.$key} = $value;
                   8990: 	$env{$prefix.$key}   = $value;
                   8991:     }
                   8992: }
                   8993: 
                   8994: 
1.41      ng       8995: =pod
                   8996: 
                   8997: =back
                   8998: 
1.112     bowersj2 8999: =cut
1.41      ng       9000: 
1.112     bowersj2 9001: 1;
                   9002: __END__;
1.41      ng       9003: 

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