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

1.10      albertel    1: # The LearningOnline Network with CAPA
1.1       albertel    2: # a pile of common routines
1.10      albertel    3: #
1.692.4.22! raeburn     4: # $Id: loncommon.pm,v 1.692.4.21 2010/01/19 06:01:09 raeburn Exp $
1.10      albertel    5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
1.1       albertel   28: 
                     29: # Makes a table out of the previous attempts
1.2       albertel   30: # Inputs result_from_symbread, user, domain, course_id
1.16      harris41   31: # Reads in non-network-related .tab files
1.1       albertel   32: 
1.35      matthew    33: # POD header:
                     34: 
1.45      matthew    35: =pod
                     36: 
1.35      matthew    37: =head1 NAME
                     38: 
                     39: Apache::loncommon - pile of common routines
                     40: 
                     41: =head1 SYNOPSIS
                     42: 
1.112     bowersj2   43: Common routines for manipulating connections, student answers,
                     44:     domains, common Javascript fragments, etc.
1.35      matthew    45: 
1.112     bowersj2   46: =head1 OVERVIEW
1.35      matthew    47: 
1.112     bowersj2   48: A collection of commonly used subroutines that don't have a natural
                     49: home anywhere else. This collection helps remove
1.35      matthew    50: redundancy from other modules and increase efficiency of memory usage.
                     51: 
                     52: =cut 
                     53: 
                     54: # End of POD header
1.1       albertel   55: package Apache::loncommon;
                     56: 
                     57: use strict;
1.258     albertel   58: use Apache::lonnet;
1.46      matthew    59: use GDBM_File;
1.51      www        60: use POSIX qw(strftime mktime);
1.82      www        61: use Apache::lonmenu();
1.498     albertel   62: use Apache::lonenc();
1.117     www        63: use Apache::lonlocal;
1.685     tempelho   64: use Apache::lonnet();
1.139     matthew    65: use HTML::Entities;
1.334     albertel   66: use Apache::lonhtmlcommon();
                     67: use Apache::loncoursedata();
1.344     albertel   68: use Apache::lontexconvert();
1.444     albertel   69: use Apache::lonclonecourse();
1.479     albertel   70: use LONCAPA qw(:DEFAULT :match);
1.657     raeburn    71: use DateTime::TimeZone;
1.687     raeburn    72: use DateTime::Locale::Catalog;
1.117     www        73: 
1.517     raeburn    74: # ---------------------------------------------- Designs
                     75: use vars qw(%defaultdesign);
                     76: 
1.22      www        77: my $readit;
                     78: 
1.517     raeburn    79: 
1.157     matthew    80: ##
                     81: ## Global Variables
                     82: ##
1.46      matthew    83: 
1.643     foxr       84: 
                     85: # ----------------------------------------------- SSI with retries:
                     86: #
                     87: 
                     88: =pod
                     89: 
1.648     raeburn    90: =head1 Server Side include with retries:
1.643     foxr       91: 
                     92: =over 4
                     93: 
1.648     raeburn    94: =item * &ssi_with_retries(resource,retries form)
1.643     foxr       95: 
                     96: Performs an ssi with some number of retries.  Retries continue either
                     97: until the result is ok or until the retry count supplied by the
                     98: caller is exhausted.  
                     99: 
                    100: Inputs:
1.648     raeburn   101: 
                    102: =over 4
                    103: 
1.643     foxr      104: resource   - Identifies the resource to insert.
1.648     raeburn   105: 
1.643     foxr      106: retries    - Count of the number of retries allowed.
1.648     raeburn   107: 
1.643     foxr      108: form       - Hash that identifies the rendering options.
                    109: 
1.648     raeburn   110: =back
                    111: 
                    112: Returns:
                    113: 
                    114: =over 4
                    115: 
1.643     foxr      116: content    - The content of the response.  If retries were exhausted this is empty.
1.648     raeburn   117: 
1.643     foxr      118: response   - The response from the last attempt (which may or may not have been successful.
                    119: 
1.648     raeburn   120: =back
                    121: 
                    122: =back
                    123: 
1.643     foxr      124: =cut
                    125: 
                    126: sub ssi_with_retries {
                    127:     my ($resource, $retries, %form) = @_;
                    128: 
                    129: 
                    130:     my $ok = 0;			# True if we got a good response.
                    131:     my $content;
                    132:     my $response;
                    133: 
                    134:     # Try to get the ssi done. within the retries count:
                    135: 
                    136:     do {
                    137: 	($content, $response) = &Apache::lonnet::ssi($resource, %form);
                    138: 	$ok      = $response->is_success;
1.650     www       139:         if (!$ok) {
                    140:             &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
                    141:         }
1.643     foxr      142: 	$retries--;
                    143:     } while (!$ok && ($retries > 0));
                    144: 
                    145:     if (!$ok) {
                    146: 	$content = '';		# On error return an empty content.
                    147:     }
                    148:     return ($content, $response);
                    149: 
                    150: }
                    151: 
                    152: 
                    153: 
1.20      www       154: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12      harris41  155: my %language;
1.124     www       156: my %supported_language;
1.12      harris41  157: my %cprtag;
1.192     taceyjo1  158: my %scprtag;
1.351     www       159: my %fe; my %fd; my %fm;
1.41      ng        160: my %category_extensions;
1.12      harris41  161: 
1.46      matthew   162: # ---------------------------------------------- Thesaurus variables
1.144     matthew   163: #
                    164: # %Keywords:
                    165: #      A hash used by &keyword to determine if a word is considered a keyword.
                    166: # $thesaurus_db_file 
                    167: #      Scalar containing the full path to the thesaurus database.
1.46      matthew   168: 
                    169: my %Keywords;
                    170: my $thesaurus_db_file;
                    171: 
1.144     matthew   172: #
                    173: # Initialize values from language.tab, copyright.tab, filetypes.tab,
                    174: # thesaurus.tab, and filecategories.tab.
                    175: #
1.18      www       176: BEGIN {
1.46      matthew   177:     # Variable initialization
                    178:     $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
                    179:     #
1.22      www       180:     unless ($readit) {
1.12      harris41  181: # ------------------------------------------------------------------- languages
                    182:     {
1.158     raeburn   183:         my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    184:                                    '/language.tab';
                    185:         if ( open(my $fh,"<$langtabfile") ) {
1.356     albertel  186:             while (my $line = <$fh>) {
                    187:                 next if ($line=~/^\#/);
                    188:                 chomp($line);
                    189:                 my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$line));
1.158     raeburn   190:                 $language{$key}=$val.' - '.$enc;
                    191:                 if ($sup) {
                    192:                     $supported_language{$key}=$sup;
                    193:                 }
                    194:             }
                    195:             close($fh);
                    196:         }
1.12      harris41  197:     }
                    198: # ------------------------------------------------------------------ copyrights
                    199:     {
1.158     raeburn   200:         my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    201:                                   '/copyright.tab';
                    202:         if ( open (my $fh,"<$copyrightfile") ) {
1.356     albertel  203:             while (my $line = <$fh>) {
                    204:                 next if ($line=~/^\#/);
                    205:                 chomp($line);
                    206:                 my ($key,$val)=(split(/\s+/,$line,2));
1.158     raeburn   207:                 $cprtag{$key}=$val;
                    208:             }
                    209:             close($fh);
                    210:         }
1.12      harris41  211:     }
1.351     www       212: # ----------------------------------------------------------- source copyrights
1.192     taceyjo1  213:     {
                    214:         my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    215:                                   '/source_copyright.tab';
                    216:         if ( open (my $fh,"<$sourcecopyrightfile") ) {
1.356     albertel  217:             while (my $line = <$fh>) {
                    218:                 next if ($line =~ /^\#/);
                    219:                 chomp($line);
                    220:                 my ($key,$val)=(split(/\s+/,$line,2));
1.192     taceyjo1  221:                 $scprtag{$key}=$val;
                    222:             }
                    223:             close($fh);
                    224:         }
                    225:     }
1.63      www       226: 
1.517     raeburn   227: # -------------------------------------------------------------- default domain designs
1.63      www       228:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517     raeburn   229:     my $designfile = $designdir.'/default.tab';
                    230:     if ( open (my $fh,"<$designfile") ) {
                    231:         while (my $line = <$fh>) {
                    232:             next if ($line =~ /^\#/);
                    233:             chomp($line);
                    234:             my ($key,$val)=(split(/\=/,$line));
                    235:             if ($val) { $defaultdesign{$key}=$val; }
                    236:         }
                    237:         close($fh);
1.63      www       238:     }
                    239: 
1.15      harris41  240: # ------------------------------------------------------------- file categories
                    241:     {
1.158     raeburn   242:         my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    243:                                   '/filecategories.tab';
                    244:         if ( open (my $fh,"<$categoryfile") ) {
1.356     albertel  245: 	    while (my $line = <$fh>) {
                    246: 		next if ($line =~ /^\#/);
                    247: 		chomp($line);
                    248:                 my ($extension,$category)=(split(/\s+/,$line,2));
1.158     raeburn   249:                 push @{$category_extensions{lc($category)}},$extension;
                    250:             }
                    251:             close($fh);
                    252:         }
                    253: 
1.15      harris41  254:     }
1.12      harris41  255: # ------------------------------------------------------------------ file types
                    256:     {
1.158     raeburn   257:         my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    258:                '/filetypes.tab';
                    259:         if ( open (my $fh,"<$typesfile") ) {
1.356     albertel  260:             while (my $line = <$fh>) {
                    261: 		next if ($line =~ /^\#/);
                    262: 		chomp($line);
                    263:                 my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158     raeburn   264:                 if ($descr ne '') {
                    265:                     $fe{$ending}=lc($emb);
                    266:                     $fd{$ending}=$descr;
1.351     www       267:                     if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158     raeburn   268:                 }
                    269:             }
                    270:             close($fh);
                    271:         }
1.12      harris41  272:     }
1.22      www       273:     &Apache::lonnet::logthis(
1.46      matthew   274:               "<font color=yellow>INFO: Read file types</font>");
1.22      www       275:     $readit=1;
1.46      matthew   276:     }  # end of unless($readit) 
1.32      matthew   277:     
                    278: }
1.112     bowersj2  279: 
1.42      matthew   280: ###############################################################
                    281: ##           HTML and Javascript Helper Functions            ##
                    282: ###############################################################
                    283: 
                    284: =pod 
                    285: 
1.112     bowersj2  286: =head1 HTML and Javascript Functions
1.42      matthew   287: 
1.112     bowersj2  288: =over 4
                    289: 
1.648     raeburn   290: =item * &browser_and_searcher_javascript()
1.112     bowersj2  291: 
                    292: X<browsing, javascript>X<searching, javascript>Returns a string
                    293: containing javascript with two functions, C<openbrowser> and
                    294: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
                    295: tags.
1.42      matthew   296: 
1.648     raeburn   297: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42      matthew   298: 
                    299: inputs: formname, elementname, only, omit
                    300: 
                    301: formname and elementname indicate the name of the html form and name of
                    302: the element that the results of the browsing selection are to be placed in. 
                    303: 
                    304: Specifying 'only' will restrict the browser to displaying only files
1.185     www       305: with the given extension.  Can be a comma separated list.
1.42      matthew   306: 
                    307: Specifying 'omit' will restrict the browser to NOT displaying files
1.185     www       308: with the given extension.  Can be a comma separated list.
1.42      matthew   309: 
1.648     raeburn   310: =item * &opensearcher(formname,elementname) [javascript]
1.42      matthew   311: 
                    312: Inputs: formname, elementname
                    313: 
                    314: formname and elementname specify the name of the html form and the name
                    315: of the element the selection from the search results will be placed in.
1.542     raeburn   316: 
1.42      matthew   317: =cut
                    318: 
                    319: sub browser_and_searcher_javascript {
1.199     albertel  320:     my ($mode)=@_;
                    321:     if (!defined($mode)) { $mode='edit'; }
1.453     albertel  322:     my $resurl=&escape_single(&lastresurl());
1.42      matthew   323:     return <<END;
1.219     albertel  324: // <!-- BEGIN LON-CAPA Internal
1.50      matthew   325:     var editbrowser = null;
1.135     albertel  326:     function openbrowser(formname,elementname,only,omit,titleelement) {
1.170     www       327:         var url = '$resurl/?';
1.42      matthew   328:         if (editbrowser == null) {
                    329:             url += 'launch=1&';
                    330:         }
                    331:         url += 'catalogmode=interactive&';
1.199     albertel  332:         url += 'mode=$mode&';
1.611     albertel  333:         url += 'inhibitmenu=yes&';
1.42      matthew   334:         url += 'form=' + formname + '&';
                    335:         if (only != null) {
                    336:             url += 'only=' + only + '&';
1.217     albertel  337:         } else {
                    338:             url += 'only=&';
                    339: 	}
1.42      matthew   340:         if (omit != null) {
                    341:             url += 'omit=' + omit + '&';
1.217     albertel  342:         } else {
                    343:             url += 'omit=&';
                    344: 	}
1.135     albertel  345:         if (titleelement != null) {
                    346:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  347:         } else {
                    348: 	    url += 'titleelement=&';
                    349: 	}
1.42      matthew   350:         url += 'element=' + elementname + '';
                    351:         var title = 'Browser';
1.435     albertel  352:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   353:         options += ',width=700,height=600';
                    354:         editbrowser = open(url,title,options,'1');
                    355:         editbrowser.focus();
                    356:     }
                    357:     var editsearcher;
1.135     albertel  358:     function opensearcher(formname,elementname,titleelement) {
1.42      matthew   359:         var url = '/adm/searchcat?';
                    360:         if (editsearcher == null) {
                    361:             url += 'launch=1&';
                    362:         }
                    363:         url += 'catalogmode=interactive&';
1.199     albertel  364:         url += 'mode=$mode&';
1.42      matthew   365:         url += 'form=' + formname + '&';
1.135     albertel  366:         if (titleelement != null) {
                    367:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  368:         } else {
                    369: 	    url += 'titleelement=&';
                    370: 	}
1.42      matthew   371:         url += 'element=' + elementname + '';
                    372:         var title = 'Search';
1.435     albertel  373:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   374:         options += ',width=700,height=600';
                    375:         editsearcher = open(url,title,options,'1');
                    376:         editsearcher.focus();
                    377:     }
1.219     albertel  378: // END LON-CAPA Internal -->
1.42      matthew   379: END
1.170     www       380: }
                    381: 
                    382: sub lastresurl {
1.258     albertel  383:     if ($env{'environment.lastresurl'}) {
                    384: 	return $env{'environment.lastresurl'}
1.170     www       385:     } else {
                    386: 	return '/res';
                    387:     }
                    388: }
                    389: 
                    390: sub storeresurl {
                    391:     my $resurl=&Apache::lonnet::clutter(shift);
                    392:     unless ($resurl=~/^\/res/) { return 0; }
                    393:     $resurl=~s/\/$//;
                    394:     &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646     raeburn   395:     &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170     www       396:     return 1;
1.42      matthew   397: }
                    398: 
1.74      www       399: sub studentbrowser_javascript {
1.111     www       400:    unless (
1.258     albertel  401:             (($env{'request.course.id'}) && 
1.302     albertel  402:              (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    403: 	      || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    404: 					  '/'.$env{'request.course.sec'})
                    405: 	      ))
1.258     albertel  406:          || ($env{'request.role'}=~/^(au|dc|su)/)
1.111     www       407:           ) { return ''; }  
1.74      www       408:    return (<<'ENDSTDBRW');
1.692.4.2  raeburn   409: <script type="text/javascript" language="Javascript">
1.692.4.4  raeburn   410: // <![CDATA[
1.74      www       411:     var stdeditbrowser;
1.692.4.2  raeburn   412:     function openstdbrowser(formname,uname,udom,roleflag,ignorefilter,courseadvonly) {
1.74      www       413:         var url = '/adm/pickstudent?';
                    414:         var filter;
1.558     albertel  415: 	if (!ignorefilter) {
                    416: 	    eval('filter=document.'+formname+'.'+uname+'.value;');
                    417: 	}
1.74      www       418:         if (filter != null) {
                    419:            if (filter != '') {
                    420:                url += 'filter='+filter+'&';
                    421: 	   }
                    422:         }
                    423:         url += 'form=' + formname + '&unameelement='+uname+
                    424:                                     '&udomelement='+udom;
1.111     www       425: 	if (roleflag) { url+="&roles=1"; }
1.692.4.2  raeburn   426:         if (courseadvonly) { url+="&courseadvonly=1"; }
1.102     www       427:         var title = 'Student_Browser';
1.74      www       428:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    429:         options += ',width=700,height=600';
                    430:         stdeditbrowser = open(url,title,options,'1');
                    431:         stdeditbrowser.focus();
                    432:     }
1.692.4.4  raeburn   433: // ]]>
1.74      www       434: </script>
                    435: ENDSTDBRW
                    436: }
1.42      matthew   437: 
1.74      www       438: sub selectstudent_link {
1.692.4.2  raeburn   439:    my ($form,$unameele,$udomele,$courseadvonly)=@_;
                    440:    my $callargs = "'".$form."','".$unameele."','".$udomele."'";
1.258     albertel  441:    if ($env{'request.course.id'}) {  
1.302     albertel  442:        if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    443: 	   && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    444: 					'/'.$env{'request.course.sec'})) {
1.111     www       445: 	   return '';
                    446:        }
1.692.4.2  raeburn   447:        if ($courseadvonly)  {
                    448:            $callargs .= ",'',1,1";
                    449:        }
                    450:        return '<span class="LC_nobreak">'.
                    451:               '<a href="javascript:openstdbrowser('.$callargs.');">'.
                    452:               &mt('Select User').'</a></span>';
1.74      www       453:    }
1.258     albertel  454:    if ($env{'request.role'}=~/^(au|dc|su)/) {
1.692.4.2  raeburn   455:        $callargs .= ",1";
                    456:        return '<span class="LC_nobreak">'.
                    457:               '<a href="javascript:openstdbrowser('.$callargs.');">'.
                    458:               &mt('Select User').'</a></span>';
1.111     www       459:    }
                    460:    return '';
1.91      www       461: }
                    462: 
1.653     raeburn   463: sub authorbrowser_javascript {
                    464:     return <<"ENDAUTHORBRW";
                    465: <script type="text/javascript">
1.692.4.4  raeburn   466: // <![CDATA[
1.653     raeburn   467: var stdeditbrowser;
                    468: 
                    469: function openauthorbrowser(formname,udom) {
                    470:     var url = '/adm/pickauthor?';
                    471:     url += 'form='+formname+'&roledom='+udom;
                    472:     var title = 'Author_Browser';
                    473:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    474:     options += ',width=700,height=600';
                    475:     stdeditbrowser = open(url,title,options,'1');
                    476:     stdeditbrowser.focus();
                    477: }
1.692.4.4  raeburn   478: // ]]>
1.653     raeburn   479: </script>
                    480: ENDAUTHORBRW
                    481: }
                    482: 
1.91      www       483: sub coursebrowser_javascript {
1.692.4.22! raeburn   484:     my ($domainfilter,$sec_element,$formname,$role_element,$crstype) = @_;
        !           485:     my $wintitle = 'Course_Browser';
        !           486:     if ($crstype eq 'Community') {
        !           487:         $wintitle = 'Community_Browser';
        !           488:     }
1.692.4.9  raeburn   489:     my $id_functions = &javascript_index_functions();
                    490:     my $output = '
1.692.4.2  raeburn   491: <script type="text/javascript" language="JavaScript">
1.692.4.4  raeburn   492: // <![CDATA[
1.468     raeburn   493:     var stdeditbrowser;'."\n";
1.692.4.9  raeburn   494: 
                    495:     $output .= <<"ENDSTDBRW";
1.692.4.22! raeburn   496:     function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,type,type_elem) {
1.91      www       497:         var url = '/adm/pickcourse?';
1.692.4.18  raeburn   498:         var formid = getFormIdByName(formname);
1.692.4.9  raeburn   499:         var domainfilter = getDomainFromSelectbox(formname,udom);
1.128     albertel  500:         if (domainfilter != null) {
                    501:            if (domainfilter != '') {
                    502:                url += 'domainfilter='+domainfilter+'&';
                    503: 	   }
                    504:         }
1.91      www       505:         url += 'form=' + formname + '&cnumelement='+uname+
1.187     albertel  506: 	                            '&cdomelement='+udom+
                    507:                                     '&cnameelement='+desc;
1.468     raeburn   508:         if (extra_element !=null && extra_element != '') {
1.594     raeburn   509:             if (formname == 'rolechoice' || formname == 'studentform') {
1.468     raeburn   510:                 url += '&roleelement='+extra_element;
                    511:                 if (domainfilter == null || domainfilter == '') {
                    512:                     url += '&domainfilter='+extra_element;
                    513:                 }
1.234     raeburn   514:             }
1.468     raeburn   515:             else {
                    516:                 if (formname == 'portform') {
                    517:                     url += '&setroles='+extra_element;
                    518:                 }
                    519:             }     
1.230     raeburn   520:         }
1.692.4.22! raeburn   521:         if (type != null && type != '') {
        !           522:             url += '&type='+type;
        !           523:         }
        !           524:         if (type_elem != null && type_elem != '') {
        !           525:             url += '&typeelement='+type_elem;
        !           526:         }
1.692.4.7  raeburn   527:         if (formname == 'ccrs') {
                    528:             var ownername = document.forms[formid].ccuname.value;
                    529:             var ownerdom =  document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
                    530:             url += '&cloner='+ownername+':'+ownerdom;
                    531:         }
1.293     raeburn   532:         if (multflag !=null && multflag != '') {
                    533:             url += '&multiple='+multflag;
                    534:         }
1.692.4.22! raeburn   535:         var title = '$wintitle';
1.91      www       536:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    537:         options += ',width=700,height=600';
                    538:         stdeditbrowser = open(url,title,options,'1');
                    539:         stdeditbrowser.focus();
                    540:     }
1.692.4.9  raeburn   541: $id_functions
1.91      www       542: ENDSTDBRW
1.692.4.21  raeburn   543:     if (($sec_element ne '') || ($role_element ne '')) {
                    544:         $output .= &setsec_javascript($sec_element,$formname,$role_element);
1.468     raeburn   545:     }
                    546:     $output .= '
1.692.4.4  raeburn   547: // ]]>
1.468     raeburn   548: </script>';
                    549:     return $output;
                    550: }
                    551: 
1.692.4.9  raeburn   552: sub javascript_index_functions {
                    553:     return <<"ENDJS";
                    554: 
                    555: function getFormIdByName(formname) {
                    556:     for (var i=0;i<document.forms.length;i++) {
                    557:         if (document.forms[i].name == formname) {
                    558:             return i;
                    559:         }
                    560:     }
                    561:     return -1;
                    562: }
                    563: 
                    564: function getIndexByName(formid,item) {
                    565:     for (var i=0;i<document.forms[formid].elements.length;i++) {
                    566:         if (document.forms[formid].elements[i].name == item) {
                    567:             return i;
                    568:         }
                    569:     }
                    570:     return -1;
                    571: }
                    572: 
                    573: function getDomainFromSelectbox(formname,udom) {
                    574:     var userdom;
                    575:     var formid = getFormIdByName(formname);
                    576:     if (formid > -1) {
                    577:         var domid = getIndexByName(formid,udom);
                    578:         if (domid > -1) {
                    579:             if (document.forms[formid].elements[domid].type == 'select-one') {
                    580:                 userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
                    581:             }
                    582:             if (document.forms[formid].elements[domid].type == 'hidden') {
                    583:                 userdom=document.forms[formid].elements[domid].value;
                    584:             }
                    585:         }
                    586:     }
                    587:     return userdom;
                    588: }
                    589: 
                    590: ENDJS
                    591: 
                    592: }
                    593: 
                    594: sub userbrowser_javascript {
                    595:     my $id_functions = &javascript_index_functions();
                    596:     return <<"ENDUSERBRW";
                    597: 
1.692.4.17  raeburn   598: function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
1.692.4.9  raeburn   599:     var url = '/adm/pickuser?';
                    600:     var userdom = getDomainFromSelectbox(formname,udom);
                    601:     if (userdom != null) {
                    602:        if (userdom != '') {
                    603:            url += 'srchdom='+userdom+'&';
                    604:        }
                    605:     }
                    606:     url += 'form=' + formname + '&unameelement='+uname+
                    607:                                 '&udomelement='+udom+
                    608:                                 '&ulastelement='+ulast+
                    609:                                 '&ufirstelement='+ufirst+
                    610:                                 '&uemailelement='+uemail+
                    611:                                 '&hideudomelement='+hideudom+
                    612:                                 '&coursedom='+crsdom;
1.692.4.17  raeburn   613:     if ((caller != null) && (caller != undefined)) {
                    614:         url += '&caller='+caller;
                    615:     }
1.692.4.9  raeburn   616:     var title = 'User_Browser';
                    617:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    618:     options += ',width=700,height=600';
                    619:     var stdeditbrowser = open(url,title,options,'1');
                    620:     stdeditbrowser.focus();
                    621: }
                    622: 
1.692.4.17  raeburn   623: function fix_domain (formname,udom,origdom,uname) {
1.692.4.9  raeburn   624:     var formid = getFormIdByName(formname);
                    625:     if (formid > -1) {
1.692.4.17  raeburn   626:         var unameid = getIndexByName(formid,uname);
1.692.4.9  raeburn   627:         var domid = getIndexByName(formid,udom);
                    628:         var hidedomid = getIndexByName(formid,origdom);
                    629:         if (hidedomid > -1) {
                    630:             var fixeddom = document.forms[formid].elements[hidedomid].value;
1.692.4.17  raeburn   631:             var unameval = document.forms[formid].elements[unameid].value;
                    632:             if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
                    633:                 if (domid > -1) {
                    634:                     var slct = document.forms[formid].elements[domid];
                    635:                     if (slct.type == 'select-one') {
                    636:                         var i;
                    637:                         for (i=0;i<slct.length;i++) {
                    638:                             if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
                    639:                         }
                    640:                     }
                    641:                     if (slct.type == 'hidden') {
                    642:                         slct.value = fixeddom;
1.692.4.9  raeburn   643:                     }
                    644:                 }
                    645:             }
                    646:         }
                    647:     }
                    648:     return;
                    649: }
                    650: 
                    651: $id_functions
                    652: ENDUSERBRW
                    653: }
                    654: 
                    655: 
1.468     raeburn   656: sub setsec_javascript {
1.692.4.21  raeburn   657:     my ($sec_element,$formname,$role_element) = @_;
                    658:     my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
                    659:         $communityrolestr);
                    660:     if ($role_element ne '') {
                    661:         my @allroles = ('st','ta','ep','in','ad');
                    662:         foreach my $crstype ('Course','Community') {
                    663:             if ($crstype eq 'Community') {
                    664:                 foreach my $role (@allroles) {
                    665:                     push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
                    666:                 }
                    667:                 push(@communityrolenames,&Apache::lonnet::plaintext('co'));
                    668:             } else {
                    669:                 foreach my $role (@allroles) {
                    670:                     push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
                    671:                 }
                    672:                 push(@courserolenames,&Apache::lonnet::plaintext('cc'));
                    673:             }
                    674:         }
                    675:         $rolestr = '"'.join('","',@allroles).'"';
                    676:         $courserolestr = '"'.join('","',@courserolenames).'"';
                    677:         $communityrolestr = '"'.join('","',@communityrolenames).'"';
                    678:     }
1.468     raeburn   679:     my $setsections = qq|
                    680: function setSect(sectionlist) {
1.629     raeburn   681:     var sectionsArray = new Array();
                    682:     if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
                    683:         sectionsArray = sectionlist.split(",");
                    684:     }
1.468     raeburn   685:     var numSections = sectionsArray.length;
                    686:     document.$formname.$sec_element.length = 0;
                    687:     if (numSections == 0) {
                    688:         document.$formname.$sec_element.multiple=false;
                    689:         document.$formname.$sec_element.size=1;
                    690:         document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
                    691:     } else {
                    692:         if (numSections == 1) {
                    693:             document.$formname.$sec_element.multiple=false;
                    694:             document.$formname.$sec_element.size=1;
                    695:             document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
                    696:             document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
                    697:             document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
                    698:         } else {
                    699:             for (var i=0; i<numSections; i++) {
                    700:                 document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
                    701:             }
                    702:             document.$formname.$sec_element.multiple=true
                    703:             if (numSections < 3) {
                    704:                 document.$formname.$sec_element.size=numSections;
                    705:             } else {
                    706:                 document.$formname.$sec_element.size=3;
                    707:             }
                    708:             document.$formname.$sec_element.options[0].selected = false
                    709:         }
                    710:     }
1.91      www       711: }
1.692.4.21  raeburn   712: 
                    713: function setRole(crstype) {
                    714: |;
                    715:     if ($role_element eq '') {
                    716:         $setsections .= '    return;
                    717: }
                    718: ';
                    719:     } else {
                    720:         $setsections .= qq|
                    721:     var elementLength = document.$formname.$role_element.length;
                    722:     var allroles = Array($rolestr);
                    723:     var courserolenames = Array($courserolestr);
                    724:     var communityrolenames = Array($communityrolestr);
                    725:     if (elementLength != undefined) {
                    726:         if (document.$formname.$role_element.options[5].value == 'cc') {
                    727:             if (crstype == 'Course') {
                    728:                 return;
                    729:             } else {
                    730:                 allroles[5] = 'co';
                    731:                 for (var i=0; i<6; i++) {
                    732:                     document.$formname.$role_element.options[i].value = allroles[i];
                    733:                     document.$formname.$role_element.options[i].text = communityrolenames[i];
                    734:                 }
                    735:             }
                    736:         } else {
                    737:             if (crstype == 'Community') {
                    738:                 return;
                    739:             } else {
                    740:                 allroles[5] = 'cc';
                    741:                 for (var i=0; i<6; i++) {
                    742:                     document.$formname.$role_element.options[i].value = allroles[i];
                    743:                     document.$formname.$role_element.options[i].text = courserolenames[i];
                    744:                 }
                    745:             }
                    746:         }
                    747:     }
                    748:     return;
                    749: }
1.468     raeburn   750: |;
1.692.4.21  raeburn   751:     }
1.468     raeburn   752:     return $setsections;
                    753: }
                    754: 
1.91      www       755: sub selectcourse_link {
1.692.4.22! raeburn   756:    my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype,
        !           757:        $typeelement) = @_;
        !           758:    my $type = $selecttype;
1.692.4.6  raeburn   759:    my $linktext = &mt('Select Course');
                    760:    if ($selecttype eq 'Community') {
                    761:        $linktext = &mt('Select Community');
1.692.4.22! raeburn   762:    } elsif ($selecttype eq 'Course/Community') {
        !           763:        $linktext = &mt('Select Course/Community');
        !           764:        $type = '';
1.692.4.6  raeburn   765:    }
1.692.4.2  raeburn   766:    return '<span class="LC_nobreak">'
                    767:          ."<a href='"
                    768:          .'javascript:opencrsbrowser("'.$form.'","'.$unameele
                    769:          .'","'.$udomele.'","'.$desc.'","'.$extra_element
1.692.4.22! raeburn   770:          .'","'.$multflag.'","'.$type.'","'.$typeelement.'");'
1.692.4.6  raeburn   771:          ."'>".$linktext.'</a>'
1.692.4.2  raeburn   772:          .'</span>';
1.74      www       773: }
1.42      matthew   774: 
1.653     raeburn   775: sub selectauthor_link {
                    776:    my ($form,$udom)=@_;
                    777:    return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
                    778:           &mt('Select Author').'</a>';
                    779: }
                    780: 
1.692.4.9  raeburn   781: sub selectuser_link {
                    782:     my ($form,$unameelem,$domelem,$lastelem,$firstelem,$emailelem,$hdomelem,
1.692.4.17  raeburn   783:         $coursedom,$linktext,$caller) = @_;
1.692.4.9  raeburn   784:     return '<a href="javascript:openuserbrowser('."'$form','$unameelem','$domelem',".
1.692.4.17  raeburn   785:            "'$lastelem','$firstelem','$emailelem','$hdomelem','$coursedom','$caller'".
1.692.4.9  raeburn   786:            ');">'.$linktext.'</a>';
                    787: }
                    788: 
1.273     raeburn   789: sub check_uncheck_jscript {
                    790:     my $jscript = <<"ENDSCRT";
                    791: function checkAll(field) {
                    792:     if (field.length > 0) {
                    793:         for (i = 0; i < field.length; i++) {
                    794:             field[i].checked = true ;
                    795:         }
                    796:     } else {
                    797:         field.checked = true
                    798:     }
                    799: }
                    800:  
                    801: function uncheckAll(field) {
                    802:     if (field.length > 0) {
                    803:         for (i = 0; i < field.length; i++) {
                    804:             field[i].checked = false ;
1.543     albertel  805:         }
                    806:     } else {
1.273     raeburn   807:         field.checked = false ;
                    808:     }
                    809: }
                    810: ENDSCRT
                    811:     return $jscript;
                    812: }
                    813: 
1.656     www       814: sub select_timezone {
1.659     raeburn   815:    my ($name,$selected,$onchange,$includeempty)=@_;
                    816:    my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
                    817:    if ($includeempty) {
                    818:        $output .= '<option value=""';
                    819:        if (($selected eq '') || ($selected eq 'local')) {
                    820:            $output .= ' selected="selected" ';
                    821:        }
                    822:        $output .= '> </option>';
                    823:    }
1.657     raeburn   824:    my @timezones = DateTime::TimeZone->all_names;
                    825:    foreach my $tzone (@timezones) {
                    826:        $output.= '<option value="'.$tzone.'"';
                    827:        if ($tzone eq $selected) {
                    828:            $output.=' selected="selected"';
                    829:        }
                    830:        $output.=">$tzone</option>\n";
1.656     www       831:    }
                    832:    $output.="</select>";
                    833:    return $output;
                    834: }
1.273     raeburn   835: 
1.687     raeburn   836: sub select_datelocale {
                    837:     my ($name,$selected,$onchange,$includeempty)=@_;
                    838:     my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
                    839:     if ($includeempty) {
                    840:         $output .= '<option value=""';
                    841:         if ($selected eq '') {
                    842:             $output .= ' selected="selected" ';
                    843:         }
                    844:         $output .= '> </option>';
                    845:     }
                    846:     my (@possibles,%locale_names);
                    847:     my @locales = DateTime::Locale::Catalog::Locales;
                    848:     foreach my $locale (@locales) {
                    849:         if (ref($locale) eq 'HASH') {
                    850:             my $id = $locale->{'id'};
                    851:             if ($id ne '') {
                    852:                 my $en_terr = $locale->{'en_territory'};
                    853:                 my $native_terr = $locale->{'native_territory'};
1.692.4.1  raeburn   854:                 my @languages = &Apache::lonlocal::preferred_languages();
1.687     raeburn   855:                 if (grep(/^en$/,@languages) || !@languages) {
                    856:                     if ($en_terr ne '') {
                    857:                         $locale_names{$id} = '('.$en_terr.')';
                    858:                     } elsif ($native_terr ne '') {
                    859:                         $locale_names{$id} = $native_terr;
                    860:                     }
                    861:                 } else {
                    862:                     if ($native_terr ne '') {
                    863:                         $locale_names{$id} = $native_terr.' ';
                    864:                     } elsif ($en_terr ne '') {
                    865:                         $locale_names{$id} = '('.$en_terr.')';
                    866:                     }
                    867:                 }
                    868:                 push (@possibles,$id);
                    869:             }
                    870:         }
                    871:     }
                    872:     foreach my $item (sort(@possibles)) {
                    873:         $output.= '<option value="'.$item.'"';
                    874:         if ($item eq $selected) {
                    875:             $output.=' selected="selected"';
                    876:         }
                    877:         $output.=">$item";
                    878:         if ($locale_names{$item} ne '') {
                    879:             $output.="  $locale_names{$item}</option>\n";
                    880:         }
                    881:         $output.="</option>\n";
                    882:     }
                    883:     $output.="</select>";
                    884:     return $output;
                    885: }
                    886: 
1.692.4.2  raeburn   887: sub select_language {
                    888:     my ($name,$selected,$includeempty) = @_;
                    889:     my %langchoices;
                    890:     if ($includeempty) {
                    891:         %langchoices = ('' => 'No language preference');
                    892:     }
                    893:     foreach my $id (&languageids()) {
                    894:         my $code = &supportedlanguagecode($id);
                    895:         if ($code) {
                    896:             $langchoices{$code} = &plainlanguagedescription($id);
                    897:         }
                    898:     }
                    899:     return &select_form($selected,$name,%langchoices);
                    900: }
                    901: 
1.42      matthew   902: =pod
1.36      matthew   903: 
1.648     raeburn   904: =item * &linked_select_forms(...)
1.36      matthew   905: 
                    906: linked_select_forms returns a string containing a <script></script> block
                    907: and html for two <select> menus.  The select menus will be linked in that
                    908: changing the value of the first menu will result in new values being placed
                    909: in the second menu.  The values in the select menu will appear in alphabetical
1.609     raeburn   910: order unless a defined order is provided.
1.36      matthew   911: 
                    912: linked_select_forms takes the following ordered inputs:
                    913: 
                    914: =over 4
                    915: 
1.112     bowersj2  916: =item * $formname, the name of the <form> tag
1.36      matthew   917: 
1.112     bowersj2  918: =item * $middletext, the text which appears between the <select> tags
1.36      matthew   919: 
1.112     bowersj2  920: =item * $firstdefault, the default value for the first menu
1.36      matthew   921: 
1.112     bowersj2  922: =item * $firstselectname, the name of the first <select> tag
1.36      matthew   923: 
1.112     bowersj2  924: =item * $secondselectname, the name of the second <select> tag
1.36      matthew   925: 
1.112     bowersj2  926: =item * $hashref, a reference to a hash containing the data for the menus.
1.36      matthew   927: 
1.609     raeburn   928: =item * $menuorder, the order of values in the first menu
                    929: 
1.41      ng        930: =back 
                    931: 
1.36      matthew   932: Below is an example of such a hash.  Only the 'text', 'default', and 
                    933: 'select2' keys must appear as stated.  keys(%menu) are the possible 
                    934: values for the first select menu.  The text that coincides with the 
1.41      ng        935: first menu value is given in $menu{$choice1}->{'text'}.  The values 
1.36      matthew   936: and text for the second menu are given in the hash pointed to by 
                    937: $menu{$choice1}->{'select2'}.  
                    938: 
1.112     bowersj2  939:  my %menu = ( A1 => { text =>"Choice A1" ,
                    940:                        default => "B3",
                    941:                        select2 => { 
                    942:                            B1 => "Choice B1",
                    943:                            B2 => "Choice B2",
                    944:                            B3 => "Choice B3",
                    945:                            B4 => "Choice B4"
1.609     raeburn   946:                            },
                    947:                        order => ['B4','B3','B1','B2'],
1.112     bowersj2  948:                    },
                    949:                A2 => { text =>"Choice A2" ,
                    950:                        default => "C2",
                    951:                        select2 => { 
                    952:                            C1 => "Choice C1",
                    953:                            C2 => "Choice C2",
                    954:                            C3 => "Choice C3"
1.609     raeburn   955:                            },
                    956:                        order => ['C2','C1','C3'],
1.112     bowersj2  957:                    },
                    958:                A3 => { text =>"Choice A3" ,
                    959:                        default => "D6",
                    960:                        select2 => { 
                    961:                            D1 => "Choice D1",
                    962:                            D2 => "Choice D2",
                    963:                            D3 => "Choice D3",
                    964:                            D4 => "Choice D4",
                    965:                            D5 => "Choice D5",
                    966:                            D6 => "Choice D6",
                    967:                            D7 => "Choice D7"
1.609     raeburn   968:                            },
                    969:                        order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112     bowersj2  970:                    }
                    971:                );
1.36      matthew   972: 
                    973: =cut
                    974: 
                    975: sub linked_select_forms {
                    976:     my ($formname,
                    977:         $middletext,
                    978:         $firstdefault,
                    979:         $firstselectname,
                    980:         $secondselectname, 
1.609     raeburn   981:         $hashref,
                    982:         $menuorder,
1.36      matthew   983:         ) = @_;
                    984:     my $second = "document.$formname.$secondselectname";
                    985:     my $first = "document.$formname.$firstselectname";
                    986:     # output the javascript to do the changing
                    987:     my $result = '';
1.692.4.2  raeburn   988:     $result.='<script type="text/javascript" language="JavaScript">'."\n";
1.692.4.4  raeburn   989:     $result.="// <![CDATA[\n";
1.36      matthew   990:     $result.="var select2data = new Object();\n";
                    991:     $" = '","';
                    992:     my $debug = '';
                    993:     foreach my $s1 (sort(keys(%$hashref))) {
                    994:         $result.="select2data.d_$s1 = new Object();\n";        
                    995:         $result.="select2data.d_$s1.def = new String('".
                    996:             $hashref->{$s1}->{'default'}."');\n";
1.609     raeburn   997:         $result.="select2data.d_$s1.values = new Array(";
1.36      matthew   998:         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609     raeburn   999:         if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
                   1000:             @s2values = @{$hashref->{$s1}->{'order'}};
                   1001:         }
1.36      matthew  1002:         $result.="\"@s2values\");\n";
                   1003:         $result.="select2data.d_$s1.texts = new Array(";        
                   1004:         my @s2texts;
                   1005:         foreach my $value (@s2values) {
                   1006:             push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
                   1007:         }
                   1008:         $result.="\"@s2texts\");\n";
                   1009:     }
                   1010:     $"=' ';
                   1011:     $result.= <<"END";
                   1012: 
                   1013: function select1_changed() {
                   1014:     // Determine new choice
                   1015:     var newvalue = "d_" + $first.value;
                   1016:     // update select2
                   1017:     var values     = select2data[newvalue].values;
                   1018:     var texts      = select2data[newvalue].texts;
                   1019:     var select2def = select2data[newvalue].def;
                   1020:     var i;
                   1021:     // out with the old
                   1022:     for (i = 0; i < $second.options.length; i++) {
                   1023:         $second.options[i] = null;
                   1024:     }
                   1025:     // in with the nuclear
                   1026:     for (i=0;i<values.length; i++) {
                   1027:         $second.options[i] = new Option(values[i]);
1.143     matthew  1028:         $second.options[i].value = values[i];
1.36      matthew  1029:         $second.options[i].text = texts[i];
                   1030:         if (values[i] == select2def) {
                   1031:             $second.options[i].selected = true;
                   1032:         }
                   1033:     }
                   1034: }
1.692.4.4  raeburn  1035: // ]]>
1.36      matthew  1036: </script>
                   1037: END
                   1038:     # output the initial values for the selection lists
                   1039:     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed()\">\n";
1.609     raeburn  1040:     my @order = sort(keys(%{$hashref}));
                   1041:     if (ref($menuorder) eq 'ARRAY') {
                   1042:         @order = @{$menuorder};
                   1043:     }
                   1044:     foreach my $value (@order) {
1.36      matthew  1045:         $result.="    <option value=\"$value\" ";
1.253     albertel 1046:         $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119     www      1047:         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36      matthew  1048:     }
                   1049:     $result .= "</select>\n";
                   1050:     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
                   1051:     $result .= $middletext;
                   1052:     $result .= "<select size=\"1\" name=\"$secondselectname\">\n";
                   1053:     my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609     raeburn  1054:     
                   1055:     my @secondorder = sort(keys(%select2));
                   1056:     if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
                   1057:         @secondorder = @{$hashref->{$firstdefault}->{'order'}};
                   1058:     }
                   1059:     foreach my $value (@secondorder) {
1.36      matthew  1060:         $result.="    <option value=\"$value\" ";        
1.253     albertel 1061:         $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119     www      1062:         $result.=">".&mt($select2{$value})."</option>\n";
1.36      matthew  1063:     }
                   1064:     $result .= "</select>\n";
                   1065:     #    return $debug;
                   1066:     return $result;
                   1067: }   #  end of sub linked_select_forms {
                   1068: 
1.45      matthew  1069: =pod
1.44      bowersj2 1070: 
1.648     raeburn  1071: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height)
1.44      bowersj2 1072: 
1.112     bowersj2 1073: Returns a string corresponding to an HTML link to the given help
                   1074: $topic, where $topic corresponds to the name of a .tex file in
                   1075: /home/httpd/html/adm/help/tex, with underscores replaced by
                   1076: spaces. 
                   1077: 
                   1078: $text will optionally be linked to the same topic, allowing you to
                   1079: link text in addition to the graphic. If you do not want to link
                   1080: text, but wish to specify one of the later parameters, pass an
                   1081: empty string. 
                   1082: 
                   1083: $stayOnPage is a value that will be interpreted as a boolean. If true,
                   1084: the link will not open a new window. If false, the link will open
                   1085: a new window using Javascript. (Default is false.) 
                   1086: 
                   1087: $width and $height are optional numerical parameters that will
                   1088: override the width and height of the popped up window, which may
                   1089: be useful for certain help topics with big pictures included. 
1.44      bowersj2 1090: 
                   1091: =cut
                   1092: 
                   1093: sub help_open_topic {
1.48      bowersj2 1094:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
                   1095:     $text = "" if (not defined $text);
1.44      bowersj2 1096:     $stayOnPage = 0 if (not defined $stayOnPage);
1.552     banghart 1097:     if ($env{'browser.interface'} eq 'textual') {
1.79      www      1098: 	$stayOnPage=1;
                   1099:     }
1.44      bowersj2 1100:     $width = 350 if (not defined $width);
                   1101:     $height = 400 if (not defined $height);
                   1102:     my $filename = $topic;
                   1103:     $filename =~ s/ /_/g;
                   1104: 
1.48      bowersj2 1105:     my $template = "";
                   1106:     my $link;
1.572     banghart 1107:     
1.159     www      1108:     $topic=~s/\W/\_/g;
1.44      bowersj2 1109: 
1.572     banghart 1110:     if (!$stayOnPage) {
1.72      bowersj2 1111: 	$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 1112:     } else {
1.48      bowersj2 1113: 	$link = "/adm/help/${filename}.hlp";
                   1114:     }
                   1115: 
                   1116:     # Add the text
1.572     banghart 1117:     if ($text ne "") {
1.77      www      1118: 	$template .= 
1.572     banghart 1119:             "<table bgcolor='#3333AA' cellspacing='1' cellpadding='1' border='0'><tr>".
1.691     bisitz   1120:             "<td bgcolor='#5555FF'><span class=\"LC_nobreak\"><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.48      bowersj2 1121:     }
                   1122: 
                   1123:     # Add the graphic
1.179     matthew  1124:     my $title = &mt('Online Help');
1.667     raeburn  1125:     my $helpicon=&lonhttpdurl("/adm/help/help.png");
1.692.4.2  raeburn  1126:     $template .= '<a target="_top" href="'.$link.'" title="'.$title.'">'.
                   1127:                  '<img src="'.$helpicon.'" border="0" alt="'.&mt('Help: [_1]',$topic).
                   1128:                  '" title="'.$title.'" /></a>';
                   1129:     if ($text ne '') {
                   1130:         $template.='</span></td></tr></table>';
                   1131:     }
1.44      bowersj2 1132:     return $template;
                   1133: 
1.106     bowersj2 1134: }
                   1135: 
                   1136: # This is a quicky function for Latex cheatsheet editing, since it 
                   1137: # appears in at least four places
                   1138: sub helpLatexCheatsheet {
1.692.4.2  raeburn  1139:     my ($topic,$text,$not_author) = @_;
                   1140:     my $out;
1.106     bowersj2 1141:     my $addOther = '';
1.692.4.3  raeburn  1142:     if ($topic) {
1.692.4.2  raeburn  1143: 	$addOther = &Apache::loncommon::help_open_topic($topic,$text,
1.106     bowersj2 1144: 						       undef, undef, 600) .
                   1145: 							   '</td><td>';
                   1146:     }
1.692.4.2  raeburn  1147:     $out = '<table><tr><td>'.
                   1148:            $addOther .
                   1149:            &Apache::loncommon::help_open_topic("Greek_Symbols",&mt('Greek Symbols'),
                   1150:                                                undef,undef,600).
                   1151:            '</td><td>'.
                   1152:            &Apache::loncommon::help_open_topic("Other_Symbols",&mt('Other Symbols'),
                   1153:                                                undef,undef,600).
                   1154:            '</td>';
                   1155:     unless ($not_author) {
                   1156:         $out .= '<td>'.
                   1157:                 &Apache::loncommon::help_open_topic("Authoring_Output_Tags",&mt('Output Tags'),
                   1158:                                                     undef,undef,600).
                   1159:                 '</td>';
                   1160:     }
                   1161:     $out .= '</tr></table>';
                   1162:     return $out;
1.172     www      1163: }
                   1164: 
1.430     albertel 1165: sub general_help {
                   1166:     my $helptopic='Student_Intro';
                   1167:     if ($env{'request.role'}=~/^(ca|au)/) {
                   1168: 	$helptopic='Authoring_Intro';
1.692.4.22! raeburn  1169:     } elsif ($env{'request.role'}=~/^(cc|co)/) {
1.430     albertel 1170: 	$helptopic='Course_Coordination_Intro';
1.672     raeburn  1171:     } elsif ($env{'request.role'}=~/^dc/) {
                   1172:         $helptopic='Domain_Coordination_Intro';
1.430     albertel 1173:     }
                   1174:     return $helptopic;
                   1175: }
                   1176: 
                   1177: sub update_help_link {
                   1178:     my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
                   1179:     my $origurl = $ENV{'REQUEST_URI'};
                   1180:     $origurl=~s|^/~|/priv/|;
                   1181:     my $timestamp = time;
                   1182:     foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
                   1183:         $$datum = &escape($$datum);
                   1184:     }
                   1185: 
                   1186:     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";
                   1187:     my $output .= <<"ENDOUTPUT";
                   1188: <script type="text/javascript">
1.692.4.4  raeburn  1189: // <![CDATA[
1.430     albertel 1190: banner_link = '$banner_link';
1.692.4.4  raeburn  1191: // ]]>
1.430     albertel 1192: </script>
                   1193: ENDOUTPUT
                   1194:     return $output;
                   1195: }
                   1196: 
                   1197: # now just updates the help link and generates a blue icon
1.193     raeburn  1198: sub help_open_menu {
1.430     albertel 1199:     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text) 
1.552     banghart 1200: 	= @_;    
1.430     albertel 1201:     $stayOnPage = 0 if (not defined $stayOnPage);
1.572     banghart 1202:     # only use pop-up help (stayOnPage == 0)
1.552     banghart 1203:     # if environment.remote is on (using remote control UI)
1.572     banghart 1204:     if ($env{'browser.interface'} eq 'textual' ||
                   1205:     	$env{'environment.remote'} eq 'off' ) {
1.552     banghart 1206:         $stayOnPage=1;
1.430     albertel 1207:     }
                   1208:     my $output;
                   1209:     if ($component_help) {
                   1210: 	if (!$text) {
                   1211: 	    $output=&help_open_topic($component_help,undef,$stayOnPage,
                   1212: 				       $width,$height);
                   1213: 	} else {
                   1214: 	    my $help_text;
                   1215: 	    $help_text=&unescape($topic);
                   1216: 	    $output='<table><tr><td>'.
                   1217: 		&help_open_topic($component_help,$help_text,$stayOnPage,
                   1218: 				 $width,$height).'</td></tr></table>';
                   1219: 	}
                   1220:     }
                   1221:     my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
                   1222:     return $output.$banner_link;
                   1223: }
                   1224: 
                   1225: sub top_nav_help {
                   1226:     my ($text) = @_;
1.436     albertel 1227:     $text = &mt($text);
1.572     banghart 1228:     my $stay_on_page = 
1.436     albertel 1229: 	($env{'browser.interface'}  eq 'textual' ||
                   1230: 	 $env{'environment.remote'} eq 'off' );
1.572     banghart 1231:     my $link = ($stay_on_page) ? "javascript:helpMenu('display')"
1.436     albertel 1232: 	                     : "javascript:helpMenu('open')";
1.572     banghart 1233:     my $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
1.436     albertel 1234: 
1.201     raeburn  1235:     my $title = &mt('Get help');
1.436     albertel 1236: 
                   1237:     return <<"END";
                   1238: $banner_link
                   1239:  <a href="$link" title="$title">$text</a>
                   1240: END
                   1241: }
                   1242: 
                   1243: sub help_menu_js {
                   1244:     my ($text) = @_;
                   1245: 
                   1246:     my $stayOnPage = 
                   1247: 	($env{'browser.interface'}  eq 'textual' ||
                   1248: 	 $env{'environment.remote'} eq 'off' );
                   1249: 
                   1250:     my $width = 620;
                   1251:     my $height = 600;
1.430     albertel 1252:     my $helptopic=&general_help();
                   1253:     my $details_link = '/adm/help/'.$helptopic.'.hlp';
1.261     albertel 1254:     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331     albertel 1255:     my $start_page =
                   1256:         &Apache::loncommon::start_page('Help Menu', undef,
                   1257: 				       {'frameset'    => 1,
                   1258: 					'js_ready'    => 1,
                   1259: 					'add_entries' => {
                   1260: 					    'border' => '0',
1.579     raeburn  1261: 					    'rows'   => "110,*",},});
1.331     albertel 1262:     my $end_page =
                   1263:         &Apache::loncommon::end_page({'frameset' => 1,
                   1264: 				      'js_ready' => 1,});
                   1265: 
1.436     albertel 1266:     my $template .= <<"ENDTEMPLATE";
                   1267: <script type="text/javascript">
1.253     albertel 1268: // <![CDATA[
1.692.4.10  raeburn  1269: // <!-- BEGIN LON-CAPA Internal
1.430     albertel 1270: var banner_link = '';
1.243     raeburn  1271: function helpMenu(target) {
                   1272:     var caller = this;
                   1273:     if (target == 'open') {
                   1274:         var newWindow = null;
                   1275:         try {
1.262     albertel 1276:             newWindow =  window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243     raeburn  1277:         }
                   1278:         catch(error) {
                   1279:             writeHelp(caller);
                   1280:             return;
                   1281:         }
                   1282:         if (newWindow) {
                   1283:             caller = newWindow;
                   1284:         }
1.193     raeburn  1285:     }
1.243     raeburn  1286:     writeHelp(caller);
                   1287:     return;
                   1288: }
                   1289: function writeHelp(caller) {
1.430     albertel 1290:     caller.document.writeln('$start_page<frame name="bannerframe"  src="'+banner_link+'" /><frame name="bodyframe" src="$details_link" /> $end_page')
1.243     raeburn  1291:     caller.document.close()
                   1292:     caller.focus()
1.193     raeburn  1293: }
1.219     albertel 1294: // END LON-CAPA Internal -->
1.692.4.10  raeburn  1295: // ]]>
1.436     albertel 1296: </script>
1.193     raeburn  1297: ENDTEMPLATE
                   1298:     return $template;
                   1299: }
                   1300: 
1.172     www      1301: sub help_open_bug {
                   1302:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1303:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1304:     unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
                   1305:     $text = "" if (not defined $text);
                   1306:     $stayOnPage = 0 if (not defined $stayOnPage);
1.258     albertel 1307:     if ($env{'browser.interface'} eq 'textual' ||
                   1308: 	$env{'environment.remote'} eq 'off' ) {
1.172     www      1309: 	$stayOnPage=1;
                   1310:     }
1.184     albertel 1311:     $width = 600 if (not defined $width);
                   1312:     $height = 600 if (not defined $height);
1.172     www      1313: 
                   1314:     $topic=~s/\W+/\+/g;
                   1315:     my $link='';
                   1316:     my $template='';
1.379     albertel 1317:     my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&amp;bug_file_loc='.
                   1318: 	&escape($ENV{'REQUEST_URI'}).'&amp;component='.$topic;
1.172     www      1319:     if (!$stayOnPage)
                   1320:     {
                   1321: 	$link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1322:     }
                   1323:     else
                   1324:     {
                   1325: 	$link = $url;
                   1326:     }
                   1327:     # Add the text
                   1328:     if ($text ne "")
                   1329:     {
                   1330: 	$template .= 
                   1331:   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436     albertel 1332:   "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.172     www      1333:     }
                   1334: 
                   1335:     # Add the graphic
1.179     matthew  1336:     my $title = &mt('Report a Bug');
1.215     albertel 1337:     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172     www      1338:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1339:  <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172     www      1340: ENDTEMPLATE
                   1341:     if ($text ne '') { $template.='</td></tr></table>' };
                   1342:     return $template;
                   1343: 
                   1344: }
                   1345: 
                   1346: sub help_open_faq {
                   1347:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1348:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1349:     unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
                   1350:     $text = "" if (not defined $text);
                   1351:     $stayOnPage = 0 if (not defined $stayOnPage);
1.258     albertel 1352:     if ($env{'browser.interface'} eq 'textual' ||
                   1353: 	$env{'environment.remote'} eq 'off' ) {
1.172     www      1354: 	$stayOnPage=1;
                   1355:     }
                   1356:     $width = 350 if (not defined $width);
                   1357:     $height = 400 if (not defined $height);
                   1358: 
                   1359:     $topic=~s/\W+/\+/g;
                   1360:     my $link='';
                   1361:     my $template='';
                   1362:     my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
                   1363:     if (!$stayOnPage)
                   1364:     {
                   1365: 	$link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1366:     }
                   1367:     else
                   1368:     {
                   1369: 	$link = $url;
                   1370:     }
                   1371: 
                   1372:     # Add the text
                   1373:     if ($text ne "")
                   1374:     {
                   1375: 	$template .= 
1.173     www      1376:   "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436     albertel 1377:   "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.172     www      1378:     }
                   1379: 
                   1380:     # Add the graphic
1.179     matthew  1381:     my $title = &mt('View the FAQ');
1.215     albertel 1382:     my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172     www      1383:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1384:  <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172     www      1385: ENDTEMPLATE
                   1386:     if ($text ne '') { $template.='</td></tr></table>' };
                   1387:     return $template;
                   1388: 
1.44      bowersj2 1389: }
1.37      matthew  1390: 
1.180     matthew  1391: ###############################################################
                   1392: ###############################################################
                   1393: 
1.45      matthew  1394: =pod
                   1395: 
1.648     raeburn  1396: =item * &change_content_javascript():
1.256     matthew  1397: 
                   1398: This and the next function allow you to create small sections of an
                   1399: otherwise static HTML page that you can update on the fly with
                   1400: Javascript, even in Netscape 4.
                   1401: 
                   1402: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
                   1403: must be written to the HTML page once. It will prove the Javascript
                   1404: function "change(name, content)". Calling the change function with the
                   1405: name of the section 
                   1406: you want to update, matching the name passed to C<changable_area>, and
                   1407: the new content you want to put in there, will put the content into
                   1408: that area.
                   1409: 
                   1410: B<Note>: Netscape 4 only reserves enough space for the changable area
                   1411: to contain room for the original contents. You need to "make space"
                   1412: for whatever changes you wish to make, and be B<sure> to check your
                   1413: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
                   1414: it's adequate for updating a one-line status display, but little more.
                   1415: This script will set the space to 100% width, so you only need to
                   1416: worry about height in Netscape 4.
                   1417: 
                   1418: Modern browsers are much less limiting, and if you can commit to the
                   1419: user not using Netscape 4, this feature may be used freely with
                   1420: pretty much any HTML.
                   1421: 
                   1422: =cut
                   1423: 
                   1424: sub change_content_javascript {
                   1425:     # If we're on Netscape 4, we need to use Layer-based code
1.258     albertel 1426:     if ($env{'browser.type'} eq 'netscape' &&
                   1427: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1428: 	return (<<NETSCAPE4);
                   1429: 	function change(name, content) {
                   1430: 	    doc = document.layers[name+"___escape"].layers[0].document;
                   1431: 	    doc.open();
                   1432: 	    doc.write(content);
                   1433: 	    doc.close();
                   1434: 	}
                   1435: NETSCAPE4
                   1436:     } else {
                   1437: 	# Otherwise, we need to use semi-standards-compliant code
                   1438: 	# (technically, "innerHTML" isn't standard but the equivalent
                   1439: 	# is really scary, and every useful browser supports it
                   1440: 	return (<<DOMBASED);
                   1441: 	function change(name, content) {
                   1442: 	    element = document.getElementById(name);
                   1443: 	    element.innerHTML = content;
                   1444: 	}
                   1445: DOMBASED
                   1446:     }
                   1447: }
                   1448: 
                   1449: =pod
                   1450: 
1.648     raeburn  1451: =item * &changable_area($name,$origContent):
1.256     matthew  1452: 
                   1453: This provides a "changable area" that can be modified on the fly via
                   1454: the Javascript code provided in C<change_content_javascript>. $name is
                   1455: the name you will use to reference the area later; do not repeat the
                   1456: same name on a given HTML page more then once. $origContent is what
                   1457: the area will originally contain, which can be left blank.
                   1458: 
                   1459: =cut
                   1460: 
                   1461: sub changable_area {
                   1462:     my ($name, $origContent) = @_;
                   1463: 
1.258     albertel 1464:     if ($env{'browser.type'} eq 'netscape' &&
                   1465: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1466: 	# If this is netscape 4, we need to use the Layer tag
                   1467: 	return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
                   1468:     } else {
                   1469: 	return "<span id='$name'>$origContent</span>";
                   1470:     }
                   1471: }
                   1472: 
                   1473: =pod
                   1474: 
1.648     raeburn  1475: =item * &viewport_geometry_js 
1.590     raeburn  1476: 
                   1477: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
                   1478: 
                   1479: =cut
                   1480: 
                   1481: 
                   1482: sub viewport_geometry_js { 
                   1483:     return <<"GEOMETRY";
                   1484: var Geometry = {};
                   1485: function init_geometry() {
                   1486:     if (Geometry.init) { return };
                   1487:     Geometry.init=1;
                   1488:     if (window.innerHeight) {
                   1489:         Geometry.getViewportHeight   = function() { return window.innerHeight; };
                   1490:         Geometry.getViewportWidth   = function() { return window.innerWidth; };
                   1491:         Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
                   1492:         Geometry.getVerticalScroll   = function() { return window.pageYOffset; };
                   1493:     }
                   1494:     else if (document.documentElement && document.documentElement.clientHeight) {
                   1495:         Geometry.getViewportHeight =
                   1496:             function() { return document.documentElement.clientHeight; };
                   1497:         Geometry.getViewportWidth =
                   1498:             function() { return document.documentElement.clientWidth; };
                   1499: 
                   1500:         Geometry.getHorizontalScroll =
                   1501:             function() { return document.documentElement.scrollLeft; };
                   1502:         Geometry.getVerticalScroll =
                   1503:             function() { return document.documentElement.scrollTop; };
                   1504:     }
                   1505:     else if (document.body.clientHeight) {
                   1506:         Geometry.getViewportHeight =
                   1507:             function() { return document.body.clientHeight; };
                   1508:         Geometry.getViewportWidth =
                   1509:             function() { return document.body.clientWidth; };
                   1510:         Geometry.getHorizontalScroll =
                   1511:             function() { return document.body.scrollLeft; };
                   1512:         Geometry.getVerticalScroll =
                   1513:             function() { return document.body.scrollTop; };
                   1514:     }
                   1515: }
                   1516: 
                   1517: GEOMETRY
                   1518: }
                   1519: 
                   1520: =pod
                   1521: 
1.648     raeburn  1522: =item * &viewport_size_js()
1.590     raeburn  1523: 
                   1524: 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. 
                   1525: 
                   1526: =cut
                   1527: 
                   1528: sub viewport_size_js {
                   1529:     my $geometry = &viewport_geometry_js();
                   1530:     return <<"DIMS";
                   1531: 
                   1532: $geometry
                   1533: 
                   1534: function getViewportDims(width,height) {
                   1535:     init_geometry();
                   1536:     width.value = Geometry.getViewportWidth();
                   1537:     height.value = Geometry.getViewportHeight();
                   1538:     return;
                   1539: }
                   1540: 
                   1541: DIMS
                   1542: }
                   1543: 
                   1544: =pod
                   1545: 
1.648     raeburn  1546: =item * &resize_textarea_js()
1.565     albertel 1547: 
                   1548: emits the needed javascript to resize a textarea to be as big as possible
                   1549: 
                   1550: creates a function resize_textrea that takes two IDs first should be
                   1551: the id of the element to resize, second should be the id of a div that
                   1552: surrounds everything that comes after the textarea, this routine needs
                   1553: to be attached to the <body> for the onload and onresize events.
                   1554: 
1.648     raeburn  1555: =back
1.565     albertel 1556: 
                   1557: =cut
                   1558: 
                   1559: sub resize_textarea_js {
1.590     raeburn  1560:     my $geometry = &viewport_geometry_js();
1.565     albertel 1561:     return <<"RESIZE";
                   1562:     <script type="text/javascript">
1.692.4.4  raeburn  1563: // <![CDATA[
1.590     raeburn  1564: $geometry
1.565     albertel 1565: 
1.588     albertel 1566: function getX(element) {
                   1567:     var x = 0;
                   1568:     while (element) {
                   1569: 	x += element.offsetLeft;
                   1570: 	element = element.offsetParent;
                   1571:     }
                   1572:     return x;
                   1573: }
                   1574: function getY(element) {
                   1575:     var y = 0;
                   1576:     while (element) {
                   1577: 	y += element.offsetTop;
                   1578: 	element = element.offsetParent;
                   1579:     }
                   1580:     return y;
                   1581: }
                   1582: 
                   1583: 
1.565     albertel 1584: function resize_textarea(textarea_id,bottom_id) {
                   1585:     init_geometry();
                   1586:     var textarea        = document.getElementById(textarea_id);
                   1587:     //alert(textarea);
                   1588: 
1.588     albertel 1589:     var textarea_top    = getY(textarea);
1.565     albertel 1590:     var textarea_height = textarea.offsetHeight;
                   1591:     var bottom          = document.getElementById(bottom_id);
1.588     albertel 1592:     var bottom_top      = getY(bottom);
1.565     albertel 1593:     var bottom_height   = bottom.offsetHeight;
                   1594:     var window_height   = Geometry.getViewportHeight();
1.588     albertel 1595:     var fudge           = 23;
1.565     albertel 1596:     var new_height      = window_height-fudge-textarea_top-bottom_height;
                   1597:     if (new_height < 300) {
                   1598: 	new_height = 300;
                   1599:     }
                   1600:     textarea.style.height=new_height+'px';
                   1601: }
1.692.4.4  raeburn  1602: // ]]>
1.565     albertel 1603: </script>
                   1604: RESIZE
                   1605: 
                   1606: }
                   1607: 
                   1608: =pod
                   1609: 
1.256     matthew  1610: =head1 Excel and CSV file utility routines
                   1611: 
                   1612: =over 4
                   1613: 
                   1614: =cut
                   1615: 
                   1616: ###############################################################
                   1617: ###############################################################
                   1618: 
                   1619: =pod
                   1620: 
1.648     raeburn  1621: =item * &csv_translate($text) 
1.37      matthew  1622: 
1.185     www      1623: Translate $text to allow it to be output as a 'comma separated values' 
1.37      matthew  1624: format.
                   1625: 
                   1626: =cut
                   1627: 
1.180     matthew  1628: ###############################################################
                   1629: ###############################################################
1.37      matthew  1630: sub csv_translate {
                   1631:     my $text = shift;
                   1632:     $text =~ s/\"/\"\"/g;
1.209     albertel 1633:     $text =~ s/\n/ /g;
1.37      matthew  1634:     return $text;
                   1635: }
1.180     matthew  1636: 
                   1637: ###############################################################
                   1638: ###############################################################
                   1639: 
                   1640: =pod
                   1641: 
1.648     raeburn  1642: =item * &define_excel_formats()
1.180     matthew  1643: 
                   1644: Define some commonly used Excel cell formats.
                   1645: 
                   1646: Currently supported formats:
                   1647: 
                   1648: =over 4
                   1649: 
                   1650: =item header
                   1651: 
                   1652: =item bold
                   1653: 
                   1654: =item h1
                   1655: 
                   1656: =item h2
                   1657: 
                   1658: =item h3
                   1659: 
1.256     matthew  1660: =item h4
                   1661: 
                   1662: =item i
                   1663: 
1.180     matthew  1664: =item date
                   1665: 
                   1666: =back
                   1667: 
                   1668: Inputs: $workbook
                   1669: 
                   1670: Returns: $format, a hash reference.
                   1671: 
                   1672: =cut
                   1673: 
                   1674: ###############################################################
                   1675: ###############################################################
                   1676: sub define_excel_formats {
                   1677:     my ($workbook) = @_;
                   1678:     my $format;
                   1679:     $format->{'header'} = $workbook->add_format(bold      => 1, 
                   1680:                                                 bottom    => 1,
                   1681:                                                 align     => 'center');
                   1682:     $format->{'bold'} = $workbook->add_format(bold=>1);
                   1683:     $format->{'h1'}   = $workbook->add_format(bold=>1, size=>18);
                   1684:     $format->{'h2'}   = $workbook->add_format(bold=>1, size=>16);
                   1685:     $format->{'h3'}   = $workbook->add_format(bold=>1, size=>14);
1.255     matthew  1686:     $format->{'h4'}   = $workbook->add_format(bold=>1, size=>12);
1.246     matthew  1687:     $format->{'i'}    = $workbook->add_format(italic=>1);
1.180     matthew  1688:     $format->{'date'} = $workbook->add_format(num_format=>
1.207     matthew  1689:                                             'mm/dd/yyyy hh:mm:ss');
1.180     matthew  1690:     return $format;
                   1691: }
                   1692: 
                   1693: ###############################################################
                   1694: ###############################################################
1.113     bowersj2 1695: 
                   1696: =pod
                   1697: 
1.648     raeburn  1698: =item * &create_workbook()
1.255     matthew  1699: 
                   1700: Create an Excel worksheet.  If it fails, output message on the
                   1701: request object and return undefs.
                   1702: 
                   1703: Inputs: Apache request object
                   1704: 
                   1705: Returns (undef) on failure, 
                   1706:     Excel worksheet object, scalar with filename, and formats 
                   1707:     from &Apache::loncommon::define_excel_formats on success
                   1708: 
                   1709: =cut
                   1710: 
                   1711: ###############################################################
                   1712: ###############################################################
                   1713: sub create_workbook {
                   1714:     my ($r) = @_;
                   1715:         #
                   1716:     # Create the excel spreadsheet
                   1717:     my $filename = '/prtspool/'.
1.258     albertel 1718:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255     matthew  1719:         time.'_'.rand(1000000000).'.xls';
                   1720:     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
                   1721:     if (! defined($workbook)) {
                   1722:         $r->log_error("Error creating excel spreadsheet $filename: $!");
                   1723:         $r->print('<p>'.&mt("Unable to create new Excel file.  ".
                   1724:                             "This error has been logged.  ".
                   1725:                             "Please alert your LON-CAPA administrator").
                   1726:                   '</p>');
                   1727:         return (undef);
                   1728:     }
                   1729:     #
                   1730:     $workbook->set_tempdir('/home/httpd/perl/tmp');
                   1731:     #
                   1732:     my $format = &Apache::loncommon::define_excel_formats($workbook);
                   1733:     return ($workbook,$filename,$format);
                   1734: }
                   1735: 
                   1736: ###############################################################
                   1737: ###############################################################
                   1738: 
                   1739: =pod
                   1740: 
1.648     raeburn  1741: =item * &create_text_file()
1.113     bowersj2 1742: 
1.542     raeburn  1743: Create a file to write to and eventually make available to the user.
1.256     matthew  1744: If file creation fails, outputs an error message on the request object and 
                   1745: return undefs.
1.113     bowersj2 1746: 
1.256     matthew  1747: Inputs: Apache request object, and file suffix
1.113     bowersj2 1748: 
1.256     matthew  1749: Returns (undef) on failure, 
                   1750:     Filehandle and filename on success.
1.113     bowersj2 1751: 
                   1752: =cut
                   1753: 
1.256     matthew  1754: ###############################################################
                   1755: ###############################################################
                   1756: sub create_text_file {
                   1757:     my ($r,$suffix) = @_;
                   1758:     if (! defined($suffix)) { $suffix = 'txt'; };
                   1759:     my $fh;
                   1760:     my $filename = '/prtspool/'.
1.258     albertel 1761:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256     matthew  1762:         time.'_'.rand(1000000000).'.'.$suffix;
                   1763:     $fh = Apache::File->new('>/home/httpd'.$filename);
                   1764:     if (! defined($fh)) {
                   1765:         $r->log_error("Couldn't open $filename for output $!");
1.683     bisitz   1766:         $r->print(&mt('Problems occurred in creating the output file. '
                   1767:                      .'This error has been logged. '
                   1768:                      .'Please alert your LON-CAPA administrator.'));
1.113     bowersj2 1769:     }
1.256     matthew  1770:     return ($fh,$filename)
1.113     bowersj2 1771: }
                   1772: 
                   1773: 
1.256     matthew  1774: =pod 
1.113     bowersj2 1775: 
                   1776: =back
                   1777: 
                   1778: =cut
1.37      matthew  1779: 
                   1780: ###############################################################
1.33      matthew  1781: ##        Home server <option> list generating code          ##
                   1782: ###############################################################
1.35      matthew  1783: 
1.169     www      1784: # ------------------------------------------
                   1785: 
                   1786: sub domain_select {
                   1787:     my ($name,$value,$multiple)=@_;
                   1788:     my %domains=map { 
1.514     albertel 1789: 	$_ => $_.' '. &Apache::lonnet::domain($_,'description') 
1.512     albertel 1790:     } &Apache::lonnet::all_domains();
1.169     www      1791:     if ($multiple) {
                   1792: 	$domains{''}=&mt('Any domain');
1.550     albertel 1793: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287     albertel 1794: 	return &multiple_select_form($name,$value,4,\%domains);
1.169     www      1795:     } else {
1.550     albertel 1796: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.169     www      1797: 	return &select_form($name,$value,%domains);
                   1798:     }
                   1799: }
                   1800: 
1.282     albertel 1801: #-------------------------------------------
                   1802: 
                   1803: =pod
                   1804: 
1.519     raeburn  1805: =head1 Routines for form select boxes
                   1806: 
                   1807: =over 4
                   1808: 
1.648     raeburn  1809: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282     albertel 1810: 
                   1811: Returns a string containing a <select> element int multiple mode
                   1812: 
                   1813: 
                   1814: Args:
                   1815:   $name - name of the <select> element
1.506     raeburn  1816:   $value - scalar or array ref of values that should already be selected
1.282     albertel 1817:   $size - number of rows long the select element is
1.283     albertel 1818:   $hash - the elements should be 'option' => 'shown text'
1.282     albertel 1819:           (shown text should already have been &mt())
1.506     raeburn  1820:   $order - (optional) array ref of the order to show the elements in
1.283     albertel 1821: 
1.282     albertel 1822: =cut
                   1823: 
                   1824: #-------------------------------------------
1.169     www      1825: sub multiple_select_form {
1.284     albertel 1826:     my ($name,$value,$size,$hash,$order)=@_;
1.169     www      1827:     my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
                   1828:     my $output='';
1.191     matthew  1829:     if (! defined($size)) {
                   1830:         $size = 4;
1.283     albertel 1831:         if (scalar(keys(%$hash))<4) {
                   1832:             $size = scalar(keys(%$hash));
1.191     matthew  1833:         }
                   1834:     }
1.692.4.2  raeburn  1835:     $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple">';
1.501     banghart 1836:     my @order;
1.506     raeburn  1837:     if (ref($order) eq 'ARRAY')  {
                   1838:         @order = @{$order};
                   1839:     } else {
                   1840:         @order = sort(keys(%$hash));
1.501     banghart 1841:     }
                   1842:     if (exists($$hash{'select_form_order'})) {
                   1843:         @order = @{$$hash{'select_form_order'}};
                   1844:     }
                   1845:         
1.284     albertel 1846:     foreach my $key (@order) {
1.356     albertel 1847:         $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284     albertel 1848:         $output.='selected="selected" ' if ($selected{$key});
                   1849:         $output.='>'.$hash->{$key}."</option>\n";
1.169     www      1850:     }
                   1851:     $output.="</select>\n";
                   1852:     return $output;
                   1853: }
                   1854: 
1.88      www      1855: #-------------------------------------------
                   1856: 
                   1857: =pod
                   1858: 
1.648     raeburn  1859: =item * &select_form($defdom,$name,%hash)
1.88      www      1860: 
                   1861: Returns a string containing a <select name='$name' size='1'> form to 
                   1862: allow a user to select options from a hash option_name => displayed text.  
                   1863: See lonrights.pm for an example invocation and use.
                   1864: 
                   1865: =cut
                   1866: 
                   1867: #-------------------------------------------
                   1868: sub select_form {
                   1869:     my ($def,$name,%hash) = @_;
                   1870:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
1.128     albertel 1871:     my @keys;
                   1872:     if (exists($hash{'select_form_order'})) {
                   1873: 	@keys=@{$hash{'select_form_order'}};
                   1874:     } else {
                   1875: 	@keys=sort(keys(%hash));
                   1876:     }
1.356     albertel 1877:     foreach my $key (@keys) {
                   1878:         $selectform.=
                   1879: 	    '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
                   1880:             ($key eq $def ? 'selected="selected" ' : '').
                   1881:                 ">".&mt($hash{$key})."</option>\n";
1.88      www      1882:     }
                   1883:     $selectform.="</select>";
                   1884:     return $selectform;
                   1885: }
                   1886: 
1.475     www      1887: # For display filters
                   1888: 
                   1889: sub display_filter {
                   1890:     if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477     www      1891:     if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.692.4.2  raeburn  1892:     return '<span class="LC_nobreak"><label>'.&mt('Records [_1]',
1.475     www      1893: 			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
                   1894: 							   (&mt('all'),10,20,50,100,1000,10000))).
1.692.4.2  raeburn  1895: 	   '</label></span> <span class="LC_nobreak">'.
1.475     www      1896:            &mt('Filter [_1]',
1.477     www      1897: 	   &select_form($env{'form.displayfilter'},
                   1898: 			'displayfilter',
                   1899: 			('currentfolder' => 'Current folder/page',
                   1900: 			 'containing' => 'Containing phrase',
                   1901: 			 'none' => 'None'))).
1.692.4.2  raeburn  1902: 			 '<input type="text" name="containingphrase" size="30" value="'.&HTML::Entities::encode($env{'form.containingphrase'}).'" /></span>';
1.475     www      1903: }
                   1904: 
1.167     www      1905: sub gradeleveldescription {
                   1906:     my $gradelevel=shift;
                   1907:     my %gradelevels=(0 => 'Not specified',
                   1908: 		     1 => 'Grade 1',
                   1909: 		     2 => 'Grade 2',
                   1910: 		     3 => 'Grade 3',
                   1911: 		     4 => 'Grade 4',
                   1912: 		     5 => 'Grade 5',
                   1913: 		     6 => 'Grade 6',
                   1914: 		     7 => 'Grade 7',
                   1915: 		     8 => 'Grade 8',
                   1916: 		     9 => 'Grade 9',
                   1917: 		     10 => 'Grade 10',
                   1918: 		     11 => 'Grade 11',
                   1919: 		     12 => 'Grade 12',
                   1920: 		     13 => 'Grade 13',
                   1921: 		     14 => '100 Level',
                   1922: 		     15 => '200 Level',
                   1923: 		     16 => '300 Level',
                   1924: 		     17 => '400 Level',
                   1925: 		     18 => 'Graduate Level');
                   1926:     return &mt($gradelevels{$gradelevel});
                   1927: }
                   1928: 
1.163     www      1929: sub select_level_form {
                   1930:     my ($deflevel,$name)=@_;
                   1931:     unless ($deflevel) { $deflevel=0; }
1.167     www      1932:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
                   1933:     for (my $i=0; $i<=18; $i++) {
                   1934:         $selectform.="<option value=\"$i\" ".
1.253     albertel 1935:             ($i==$deflevel ? 'selected="selected" ' : '').
1.167     www      1936:                 ">".&gradeleveldescription($i)."</option>\n";
                   1937:     }
                   1938:     $selectform.="</select>";
                   1939:     return $selectform;
1.163     www      1940: }
1.167     www      1941: 
1.35      matthew  1942: #-------------------------------------------
                   1943: 
1.45      matthew  1944: =pod
                   1945: 
1.692.4.7  raeburn  1946: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange)
1.35      matthew  1947: 
                   1948: Returns a string containing a <select name='$name' size='1'> form to 
                   1949: allow a user to select the domain to preform an operation in.  
                   1950: See loncreateuser.pm for an example invocation and use.
                   1951: 
1.90      www      1952: If the $includeempty flag is set, it also includes an empty choice ("no domain
                   1953: selected");
                   1954: 
1.692.4.2  raeburn  1955: If the $showdomdesc flag is set, the domain name is followed by the domain description.
                   1956: 
1.692.4.7  raeburn  1957: The optional $onchange argument specifies what should occur if the domain selector is changed, e.g., 'this.form.submit()' if the form is to be automatically submitted.
1.563     raeburn  1958: 
1.35      matthew  1959: =cut
                   1960: 
                   1961: #-------------------------------------------
1.34      matthew  1962: sub select_dom_form {
1.692.4.7  raeburn  1963:     my ($defdom,$name,$includeempty,$showdomdesc,$onchange) = @_;
                   1964:     if ($onchange) {
                   1965:         $onchange = ' onchange="'.$onchange.'"';
1.692.4.2  raeburn  1966:     }
1.550     albertel 1967:     my @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
1.90      www      1968:     if ($includeempty) { @domains=('',@domains); }
1.692.4.2  raeburn  1969:     my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange>\n";
1.356     albertel 1970:     foreach my $dom (@domains) {
                   1971:         $selectdomain.="<option value=\"$dom\" ".
1.563     raeburn  1972:             ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
                   1973:         if ($showdomdesc) {
                   1974:             if ($dom ne '') {
                   1975:                 my $domdesc = &Apache::lonnet::domain($dom,'description');
                   1976:                 if ($domdesc ne '') {
                   1977:                     $selectdomain .= ' ('.$domdesc.')';
                   1978:                 }
                   1979:             } 
                   1980:         }
                   1981:         $selectdomain .= "</option>\n";
1.34      matthew  1982:     }
                   1983:     $selectdomain.="</select>";
                   1984:     return $selectdomain;
                   1985: }
                   1986: 
1.35      matthew  1987: #-------------------------------------------
                   1988: 
1.45      matthew  1989: =pod
                   1990: 
1.648     raeburn  1991: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35      matthew  1992: 
1.586     raeburn  1993: input: 4 arguments (two required, two optional) - 
                   1994:     $domain - domain of new user
                   1995:     $name - name of form element
                   1996:     $default - Value of 'default' causes a default item to be first 
                   1997:                             option, and selected by default. 
                   1998:     $hide - Value of 'hide' causes hiding of the name of the server, 
                   1999:                             if 1 server found, or default, if 0 found.
1.594     raeburn  2000: output: returns 2 items: 
1.586     raeburn  2001: (a) form element which contains either:
                   2002:    (i) <select name="$name">
                   2003:         <option value="$hostid1">$hostid $servers{$hostid}</option>
                   2004:         <option value="$hostid2">$hostid $servers{$hostid}</option>       
                   2005:        </select>
                   2006:        form item if there are multiple library servers in $domain, or
                   2007:    (ii) an <input type="hidden" name="$name" value="$hostid" /> form item 
                   2008:        if there is only one library server in $domain.
                   2009: 
                   2010: (b) number of library servers found.
                   2011: 
                   2012: See loncreateuser.pm for example of use.
1.35      matthew  2013: 
                   2014: =cut
                   2015: 
                   2016: #-------------------------------------------
1.586     raeburn  2017: sub home_server_form_item {
                   2018:     my ($domain,$name,$default,$hide) = @_;
1.513     albertel 2019:     my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586     raeburn  2020:     my $result;
                   2021:     my $numlib = keys(%servers);
                   2022:     if ($numlib > 1) {
                   2023:         $result .= '<select name="'.$name.'" />'."\n";
                   2024:         if ($default) {
1.692.4.2  raeburn  2025:             $result .= '<option value="default" selected="selected">'.&mt('default').
1.586     raeburn  2026:                        '</option>'."\n";
                   2027:         }
                   2028:         foreach my $hostid (sort(keys(%servers))) {
                   2029:             $result.= '<option value="'.$hostid.'">'.
                   2030: 	              $hostid.' '.$servers{$hostid}."</option>\n";
                   2031:         }
                   2032:         $result .= '</select>'."\n";
                   2033:     } elsif ($numlib == 1) {
                   2034:         my $hostid;
                   2035:         foreach my $item (keys(%servers)) {
                   2036:             $hostid = $item;
                   2037:         }
                   2038:         $result .= '<input type="hidden" name="'.$name.'" value="'.
                   2039:                    $hostid.'" />';
                   2040:                    if (!$hide) {
                   2041:                        $result .= $hostid.' '.$servers{$hostid};
                   2042:                    }
                   2043:                    $result .= "\n";
                   2044:     } elsif ($default) {
                   2045:         $result .= '<input type="hidden" name="'.$name.
                   2046:                    '" value="default" />';
                   2047:                    if (!$hide) {
                   2048:                        $result .= &mt('default');
                   2049:                    }
                   2050:                    $result .= "\n";
1.33      matthew  2051:     }
1.586     raeburn  2052:     return ($result,$numlib);
1.33      matthew  2053: }
1.112     bowersj2 2054: 
                   2055: =pod
                   2056: 
1.534     albertel 2057: =back 
                   2058: 
1.112     bowersj2 2059: =cut
1.87      matthew  2060: 
                   2061: ###############################################################
1.112     bowersj2 2062: ##                  Decoding User Agent                      ##
1.87      matthew  2063: ###############################################################
                   2064: 
                   2065: =pod
                   2066: 
1.112     bowersj2 2067: =head1 Decoding the User Agent
                   2068: 
                   2069: =over 4
                   2070: 
                   2071: =item * &decode_user_agent()
1.87      matthew  2072: 
                   2073: Inputs: $r
                   2074: 
                   2075: Outputs:
                   2076: 
                   2077: =over 4
                   2078: 
1.112     bowersj2 2079: =item * $httpbrowser
1.87      matthew  2080: 
1.112     bowersj2 2081: =item * $clientbrowser
1.87      matthew  2082: 
1.112     bowersj2 2083: =item * $clientversion
1.87      matthew  2084: 
1.112     bowersj2 2085: =item * $clientmathml
1.87      matthew  2086: 
1.112     bowersj2 2087: =item * $clientunicode
1.87      matthew  2088: 
1.112     bowersj2 2089: =item * $clientos
1.87      matthew  2090: 
                   2091: =back
                   2092: 
1.157     matthew  2093: =back 
                   2094: 
1.87      matthew  2095: =cut
                   2096: 
                   2097: ###############################################################
                   2098: ###############################################################
                   2099: sub decode_user_agent {
1.247     albertel 2100:     my ($r)=@_;
1.87      matthew  2101:     my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
                   2102:     my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
                   2103:     my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247     albertel 2104:     if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87      matthew  2105:     my $clientbrowser='unknown';
                   2106:     my $clientversion='0';
                   2107:     my $clientmathml='';
                   2108:     my $clientunicode='0';
                   2109:     for (my $i=0;$i<=$#browsertype;$i++) {
                   2110:         my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\:/,$browsertype[$i]);
                   2111: 	if (($httpbrowser=~/$match/i)  && ($httpbrowser!~/$notmatch/i)) {
                   2112: 	    $clientbrowser=$bname;
                   2113:             $httpbrowser=~/$vreg/i;
                   2114: 	    $clientversion=$1;
                   2115:             $clientmathml=($clientversion>=$minv);
                   2116:             $clientunicode=($clientversion>=$univ);
                   2117: 	}
                   2118:     }
                   2119:     my $clientos='unknown';
                   2120:     if (($httpbrowser=~/linux/i) ||
                   2121:         ($httpbrowser=~/unix/i) ||
                   2122:         ($httpbrowser=~/ux/i) ||
                   2123:         ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
                   2124:     if (($httpbrowser=~/vax/i) ||
                   2125:         ($httpbrowser=~/vms/i)) { $clientos='vms'; }
                   2126:     if ($httpbrowser=~/next/i) { $clientos='next'; }
                   2127:     if (($httpbrowser=~/mac/i) ||
                   2128:         ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
                   2129:     if ($httpbrowser=~/win/i) { $clientos='win'; }
                   2130:     if ($httpbrowser=~/embed/i) { $clientos='pda'; }
                   2131:     return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
                   2132:             $clientunicode,$clientos,);
                   2133: }
                   2134: 
1.32      matthew  2135: ###############################################################
                   2136: ##    Authentication changing form generation subroutines    ##
                   2137: ###############################################################
                   2138: ##
                   2139: ## All of the authform_xxxxxxx subroutines take their inputs in a
                   2140: ## hash, and have reasonable default values.
                   2141: ##
                   2142: ##    formname = the name given in the <form> tag.
1.35      matthew  2143: #-------------------------------------------
                   2144: 
1.45      matthew  2145: =pod
                   2146: 
1.112     bowersj2 2147: =head1 Authentication Routines
                   2148: 
                   2149: =over 4
                   2150: 
1.648     raeburn  2151: =item * &authform_xxxxxx()
1.35      matthew  2152: 
                   2153: The authform_xxxxxx subroutines provide javascript and html forms which 
                   2154: handle some of the conveniences required for authentication forms.  
                   2155: This is not an optimal method, but it works.  
                   2156: 
                   2157: =over 4
                   2158: 
1.112     bowersj2 2159: =item * authform_header
1.35      matthew  2160: 
1.112     bowersj2 2161: =item * authform_authorwarning
1.35      matthew  2162: 
1.112     bowersj2 2163: =item * authform_nochange
1.35      matthew  2164: 
1.112     bowersj2 2165: =item * authform_kerberos
1.35      matthew  2166: 
1.112     bowersj2 2167: =item * authform_internal
1.35      matthew  2168: 
1.112     bowersj2 2169: =item * authform_filesystem
1.35      matthew  2170: 
                   2171: =back
                   2172: 
1.648     raeburn  2173: See loncreateuser.pm for invocation and use examples.
1.157     matthew  2174: 
1.35      matthew  2175: =cut
                   2176: 
                   2177: #-------------------------------------------
1.32      matthew  2178: sub authform_header{  
                   2179:     my %in = (
                   2180:         formname => 'cu',
1.80      albertel 2181:         kerb_def_dom => '',
1.32      matthew  2182:         @_,
                   2183:     );
                   2184:     $in{'formname'} = 'document.' . $in{'formname'};
                   2185:     my $result='';
1.80      albertel 2186: 
                   2187: #---------------------------------------------- Code for upper case translation
                   2188:     my $Javascript_toUpperCase;
                   2189:     unless ($in{kerb_def_dom}) {
                   2190:         $Javascript_toUpperCase =<<"END";
                   2191:         switch (choice) {
                   2192:            case 'krb': currentform.elements[choicearg].value =
                   2193:                currentform.elements[choicearg].value.toUpperCase();
                   2194:                break;
                   2195:            default:
                   2196:         }
                   2197: END
                   2198:     } else {
                   2199:         $Javascript_toUpperCase = "";
                   2200:     }
                   2201: 
1.165     raeburn  2202:     my $radioval = "'nochange'";
1.591     raeburn  2203:     if (defined($in{'curr_authtype'})) {
                   2204:         if ($in{'curr_authtype'} ne '') {
                   2205:             $radioval = "'".$in{'curr_authtype'}."arg'";
                   2206:         }
1.174     matthew  2207:     }
1.165     raeburn  2208:     my $argfield = 'null';
1.591     raeburn  2209:     if (defined($in{'mode'})) {
1.165     raeburn  2210:         if ($in{'mode'} eq 'modifycourse')  {
1.591     raeburn  2211:             if (defined($in{'curr_autharg'})) {
                   2212:                 if ($in{'curr_autharg'} ne '') {
1.165     raeburn  2213:                     $argfield = "'$in{'curr_autharg'}'";
                   2214:                 }
                   2215:             }
                   2216:         }
                   2217:     }
                   2218: 
1.32      matthew  2219:     $result.=<<"END";
                   2220: var current = new Object();
1.165     raeburn  2221: current.radiovalue = $radioval;
                   2222: current.argfield = $argfield;
1.32      matthew  2223: 
                   2224: function changed_radio(choice,currentform) {
                   2225:     var choicearg = choice + 'arg';
                   2226:     // If a radio button in changed, we need to change the argfield
                   2227:     if (current.radiovalue != choice) {
                   2228:         current.radiovalue = choice;
                   2229:         if (current.argfield != null) {
                   2230:             currentform.elements[current.argfield].value = '';
                   2231:         }
                   2232:         if (choice == 'nochange') {
                   2233:             current.argfield = null;
                   2234:         } else {
                   2235:             current.argfield = choicearg;
                   2236:             switch(choice) {
                   2237:                 case 'krb': 
                   2238:                     currentform.elements[current.argfield].value = 
                   2239:                         "$in{'kerb_def_dom'}";
                   2240:                 break;
                   2241:               default:
                   2242:                 break;
                   2243:             }
                   2244:         }
                   2245:     }
                   2246:     return;
                   2247: }
1.22      www      2248: 
1.32      matthew  2249: function changed_text(choice,currentform) {
                   2250:     var choicearg = choice + 'arg';
                   2251:     if (currentform.elements[choicearg].value !='') {
1.80      albertel 2252:         $Javascript_toUpperCase
1.32      matthew  2253:         // clear old field
                   2254:         if ((current.argfield != choicearg) && (current.argfield != null)) {
                   2255:             currentform.elements[current.argfield].value = '';
                   2256:         }
                   2257:         current.argfield = choicearg;
                   2258:     }
                   2259:     set_auth_radio_buttons(choice,currentform);
                   2260:     return;
1.20      www      2261: }
1.32      matthew  2262: 
                   2263: function set_auth_radio_buttons(newvalue,currentform) {
                   2264:     var i=0;
                   2265:     while (i < currentform.login.length) {
                   2266:         if (currentform.login[i].value == newvalue) { break; }
                   2267:         i++;
                   2268:     }
                   2269:     if (i == currentform.login.length) {
                   2270:         return;
                   2271:     }
                   2272:     current.radiovalue = newvalue;
                   2273:     currentform.login[i].checked = true;
                   2274:     return;
                   2275: }
                   2276: END
                   2277:     return $result;
                   2278: }
                   2279: 
                   2280: sub authform_authorwarning{
                   2281:     my $result='';
1.144     matthew  2282:     $result='<i>'.
                   2283:         &mt('As a general rule, only authors or co-authors should be '.
                   2284:             'filesystem authenticated '.
                   2285:             '(which allows access to the server filesystem).')."</i>\n";
1.32      matthew  2286:     return $result;
                   2287: }
                   2288: 
                   2289: sub authform_nochange{  
                   2290:     my %in = (
                   2291:               formname => 'document.cu',
                   2292:               kerb_def_dom => 'MSU.EDU',
                   2293:               @_,
                   2294:           );
1.586     raeburn  2295:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'}); 
                   2296:     my $result;
                   2297:     if (keys(%can_assign) == 0) {
                   2298:         $result = &mt('Under you current role you are not permitted to change login settings for this user');  
                   2299:     } else {
                   2300:         $result = '<label>'.&mt('[_1] Do not change login data',
                   2301:                   '<input type="radio" name="login" value="nochange" '.
                   2302:                   'checked="checked" onclick="'.
1.281     albertel 2303:             "javascript:changed_radio('nochange',$in{'formname'});".'" />').
                   2304: 	    '</label>';
1.586     raeburn  2305:     }
1.32      matthew  2306:     return $result;
                   2307: }
                   2308: 
1.591     raeburn  2309: sub authform_kerberos {
1.32      matthew  2310:     my %in = (
                   2311:               formname => 'document.cu',
                   2312:               kerb_def_dom => 'MSU.EDU',
1.80      albertel 2313:               kerb_def_auth => 'krb4',
1.32      matthew  2314:               @_,
                   2315:               );
1.586     raeburn  2316:     my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
                   2317:         $autharg,$jscall);
                   2318:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.80      albertel 2319:     if ($in{'kerb_def_auth'} eq 'krb5') {
1.692.4.2  raeburn  2320:        $check5 = ' checked="checked"';
1.80      albertel 2321:     } else {
1.692.4.2  raeburn  2322:        $check4 = ' checked="checked"';
1.80      albertel 2323:     }
1.165     raeburn  2324:     $krbarg = $in{'kerb_def_dom'};
1.591     raeburn  2325:     if (defined($in{'curr_authtype'})) {
                   2326:         if ($in{'curr_authtype'} eq 'krb') {
1.692.4.2  raeburn  2327:             $krbcheck = ' checked="checked"';
1.623     raeburn  2328:             if (defined($in{'mode'})) {
                   2329:                 if ($in{'mode'} eq 'modifyuser') {
                   2330:                     $krbcheck = '';
                   2331:                 }
                   2332:             }
1.591     raeburn  2333:             if (defined($in{'curr_kerb_ver'})) {
                   2334:                 if ($in{'curr_krb_ver'} eq '5') {
1.692.4.2  raeburn  2335:                     $check5 = ' checked="checked"';
1.591     raeburn  2336:                     $check4 = '';
                   2337:                 } else {
1.692.4.2  raeburn  2338:                     $check4 = ' checked="checked"';
1.591     raeburn  2339:                     $check5 = '';
                   2340:                 }
1.586     raeburn  2341:             }
1.591     raeburn  2342:             if (defined($in{'curr_autharg'})) {
1.165     raeburn  2343:                 $krbarg = $in{'curr_autharg'};
                   2344:             }
1.586     raeburn  2345:             if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591     raeburn  2346:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2347:                     $result = 
                   2348:     &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
                   2349:         $in{'curr_autharg'},$krbver);
                   2350:                 } else {
                   2351:                     $result =
                   2352:     &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
                   2353:                 }
                   2354:                 return $result; 
                   2355:             }
                   2356:         }
                   2357:     } else {
                   2358:         if ($authnum == 1) {
1.692.4.2  raeburn  2359:             $authtype = '<input type="hidden" name="login" value="krb" />';
1.165     raeburn  2360:         }
                   2361:     }
1.586     raeburn  2362:     if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
                   2363:         return;
1.587     raeburn  2364:     } elsif ($authtype eq '') {
1.591     raeburn  2365:         if (defined($in{'mode'})) {
1.587     raeburn  2366:             if ($in{'mode'} eq 'modifycourse') {
                   2367:                 if ($authnum == 1) {
1.692.4.2  raeburn  2368:                     $authtype = '<input type="hidden" name="login" value="krb" />';
1.587     raeburn  2369:                 }
                   2370:             }
                   2371:         }
1.586     raeburn  2372:     }
                   2373:     $jscall = "javascript:changed_radio('krb',$in{'formname'});";
                   2374:     if ($authtype eq '') {
                   2375:         $authtype = '<input type="radio" name="login" value="krb" '.
                   2376:                     'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
                   2377:                     $krbcheck.' />';
                   2378:     }
                   2379:     if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
                   2380:         ($can_assign{'krb4'} && !$can_assign{'krb5'} && 
                   2381:          $in{'curr_authtype'} eq 'krb5') ||
                   2382:         (!$can_assign{'krb4'} && $can_assign{'krb5'} && 
                   2383:          $in{'curr_authtype'} eq 'krb4')) {
                   2384:         $result .= &mt
1.144     matthew  2385:         ('[_1] Kerberos authenticated with domain [_2] '.
1.281     albertel 2386:          '[_3] Version 4 [_4] Version 5 [_5]',
1.586     raeburn  2387:          '<label>'.$authtype,
1.281     albertel 2388:          '</label><input type="text" size="10" name="krbarg" '.
1.165     raeburn  2389:              'value="'.$krbarg.'" '.
1.144     matthew  2390:              'onchange="'.$jscall.'" />',
1.281     albertel 2391:          '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
                   2392:          '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
                   2393: 	 '</label>');
1.586     raeburn  2394:     } elsif ($can_assign{'krb4'}) {
                   2395:         $result .= &mt
                   2396:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2397:          '[_3] Version 4 [_4]',
                   2398:          '<label>'.$authtype,
                   2399:          '</label><input type="text" size="10" name="krbarg" '.
                   2400:              'value="'.$krbarg.'" '.
                   2401:              'onchange="'.$jscall.'" />',
                   2402:          '<label><input type="hidden" name="krbver" value="4" />',
                   2403:          '</label>');
                   2404:     } elsif ($can_assign{'krb5'}) {
                   2405:         $result .= &mt
                   2406:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2407:          '[_3] Version 5 [_4]',
                   2408:          '<label>'.$authtype,
                   2409:          '</label><input type="text" size="10" name="krbarg" '.
                   2410:              'value="'.$krbarg.'" '.
                   2411:              'onchange="'.$jscall.'" />',
                   2412:          '<label><input type="hidden" name="krbver" value="5" />',
                   2413:          '</label>');
                   2414:     }
1.32      matthew  2415:     return $result;
                   2416: }
                   2417: 
                   2418: sub authform_internal{  
1.586     raeburn  2419:     my %in = (
1.32      matthew  2420:                 formname => 'document.cu',
                   2421:                 kerb_def_dom => 'MSU.EDU',
                   2422:                 @_,
                   2423:                 );
1.586     raeburn  2424:     my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
                   2425:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2426:     if (defined($in{'curr_authtype'})) {
                   2427:         if ($in{'curr_authtype'} eq 'int') {
1.586     raeburn  2428:             if ($can_assign{'int'}) {
1.692.4.2  raeburn  2429:                 $intcheck = 'checked="checked" ';
1.623     raeburn  2430:                 if (defined($in{'mode'})) {
                   2431:                     if ($in{'mode'} eq 'modifyuser') {
                   2432:                         $intcheck = '';
                   2433:                     }
                   2434:                 }
1.591     raeburn  2435:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2436:                     $intarg = $in{'curr_autharg'};
                   2437:                 }
                   2438:             } else {
                   2439:                 $result = &mt('Currently internally authenticated.');
                   2440:                 return $result;
1.165     raeburn  2441:             }
                   2442:         }
1.586     raeburn  2443:     } else {
                   2444:         if ($authnum == 1) {
1.692.4.2  raeburn  2445:             $authtype = '<input type="hidden" name="login" value="int" />';
1.586     raeburn  2446:         }
                   2447:     }
                   2448:     if (!$can_assign{'int'}) {
                   2449:         return;
1.587     raeburn  2450:     } elsif ($authtype eq '') {
1.591     raeburn  2451:         if (defined($in{'mode'})) {
1.587     raeburn  2452:             if ($in{'mode'} eq 'modifycourse') {
                   2453:                 if ($authnum == 1) {
1.692.4.2  raeburn  2454:                     $authtype = '<input type="hidden" name="login" value="int" />';
1.587     raeburn  2455:                 }
                   2456:             }
                   2457:         }
1.165     raeburn  2458:     }
1.586     raeburn  2459:     $jscall = "javascript:changed_radio('int',$in{'formname'});";
                   2460:     if ($authtype eq '') {
                   2461:         $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
                   2462:                     ' onchange="'.$jscall.'" onclick="'.$jscall.'" />';
                   2463:     }
1.605     bisitz   2464:     $autharg = '<input type="password" size="10" name="intarg" value="'.
1.586     raeburn  2465:                $intarg.'" onchange="'.$jscall.'" />';
                   2466:     $result = &mt
1.144     matthew  2467:         ('[_1] Internally authenticated (with initial password [_2])',
1.586     raeburn  2468:          '<label>'.$authtype,'</label>'.$autharg);
1.692.4.4  raeburn  2469:     $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  2470:     return $result;
                   2471: }
                   2472: 
                   2473: sub authform_local{  
                   2474:     my %in = (
                   2475:               formname => 'document.cu',
                   2476:               kerb_def_dom => 'MSU.EDU',
                   2477:               @_,
                   2478:               );
1.586     raeburn  2479:     my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
                   2480:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2481:     if (defined($in{'curr_authtype'})) {
                   2482:         if ($in{'curr_authtype'} eq 'loc') {
1.586     raeburn  2483:             if ($can_assign{'loc'}) {
1.692.4.2  raeburn  2484:                 $loccheck = 'checked="checked" ';
1.623     raeburn  2485:                 if (defined($in{'mode'})) {
                   2486:                     if ($in{'mode'} eq 'modifyuser') {
                   2487:                         $loccheck = '';
                   2488:                     }
                   2489:                 }
1.591     raeburn  2490:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2491:                     $locarg = $in{'curr_autharg'};
                   2492:                 }
                   2493:             } else {
                   2494:                 $result = &mt('Currently using local (institutional) authentication.');
                   2495:                 return $result;
1.165     raeburn  2496:             }
                   2497:         }
1.586     raeburn  2498:     } else {
                   2499:         if ($authnum == 1) {
1.692.4.2  raeburn  2500:             $authtype = '<input type="hidden" name="login" value="loc" />';
1.586     raeburn  2501:         }
                   2502:     }
                   2503:     if (!$can_assign{'loc'}) {
                   2504:         return;
1.587     raeburn  2505:     } elsif ($authtype eq '') {
1.591     raeburn  2506:         if (defined($in{'mode'})) {
1.587     raeburn  2507:             if ($in{'mode'} eq 'modifycourse') {
                   2508:                 if ($authnum == 1) {
1.692.4.2  raeburn  2509:                     $authtype = '<input type="hidden" name="login" value="loc" />';
1.587     raeburn  2510:                 }
                   2511:             }
                   2512:         }
1.165     raeburn  2513:     }
1.586     raeburn  2514:     $jscall = "javascript:changed_radio('loc',$in{'formname'});";
                   2515:     if ($authtype eq '') {
                   2516:         $authtype = '<input type="radio" name="login" value="loc" '.
                   2517:                     $loccheck.' onchange="'.$jscall.'" onclick="'.
                   2518:                     $jscall.'" />';
                   2519:     }
                   2520:     $autharg = '<input type="text" size="10" name="locarg" value="'.
                   2521:                $locarg.'" onchange="'.$jscall.'" />';
                   2522:     $result = &mt('[_1] Local Authentication with argument [_2]',
                   2523:                   '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  2524:     return $result;
                   2525: }
                   2526: 
                   2527: sub authform_filesystem{  
                   2528:     my %in = (
                   2529:               formname => 'document.cu',
                   2530:               kerb_def_dom => 'MSU.EDU',
                   2531:               @_,
                   2532:               );
1.586     raeburn  2533:     my ($fsyscheck,$result,$authtype,$autharg,$jscall);
                   2534:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2535:     if (defined($in{'curr_authtype'})) {
                   2536:         if ($in{'curr_authtype'} eq 'fsys') {
1.586     raeburn  2537:             if ($can_assign{'fsys'}) {
1.692.4.2  raeburn  2538:                 $fsyscheck = 'checked="checked" ';
1.623     raeburn  2539:                 if (defined($in{'mode'})) {
                   2540:                     if ($in{'mode'} eq 'modifyuser') {
                   2541:                         $fsyscheck = '';
                   2542:                     }
                   2543:                 }
1.586     raeburn  2544:             } else {
                   2545:                 $result = &mt('Currently Filesystem Authenticated.');
                   2546:                 return $result;
                   2547:             }           
                   2548:         }
                   2549:     } else {
                   2550:         if ($authnum == 1) {
1.692.4.2  raeburn  2551:             $authtype = '<input type="hidden" name="login" value="fsys" />';
1.586     raeburn  2552:         }
                   2553:     }
                   2554:     if (!$can_assign{'fsys'}) {
                   2555:         return;
1.587     raeburn  2556:     } elsif ($authtype eq '') {
1.591     raeburn  2557:         if (defined($in{'mode'})) {
1.587     raeburn  2558:             if ($in{'mode'} eq 'modifycourse') {
                   2559:                 if ($authnum == 1) {
1.692.4.2  raeburn  2560:                     $authtype = '<input type="hidden" name="login" value="fsys" />';
1.587     raeburn  2561:                 }
                   2562:             }
                   2563:         }
1.586     raeburn  2564:     }
                   2565:     $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
                   2566:     if ($authtype eq '') {
                   2567:         $authtype = '<input type="radio" name="login" value="fsys" '.
                   2568:                     $fsyscheck.' onchange="'.$jscall.'" onclick="'.
                   2569:                     $jscall.'" />';
                   2570:     }
                   2571:     $autharg = '<input type="text" size="10" name="fsysarg" value=""'.
                   2572:                ' onchange="'.$jscall.'" />';
                   2573:     $result = &mt
1.144     matthew  2574:         ('[_1] Filesystem Authenticated (with initial password [_2])',
1.281     albertel 2575:          '<label><input type="radio" name="login" value="fsys" '.
1.586     raeburn  2576:          $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.605     bisitz   2577:          '</label><input type="password" size="10" name="fsysarg" value="" '.
1.144     matthew  2578:                   'onchange="'.$jscall.'" />');
1.32      matthew  2579:     return $result;
                   2580: }
                   2581: 
1.586     raeburn  2582: sub get_assignable_auth {
                   2583:     my ($dom) = @_;
                   2584:     if ($dom eq '') {
                   2585:         $dom = $env{'request.role.domain'};
                   2586:     }
                   2587:     my %can_assign = (
                   2588:                           krb4 => 1,
                   2589:                           krb5 => 1,
                   2590:                           int  => 1,
                   2591:                           loc  => 1,
                   2592:                      );
                   2593:     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
                   2594:     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   2595:         if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
                   2596:             my $authhash = $domconfig{'usercreation'}{'authtypes'};
                   2597:             my $context;
                   2598:             if ($env{'request.role'} =~ /^au/) {
                   2599:                 $context = 'author';
                   2600:             } elsif ($env{'request.role'} =~ /^dc/) {
                   2601:                 $context = 'domain';
                   2602:             } elsif ($env{'request.course.id'}) {
                   2603:                 $context = 'course';
                   2604:             }
                   2605:             if ($context) {
                   2606:                 if (ref($authhash->{$context}) eq 'HASH') {
                   2607:                    %can_assign = %{$authhash->{$context}}; 
                   2608:                 }
                   2609:             }
                   2610:         }
                   2611:     }
                   2612:     my $authnum = 0;
                   2613:     foreach my $key (keys(%can_assign)) {
                   2614:         if ($can_assign{$key}) {
                   2615:             $authnum ++;
                   2616:         }
                   2617:     }
                   2618:     if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
                   2619:         $authnum --;
                   2620:     }
                   2621:     return ($authnum,%can_assign);
                   2622: }
                   2623: 
1.80      albertel 2624: ###############################################################
                   2625: ##    Get Kerberos Defaults for Domain                 ##
                   2626: ###############################################################
                   2627: ##
                   2628: ## Returns default kerberos version and an associated argument
                   2629: ## as listed in file domain.tab. If not listed, provides
                   2630: ## appropriate default domain and kerberos version.
                   2631: ##
                   2632: #-------------------------------------------
                   2633: 
                   2634: =pod
                   2635: 
1.648     raeburn  2636: =item * &get_kerberos_defaults()
1.80      albertel 2637: 
                   2638: get_kerberos_defaults($target_domain) returns the default kerberos
1.641     raeburn  2639: version and domain. If not found, it defaults to version 4 and the 
                   2640: domain of the server.
1.80      albertel 2641: 
1.648     raeburn  2642: =over 4
                   2643: 
1.80      albertel 2644: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
                   2645: 
1.648     raeburn  2646: =back
                   2647: 
                   2648: =back
                   2649: 
1.80      albertel 2650: =cut
                   2651: 
                   2652: #-------------------------------------------
                   2653: sub get_kerberos_defaults {
                   2654:     my $domain=shift;
1.641     raeburn  2655:     my ($krbdef,$krbdefdom);
                   2656:     my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
                   2657:     if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
                   2658:         $krbdef = $domdefaults{'auth_def'};
                   2659:         $krbdefdom = $domdefaults{'auth_arg_def'};
                   2660:     } else {
1.80      albertel 2661:         $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
                   2662:         my $krbdefdom=$1;
                   2663:         $krbdefdom=~tr/a-z/A-Z/;
                   2664:         $krbdef = "krb4";
                   2665:     }
                   2666:     return ($krbdef,$krbdefdom);
                   2667: }
1.112     bowersj2 2668: 
1.32      matthew  2669: 
1.46      matthew  2670: ###############################################################
                   2671: ##                Thesaurus Functions                        ##
                   2672: ###############################################################
1.20      www      2673: 
1.46      matthew  2674: =pod
1.20      www      2675: 
1.112     bowersj2 2676: =head1 Thesaurus Functions
                   2677: 
                   2678: =over 4
                   2679: 
1.648     raeburn  2680: =item * &initialize_keywords()
1.46      matthew  2681: 
                   2682: Initializes the package variable %Keywords if it is empty.  Uses the
                   2683: package variable $thesaurus_db_file.
                   2684: 
                   2685: =cut
                   2686: 
                   2687: ###################################################
                   2688: 
                   2689: sub initialize_keywords {
                   2690:     return 1 if (scalar keys(%Keywords));
                   2691:     # If we are here, %Keywords is empty, so fill it up
                   2692:     #   Make sure the file we need exists...
                   2693:     if (! -e $thesaurus_db_file) {
                   2694:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
                   2695:                                  " failed because it does not exist");
                   2696:         return 0;
                   2697:     }
                   2698:     #   Set up the hash as a database
                   2699:     my %thesaurus_db;
                   2700:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 2701:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  2702:         &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
                   2703:                                  $thesaurus_db_file);
                   2704:         return 0;
                   2705:     } 
                   2706:     #  Get the average number of appearances of a word.
                   2707:     my $avecount = $thesaurus_db{'average.count'};
                   2708:     #  Put keywords (those that appear > average) into %Keywords
                   2709:     while (my ($word,$data)=each (%thesaurus_db)) {
                   2710:         my ($count,undef) = split /:/,$data;
                   2711:         $Keywords{$word}++ if ($count > $avecount);
                   2712:     }
                   2713:     untie %thesaurus_db;
                   2714:     # Remove special values from %Keywords.
1.356     albertel 2715:     foreach my $value ('total.count','average.count') {
                   2716:         delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586     raeburn  2717:   }
1.46      matthew  2718:     return 1;
                   2719: }
                   2720: 
                   2721: ###################################################
                   2722: 
                   2723: =pod
                   2724: 
1.648     raeburn  2725: =item * &keyword($word)
1.46      matthew  2726: 
                   2727: Returns true if $word is a keyword.  A keyword is a word that appears more 
                   2728: than the average number of times in the thesaurus database.  Calls 
                   2729: &initialize_keywords
                   2730: 
                   2731: =cut
                   2732: 
                   2733: ###################################################
1.20      www      2734: 
                   2735: sub keyword {
1.46      matthew  2736:     return if (!&initialize_keywords());
                   2737:     my $word=lc(shift());
                   2738:     $word=~s/\W//g;
                   2739:     return exists($Keywords{$word});
1.20      www      2740: }
1.46      matthew  2741: 
                   2742: ###############################################################
                   2743: 
                   2744: =pod 
1.20      www      2745: 
1.648     raeburn  2746: =item * &get_related_words()
1.46      matthew  2747: 
1.160     matthew  2748: Look up a word in the thesaurus.  Takes a scalar argument and returns
1.46      matthew  2749: an array of words.  If the keyword is not in the thesaurus, an empty array
                   2750: will be returned.  The order of the words returned is determined by the
                   2751: database which holds them.
                   2752: 
                   2753: Uses global $thesaurus_db_file.
                   2754: 
                   2755: =cut
                   2756: 
                   2757: ###############################################################
                   2758: sub get_related_words {
                   2759:     my $keyword = shift;
                   2760:     my %thesaurus_db;
                   2761:     if (! -e $thesaurus_db_file) {
                   2762:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
                   2763:                                  "failed because the file does not exist");
                   2764:         return ();
                   2765:     }
                   2766:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 2767:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  2768:         return ();
                   2769:     } 
                   2770:     my @Words=();
1.429     www      2771:     my $count=0;
1.46      matthew  2772:     if (exists($thesaurus_db{$keyword})) {
1.356     albertel 2773: 	# The first element is the number of times
                   2774: 	# the word appears.  We do not need it now.
1.429     www      2775: 	my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
                   2776: 	my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
                   2777: 	my $threshold=$mostfrequentcount/10;
                   2778:         foreach my $possibleword (@RelatedWords) {
                   2779:             my ($word,$wordcount)=split(/\,/,$possibleword);
                   2780:             if ($wordcount>$threshold) {
                   2781: 		push(@Words,$word);
                   2782:                 $count++;
                   2783:                 if ($count>10) { last; }
                   2784: 	    }
1.20      www      2785:         }
                   2786:     }
1.46      matthew  2787:     untie %thesaurus_db;
                   2788:     return @Words;
1.14      harris41 2789: }
1.46      matthew  2790: 
1.112     bowersj2 2791: =pod
                   2792: 
                   2793: =back
                   2794: 
                   2795: =cut
1.61      www      2796: 
                   2797: # -------------------------------------------------------------- Plaintext name
1.81      albertel 2798: =pod
                   2799: 
1.112     bowersj2 2800: =head1 User Name Functions
                   2801: 
                   2802: =over 4
                   2803: 
1.648     raeburn  2804: =item * &plainname($uname,$udom,$first)
1.81      albertel 2805: 
1.112     bowersj2 2806: Takes a users logon name and returns it as a string in
1.226     albertel 2807: "first middle last generation" form 
                   2808: if $first is set to 'lastname' then it returns it as
                   2809: 'lastname generation, firstname middlename' if their is a lastname
1.81      albertel 2810: 
                   2811: =cut
1.61      www      2812: 
1.295     www      2813: 
1.81      albertel 2814: ###############################################################
1.61      www      2815: sub plainname {
1.226     albertel 2816:     my ($uname,$udom,$first)=@_;
1.537     albertel 2817:     return if (!defined($uname) || !defined($udom));
1.295     www      2818:     my %names=&getnames($uname,$udom);
1.226     albertel 2819:     my $name=&Apache::lonnet::format_name($names{'firstname'},
                   2820: 					  $names{'middlename'},
                   2821: 					  $names{'lastname'},
                   2822: 					  $names{'generation'},$first);
                   2823:     $name=~s/^\s+//;
1.62      www      2824:     $name=~s/\s+$//;
                   2825:     $name=~s/\s+/ /g;
1.353     albertel 2826:     if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62      www      2827:     return $name;
1.61      www      2828: }
1.66      www      2829: 
                   2830: # -------------------------------------------------------------------- Nickname
1.81      albertel 2831: =pod
                   2832: 
1.648     raeburn  2833: =item * &nickname($uname,$udom)
1.81      albertel 2834: 
                   2835: Gets a users name and returns it as a string as
                   2836: 
                   2837: "&quot;nickname&quot;"
1.66      www      2838: 
1.81      albertel 2839: if the user has a nickname or
                   2840: 
                   2841: "first middle last generation"
                   2842: 
                   2843: if the user does not
                   2844: 
                   2845: =cut
1.66      www      2846: 
                   2847: sub nickname {
                   2848:     my ($uname,$udom)=@_;
1.537     albertel 2849:     return if (!defined($uname) || !defined($udom));
1.295     www      2850:     my %names=&getnames($uname,$udom);
1.68      albertel 2851:     my $name=$names{'nickname'};
1.66      www      2852:     if ($name) {
                   2853:        $name='&quot;'.$name.'&quot;'; 
                   2854:     } else {
                   2855:        $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
                   2856: 	     $names{'lastname'}.' '.$names{'generation'};
                   2857:        $name=~s/\s+$//;
                   2858:        $name=~s/\s+/ /g;
                   2859:     }
                   2860:     return $name;
                   2861: }
                   2862: 
1.295     www      2863: sub getnames {
                   2864:     my ($uname,$udom)=@_;
1.537     albertel 2865:     return if (!defined($uname) || !defined($udom));
1.433     albertel 2866:     if ($udom eq 'public' && $uname eq 'public') {
                   2867: 	return ('lastname' => &mt('Public'));
                   2868:     }
1.295     www      2869:     my $id=$uname.':'.$udom;
                   2870:     my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
                   2871:     if ($cached) {
                   2872: 	return %{$names};
                   2873:     } else {
                   2874: 	my %loadnames=&Apache::lonnet::get('environment',
                   2875:                     ['firstname','middlename','lastname','generation','nickname'],
                   2876: 					 $udom,$uname);
                   2877: 	&Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
                   2878: 	return %loadnames;
                   2879:     }
                   2880: }
1.61      www      2881: 
1.542     raeburn  2882: # -------------------------------------------------------------------- getemails
1.648     raeburn  2883: 
1.542     raeburn  2884: =pod
                   2885: 
1.648     raeburn  2886: =item * &getemails($uname,$udom)
1.542     raeburn  2887: 
                   2888: Gets a user's email information and returns it as a hash with keys:
                   2889: notification, critnotification, permanentemail
                   2890: 
                   2891: For notification and critnotification, values are comma-separated lists 
1.648     raeburn  2892: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542     raeburn  2893:  
1.648     raeburn  2894: 
1.542     raeburn  2895: =cut
                   2896: 
1.648     raeburn  2897: 
1.466     albertel 2898: sub getemails {
                   2899:     my ($uname,$udom)=@_;
                   2900:     if ($udom eq 'public' && $uname eq 'public') {
                   2901: 	return;
                   2902:     }
1.467     www      2903:     if (!$udom) { $udom=$env{'user.domain'}; }
                   2904:     if (!$uname) { $uname=$env{'user.name'}; }
1.466     albertel 2905:     my $id=$uname.':'.$udom;
                   2906:     my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
                   2907:     if ($cached) {
                   2908: 	return %{$names};
                   2909:     } else {
                   2910: 	my %loadnames=&Apache::lonnet::get('environment',
                   2911:                     			   ['notification','critnotification',
                   2912: 					    'permanentemail'],
                   2913: 					   $udom,$uname);
                   2914: 	&Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
                   2915: 	return %loadnames;
                   2916:     }
                   2917: }
                   2918: 
1.551     albertel 2919: sub flush_email_cache {
                   2920:     my ($uname,$udom)=@_;
                   2921:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   2922:     if (!$uname) { $uname=$env{'user.name'};   }
                   2923:     return if ($udom eq 'public' && $uname eq 'public');
                   2924:     my $id=$uname.':'.$udom;
                   2925:     &Apache::lonnet::devalidate_cache_new('emailscache',$id);
                   2926: }
                   2927: 
1.692.4.2  raeburn  2928: # -------------------------------------------------------------------- getlangs
                   2929: 
                   2930: =pod
                   2931: 
                   2932: =item * &getlangs($uname,$udom)
                   2933: 
                   2934: Gets a user's language preference and returns it as a hash with key:
                   2935: language.
                   2936: 
                   2937: =cut
                   2938: 
                   2939: 
                   2940: sub getlangs {
                   2941:     my ($uname,$udom) = @_;
                   2942:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   2943:     if (!$uname) { $uname=$env{'user.name'};   }
                   2944:     my $id=$uname.':'.$udom;
                   2945:     my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
                   2946:     if ($cached) {
                   2947:         return %{$langs};
                   2948:     } else {
                   2949:         my %loadlangs=&Apache::lonnet::get('environment',['languages'],
                   2950:                                            $udom,$uname);
                   2951:         &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
                   2952:         return %loadlangs;
                   2953:     }
                   2954: }
                   2955: 
                   2956: sub flush_langs_cache {
                   2957:     my ($uname,$udom)=@_;
                   2958:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   2959:     if (!$uname) { $uname=$env{'user.name'};   }
                   2960:     return if ($udom eq 'public' && $uname eq 'public');
                   2961:     my $id=$uname.':'.$udom;
                   2962:     &Apache::lonnet::devalidate_cache_new('userlangs',$id);
                   2963: }
                   2964: 
1.61      www      2965: # ------------------------------------------------------------------ Screenname
1.81      albertel 2966: 
                   2967: =pod
                   2968: 
1.648     raeburn  2969: =item * &screenname($uname,$udom)
1.81      albertel 2970: 
                   2971: Gets a users screenname and returns it as a string
                   2972: 
                   2973: =cut
1.61      www      2974: 
                   2975: sub screenname {
                   2976:     my ($uname,$udom)=@_;
1.258     albertel 2977:     if ($uname eq $env{'user.name'} &&
                   2978: 	$udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212     albertel 2979:     my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68      albertel 2980:     return $names{'screenname'};
1.62      www      2981: }
                   2982: 
1.692.4.2  raeburn  2983: # ------------------------------------------------------------- Confirm Wrapper
                   2984: =pod
                   2985: 
                   2986: =item confirmwrapper
                   2987: 
                   2988: Wrap messages about completion of operation in box
                   2989: 
                   2990: =cut
                   2991: 
                   2992: sub confirmwrapper {
                   2993:     my ($message)=@_;
                   2994:     if ($message) {
                   2995:         return "\n".'<div class="LC_confirm_box">'."\n"
                   2996:                .$message."\n"
                   2997:                .'</div>'."\n";
                   2998:     } else {
                   2999:         return $message;
                   3000:     }
                   3001: }
1.212     albertel 3002: 
1.62      www      3003: # ------------------------------------------------------------- Message Wrapper
                   3004: 
                   3005: sub messagewrapper {
1.369     www      3006:     my ($link,$username,$domain,$subject,$text)=@_;
1.62      www      3007:     return 
1.441     albertel 3008:         '<a href="/adm/email?compose=individual&amp;'.
                   3009:         'recname='.$username.'&amp;recdom='.$domain.
                   3010: 	'&amp;subject='.&escape($subject).'&amp;text='.&escape($text).'" '.
1.200     matthew  3011:         'title="'.&mt('Send message').'">'.$link.'</a>';
1.74      www      3012: }
                   3013: # --------------------------------------------------------------- Notes Wrapper
                   3014: 
                   3015: sub noteswrapper {
                   3016:     my ($link,$un,$do)=@_;
                   3017:     return 
                   3018: "<a href='/adm/email?recordftf=retrieve&recname=$un&recdom=$do'>$link</a>";
1.62      www      3019: }
                   3020: # ------------------------------------------------------------- Aboutme Wrapper
                   3021: 
                   3022: sub aboutmewrapper {
1.166     www      3023:     my ($link,$username,$domain,$target)=@_;
1.447     raeburn  3024:     if (!defined($username)  && !defined($domain)) {
                   3025:         return;
                   3026:     }
1.205     www      3027:     return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.692.4.2  raeburn  3028: 	($target?' target="$target"':'').' title="'.&mt("View this user's personal information page").'">'.$link.'</a>';
1.62      www      3029: }
                   3030: 
                   3031: # ------------------------------------------------------------ Syllabus Wrapper
                   3032: 
                   3033: 
                   3034: sub syllabuswrapper {
1.109     matthew  3035:     my ($linktext,$coursedir,$domain,$fontcolor)=@_;
                   3036:     if ($fontcolor) { 
                   3037:         $linktext='<font color="'.$fontcolor.'">'.$linktext.'</font>'; 
                   3038:     }
1.208     matthew  3039:     return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61      www      3040: }
1.14      harris41 3041: 
1.208     matthew  3042: sub track_student_link {
1.692.4.17  raeburn  3043:     my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
1.268     albertel 3044:     my $link ="/adm/trackstudent?";
1.208     matthew  3045:     my $title = 'View recent activity';
                   3046:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   3047:         defined($sdom)  && $sdom  !~ /^\s*$/) {
1.268     albertel 3048:         $link .= "selected_student=$sname:$sdom";
1.208     matthew  3049:         $title .= ' of this student';
1.268     albertel 3050:     } 
1.208     matthew  3051:     if (defined($target) && $target !~ /^\s*$/) {
                   3052:         $target = qq{target="$target"};
                   3053:     } else {
                   3054:         $target = '';
                   3055:     }
1.268     albertel 3056:     if ($start) { $link.='&amp;start='.$start; }
1.692.4.17  raeburn  3057:     if ($only_body) { $link .= '&amp;only_body=1'; }
1.554     albertel 3058:     $title = &mt($title);
                   3059:     $linktext = &mt($linktext);
1.448     albertel 3060:     return qq{<a href="$link" title="$title" $target>$linktext</a>}.
                   3061: 	&help_open_topic('View_recent_activity');
1.208     matthew  3062: }
                   3063: 
1.692.4.2  raeburn  3064: sub slot_reservations_link {
                   3065:     my ($linktext,$sname,$sdom,$target) = @_;
                   3066:     my $link ="/adm/slotrequest?command=showresv&amp;origin=aboutme";
                   3067:     my $title = 'View slot reservation history';
                   3068:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   3069:         defined($sdom)  && $sdom  !~ /^\s*$/) {
                   3070:         $link .= "&amp;uname=$sname&amp;udom=$sdom";
                   3071:         $title .= ' of this student';
                   3072:     }
                   3073:     if (defined($target) && $target !~ /^\s*$/) {
                   3074:         $target = qq{target="$target"};
                   3075:     } else {
                   3076:         $target = '';
                   3077:     }
                   3078:     $title = &mt($title);
                   3079:     $linktext = &mt($linktext);
                   3080:     return qq{<a href="$link" title="$title" $target>$linktext</a>};
                   3081: # FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
                   3082: 
                   3083: }
                   3084: 
1.508     www      3085: # ===================================================== Display a student photo
                   3086: 
                   3087: 
1.509     albertel 3088: sub student_image_tag {
1.508     www      3089:     my ($domain,$user)=@_;
                   3090:     my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
                   3091:     if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
                   3092: 	return '<img src="'.$imgsrc.'" align="right" />';
                   3093:     } else {
                   3094: 	return '';
                   3095:     }
                   3096: }
                   3097: 
1.112     bowersj2 3098: =pod
                   3099: 
                   3100: =back
                   3101: 
                   3102: =head1 Access .tab File Data
                   3103: 
                   3104: =over 4
                   3105: 
1.648     raeburn  3106: =item * &languageids() 
1.112     bowersj2 3107: 
                   3108: returns list of all language ids
                   3109: 
                   3110: =cut
                   3111: 
1.14      harris41 3112: sub languageids {
1.16      harris41 3113:     return sort(keys(%language));
1.14      harris41 3114: }
                   3115: 
1.112     bowersj2 3116: =pod
                   3117: 
1.648     raeburn  3118: =item * &languagedescription() 
1.112     bowersj2 3119: 
                   3120: returns description of a specified language id
                   3121: 
                   3122: =cut
                   3123: 
1.14      harris41 3124: sub languagedescription {
1.125     www      3125:     my $code=shift;
                   3126:     return  ($supported_language{$code}?'* ':'').
                   3127:             $language{$code}.
1.126     www      3128: 	    ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145     www      3129: }
                   3130: 
                   3131: sub plainlanguagedescription {
                   3132:     my $code=shift;
                   3133:     return $language{$code};
                   3134: }
                   3135: 
                   3136: sub supportedlanguagecode {
                   3137:     my $code=shift;
                   3138:     return $supported_language{$code};
1.97      www      3139: }
                   3140: 
1.112     bowersj2 3141: =pod
                   3142: 
1.648     raeburn  3143: =item * &copyrightids() 
1.112     bowersj2 3144: 
                   3145: returns list of all copyrights
                   3146: 
                   3147: =cut
                   3148: 
                   3149: sub copyrightids {
                   3150:     return sort(keys(%cprtag));
                   3151: }
                   3152: 
                   3153: =pod
                   3154: 
1.648     raeburn  3155: =item * &copyrightdescription() 
1.112     bowersj2 3156: 
                   3157: returns description of a specified copyright id
                   3158: 
                   3159: =cut
                   3160: 
                   3161: sub copyrightdescription {
1.166     www      3162:     return &mt($cprtag{shift(@_)});
1.112     bowersj2 3163: }
1.197     matthew  3164: 
                   3165: =pod
                   3166: 
1.648     raeburn  3167: =item * &source_copyrightids() 
1.192     taceyjo1 3168: 
                   3169: returns list of all source copyrights
                   3170: 
                   3171: =cut
                   3172: 
                   3173: sub source_copyrightids {
                   3174:     return sort(keys(%scprtag));
                   3175: }
                   3176: 
                   3177: =pod
                   3178: 
1.648     raeburn  3179: =item * &source_copyrightdescription() 
1.192     taceyjo1 3180: 
                   3181: returns description of a specified source copyright id
                   3182: 
                   3183: =cut
                   3184: 
                   3185: sub source_copyrightdescription {
                   3186:     return &mt($scprtag{shift(@_)});
                   3187: }
1.112     bowersj2 3188: 
                   3189: =pod
                   3190: 
1.648     raeburn  3191: =item * &filecategories() 
1.112     bowersj2 3192: 
                   3193: returns list of all file categories
                   3194: 
                   3195: =cut
                   3196: 
                   3197: sub filecategories {
                   3198:     return sort(keys(%category_extensions));
                   3199: }
                   3200: 
                   3201: =pod
                   3202: 
1.648     raeburn  3203: =item * &filecategorytypes() 
1.112     bowersj2 3204: 
                   3205: returns list of file types belonging to a given file
                   3206: category
                   3207: 
                   3208: =cut
                   3209: 
                   3210: sub filecategorytypes {
1.356     albertel 3211:     my ($cat) = @_;
                   3212:     return @{$category_extensions{lc($cat)}};
1.112     bowersj2 3213: }
                   3214: 
                   3215: =pod
                   3216: 
1.648     raeburn  3217: =item * &fileembstyle() 
1.112     bowersj2 3218: 
                   3219: returns embedding style for a specified file type
                   3220: 
                   3221: =cut
                   3222: 
                   3223: sub fileembstyle {
                   3224:     return $fe{lc(shift(@_))};
1.169     www      3225: }
                   3226: 
1.351     www      3227: sub filemimetype {
                   3228:     return $fm{lc(shift(@_))};
                   3229: }
                   3230: 
1.169     www      3231: 
                   3232: sub filecategoryselect {
                   3233:     my ($name,$value)=@_;
1.189     matthew  3234:     return &select_form($value,$name,
1.169     www      3235: 			'' => &mt('Any category'),
                   3236: 			map { $_,$_ } sort(keys(%category_extensions)));
1.112     bowersj2 3237: }
                   3238: 
                   3239: =pod
                   3240: 
1.648     raeburn  3241: =item * &filedescription() 
1.112     bowersj2 3242: 
                   3243: returns description for a specified file type
                   3244: 
                   3245: =cut
                   3246: 
                   3247: sub filedescription {
1.188     matthew  3248:     my $file_description = $fd{lc(shift())};
                   3249:     $file_description =~ s:([\[\]]):~$1:g;
                   3250:     return &mt($file_description);
1.112     bowersj2 3251: }
                   3252: 
                   3253: =pod
                   3254: 
1.648     raeburn  3255: =item * &filedescriptionex() 
1.112     bowersj2 3256: 
                   3257: returns description for a specified file type with
                   3258: extra formatting
                   3259: 
                   3260: =cut
                   3261: 
                   3262: sub filedescriptionex {
                   3263:     my $ex=shift;
1.188     matthew  3264:     my $file_description = $fd{lc($ex)};
                   3265:     $file_description =~ s:([\[\]]):~$1:g;
                   3266:     return '.'.$ex.' '.&mt($file_description);
1.112     bowersj2 3267: }
                   3268: 
                   3269: # End of .tab access
                   3270: =pod
                   3271: 
                   3272: =back
                   3273: 
                   3274: =cut
                   3275: 
                   3276: # ------------------------------------------------------------------ File Types
                   3277: sub fileextensions {
                   3278:     return sort(keys(%fe));
                   3279: }
                   3280: 
1.97      www      3281: # ----------------------------------------------------------- Display Languages
                   3282: # returns a hash with all desired display languages
                   3283: #
                   3284: 
                   3285: sub display_languages {
                   3286:     my %languages=();
1.692.4.1  raeburn  3287:     foreach my $lang (&Apache::lonlocal::preferred_languages()) {
1.356     albertel 3288: 	$languages{$lang}=1;
1.97      www      3289:     }
                   3290:     &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258     albertel 3291:     if ($env{'form.displaylanguage'}) {
1.356     albertel 3292: 	foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
                   3293: 	    $languages{$lang}=1;
1.97      www      3294:         }
                   3295:     }
                   3296:     return %languages;
1.14      harris41 3297: }
                   3298: 
1.582     albertel 3299: sub languages {
                   3300:     my ($possible_langs) = @_;
1.692.4.1  raeburn  3301:     my @preferred_langs = &Apache::lonlocal::preferred_languages();
1.582     albertel 3302:     if (!ref($possible_langs)) {
                   3303: 	if( wantarray ) {
                   3304: 	    return @preferred_langs;
                   3305: 	} else {
                   3306: 	    return $preferred_langs[0];
                   3307: 	}
                   3308:     }
                   3309:     my %possibilities = map { $_ => 1 } (@$possible_langs);
                   3310:     my @preferred_possibilities;
                   3311:     foreach my $preferred_lang (@preferred_langs) {
                   3312: 	if (exists($possibilities{$preferred_lang})) {
                   3313: 	    push(@preferred_possibilities, $preferred_lang);
                   3314: 	}
                   3315:     }
                   3316:     if( wantarray ) {
                   3317: 	return @preferred_possibilities;
                   3318:     }
                   3319:     return $preferred_possibilities[0];
                   3320: }
                   3321: 
1.692.4.2  raeburn  3322: sub user_lang {
                   3323:     my ($touname,$toudom,$fromcid) = @_;
                   3324:     my @userlangs;
                   3325:     if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
                   3326:         @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
                   3327:                     $env{'course.'.$fromcid.'.languages'}));
                   3328:     } else {
                   3329:         my %langhash = &getlangs($touname,$toudom);
                   3330:         if ($langhash{'languages'} ne '') {
                   3331:             @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
                   3332:         } else {
                   3333:             my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
                   3334:             if ($domdefs{'lang_def'} ne '') {
                   3335:                 @userlangs = ($domdefs{'lang_def'});
                   3336:             }
                   3337:         }
                   3338:     }
                   3339:     my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
                   3340:     my $user_lh = Apache::localize->get_handle(@languages);
                   3341:     return $user_lh;
                   3342: }
                   3343: 
1.112     bowersj2 3344: ###############################################################
                   3345: ##               Student Answer Attempts                     ##
                   3346: ###############################################################
                   3347: 
                   3348: =pod
                   3349: 
                   3350: =head1 Alternate Problem Views
                   3351: 
                   3352: =over 4
                   3353: 
1.648     raeburn  3354: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.112     bowersj2 3355:     $getattempt, $regexp, $gradesub)
                   3356: 
                   3357: Return string with previous attempt on problem. Arguments:
                   3358: 
                   3359: =over 4
                   3360: 
                   3361: =item * $symb: Problem, including path
                   3362: 
                   3363: =item * $username: username of the desired student
                   3364: 
                   3365: =item * $domain: domain of the desired student
1.14      harris41 3366: 
1.112     bowersj2 3367: =item * $course: Course ID
1.14      harris41 3368: 
1.112     bowersj2 3369: =item * $getattempt: Leave blank for all attempts, otherwise put
                   3370:     something
1.14      harris41 3371: 
1.112     bowersj2 3372: =item * $regexp: if string matches this regexp, the string will be
                   3373:     sent to $gradesub
1.14      harris41 3374: 
1.112     bowersj2 3375: =item * $gradesub: routine that processes the string if it matches $regexp
1.14      harris41 3376: 
1.112     bowersj2 3377: =back
1.14      harris41 3378: 
1.112     bowersj2 3379: The output string is a table containing all desired attempts, if any.
1.16      harris41 3380: 
1.112     bowersj2 3381: =cut
1.1       albertel 3382: 
                   3383: sub get_previous_attempt {
1.43      ng       3384:   my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub)=@_;
1.1       albertel 3385:   my $prevattempts='';
1.43      ng       3386:   no strict 'refs';
1.1       albertel 3387:   if ($symb) {
1.3       albertel 3388:     my (%returnhash)=
                   3389:       &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1       albertel 3390:     if ($returnhash{'version'}) {
                   3391:       my %lasthash=();
                   3392:       my $version;
                   3393:       for ($version=1;$version<=$returnhash{'version'};$version++) {
1.356     albertel 3394:         foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
                   3395: 	  $lasthash{$key}=$returnhash{$version.':'.$key};
1.19      harris41 3396:         }
1.1       albertel 3397:       }
1.596     albertel 3398:       $prevattempts=&start_data_table().&start_data_table_header_row();
                   3399:       $prevattempts.='<th>'.&mt('History').'</th>';
1.356     albertel 3400:       foreach my $key (sort(keys(%lasthash))) {
                   3401: 	my ($ign,@parts) = split(/\./,$key);
1.41      ng       3402: 	if ($#parts > 0) {
1.31      albertel 3403: 	  my $data=$parts[-1];
                   3404: 	  pop(@parts);
1.596     albertel 3405: 	  $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.'&nbsp;</th>';
1.31      albertel 3406: 	} else {
1.41      ng       3407: 	  if ($#parts == 0) {
                   3408: 	    $prevattempts.='<th>'.$parts[0].'</th>';
                   3409: 	  } else {
                   3410: 	    $prevattempts.='<th>'.$ign.'</th>';
                   3411: 	  }
1.31      albertel 3412: 	}
1.16      harris41 3413:       }
1.596     albertel 3414:       $prevattempts.=&end_data_table_header_row();
1.40      ng       3415:       if ($getattempt eq '') {
                   3416: 	for ($version=1;$version<=$returnhash{'version'};$version++) {
1.596     albertel 3417: 	  $prevattempts.=&start_data_table_row().
                   3418: 	      '<td>'.&mt('Transaction [_1]',$version).'</td>';
1.356     albertel 3419: 	    foreach my $key (sort(keys(%lasthash))) {
1.581     albertel 3420: 		my $value = &format_previous_attempt_value($key,
                   3421: 							   $returnhash{$version.':'.$key});
                   3422: 		$prevattempts.='<td>'.$value.'&nbsp;</td>';   
1.40      ng       3423: 	    }
1.596     albertel 3424: 	  $prevattempts.=&end_data_table_row();
1.40      ng       3425: 	 }
1.1       albertel 3426:       }
1.596     albertel 3427:       $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356     albertel 3428:       foreach my $key (sort(keys(%lasthash))) {
1.581     albertel 3429: 	my $value = &format_previous_attempt_value($key,$lasthash{$key});
1.356     albertel 3430: 	if ($key =~/$regexp$/ && (defined &$gradesub)) {$value = &$gradesub($value)}
1.40      ng       3431: 	$prevattempts.='<td>'.$value.'&nbsp;</td>';
1.16      harris41 3432:       }
1.596     albertel 3433:       $prevattempts.= &end_data_table_row().&end_data_table();
1.1       albertel 3434:     } else {
1.596     albertel 3435:       $prevattempts=
                   3436: 	  &start_data_table().&start_data_table_row().
                   3437: 	  '<td>'.&mt('Nothing submitted - no attempts.').'</td>'.
                   3438: 	  &end_data_table_row().&end_data_table();
1.1       albertel 3439:     }
                   3440:   } else {
1.596     albertel 3441:     $prevattempts=
                   3442: 	  &start_data_table().&start_data_table_row().
                   3443: 	  '<td>'.&mt('No data.').'</td>'.
                   3444: 	  &end_data_table_row().&end_data_table();
1.1       albertel 3445:   }
1.10      albertel 3446: }
                   3447: 
1.581     albertel 3448: sub format_previous_attempt_value {
                   3449:     my ($key,$value) = @_;
                   3450:     if ($key =~ /timestamp/) {
                   3451: 	$value = &Apache::lonlocal::locallocaltime($value);
                   3452:     } elsif (ref($value) eq 'ARRAY') {
                   3453: 	$value = '('.join(', ', @{ $value }).')';
                   3454:     } else {
                   3455: 	$value = &unescape($value);
                   3456:     }
                   3457:     return $value;
                   3458: }
                   3459: 
                   3460: 
1.107     albertel 3461: sub relative_to_absolute {
                   3462:     my ($url,$output)=@_;
                   3463:     my $parser=HTML::TokeParser->new(\$output);
                   3464:     my $token;
                   3465:     my $thisdir=$url;
                   3466:     my @rlinks=();
                   3467:     while ($token=$parser->get_token) {
                   3468: 	if ($token->[0] eq 'S') {
                   3469: 	    if ($token->[1] eq 'a') {
                   3470: 		if ($token->[2]->{'href'}) {
                   3471: 		    $rlinks[$#rlinks+1]=$token->[2]->{'href'};
                   3472: 		}
                   3473: 	    } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
                   3474: 		$rlinks[$#rlinks+1]=$token->[2]->{'src'};
                   3475: 	    } elsif ($token->[1] eq 'base') {
                   3476: 		$thisdir=$token->[2]->{'href'};
                   3477: 	    }
                   3478: 	}
                   3479:     }
                   3480:     $thisdir=~s-/[^/]*$--;
1.356     albertel 3481:     foreach my $link (@rlinks) {
1.692.4.2  raeburn  3482: 	unless (($link=~/^https?\:\/\//i) ||
1.356     albertel 3483: 		($link=~/^\//) ||
                   3484: 		($link=~/^javascript:/i) ||
                   3485: 		($link=~/^mailto:/i) ||
                   3486: 		($link=~/^\#/)) {
                   3487: 	    my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
                   3488: 	    $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107     albertel 3489: 	}
                   3490:     }
                   3491: # -------------------------------------------------- Deal with Applet codebases
                   3492:     $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
                   3493:     return $output;
                   3494: }
                   3495: 
1.112     bowersj2 3496: =pod
                   3497: 
1.648     raeburn  3498: =item * &get_student_view()
1.112     bowersj2 3499: 
                   3500: show a snapshot of what student was looking at
                   3501: 
                   3502: =cut
                   3503: 
1.10      albertel 3504: sub get_student_view {
1.186     albertel 3505:   my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114     www      3506:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 3507:   my (%form);
1.10      albertel 3508:   my @elements=('symb','courseid','domain','username');
                   3509:   foreach my $element (@elements) {
1.186     albertel 3510:       $form{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 3511:   }
1.186     albertel 3512:   if (defined($moreenv)) {
                   3513:       %form=(%form,%{$moreenv});
                   3514:   }
1.236     albertel 3515:   if (defined($target)) { $form{'grade_target'} = $target; }
1.107     albertel 3516:   $feedurl=&Apache::lonnet::clutter($feedurl);
1.650     www      3517:   my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11      albertel 3518:   $userview=~s/\<body[^\>]*\>//gi;
                   3519:   $userview=~s/\<\/body\>//gi;
                   3520:   $userview=~s/\<html\>//gi;
                   3521:   $userview=~s/\<\/html\>//gi;
                   3522:   $userview=~s/\<head\>//gi;
                   3523:   $userview=~s/\<\/head\>//gi;
                   3524:   $userview=~s/action\s*\=/would_be_action\=/gi;
1.107     albertel 3525:   $userview=&relative_to_absolute($feedurl,$userview);
1.650     www      3526:   if (wantarray) {
                   3527:      return ($userview,$response);
                   3528:   } else {
                   3529:      return $userview;
                   3530:   }
                   3531: }
                   3532: 
                   3533: sub get_student_view_with_retries {
                   3534:   my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
                   3535: 
                   3536:     my $ok = 0;                 # True if we got a good response.
                   3537:     my $content;
                   3538:     my $response;
                   3539: 
                   3540:     # Try to get the student_view done. within the retries count:
                   3541:     
                   3542:     do {
                   3543:          ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
                   3544:          $ok      = $response->is_success;
                   3545:          if (!$ok) {
                   3546:             &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
                   3547:          }
                   3548:          $retries--;
                   3549:     } while (!$ok && ($retries > 0));
                   3550:     
                   3551:     if (!$ok) {
                   3552:        $content = '';          # On error return an empty content.
                   3553:     }
1.651     www      3554:     if (wantarray) {
                   3555:        return ($content, $response);
                   3556:     } else {
                   3557:        return $content;
                   3558:     }
1.11      albertel 3559: }
                   3560: 
1.112     bowersj2 3561: =pod
                   3562: 
1.648     raeburn  3563: =item * &get_student_answers() 
1.112     bowersj2 3564: 
                   3565: show a snapshot of how student was answering problem
                   3566: 
                   3567: =cut
                   3568: 
1.11      albertel 3569: sub get_student_answers {
1.100     sakharuk 3570:   my ($symb,$username,$domain,$courseid,%form) = @_;
1.114     www      3571:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 3572:   my (%moreenv);
1.11      albertel 3573:   my @elements=('symb','courseid','domain','username');
                   3574:   foreach my $element (@elements) {
1.186     albertel 3575:     $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 3576:   }
1.186     albertel 3577:   $moreenv{'grade_target'}='answer';
                   3578:   %moreenv=(%form,%moreenv);
1.497     raeburn  3579:   $feedurl = &Apache::lonnet::clutter($feedurl);
                   3580:   my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10      albertel 3581:   return $userview;
1.1       albertel 3582: }
1.116     albertel 3583: 
                   3584: =pod
                   3585: 
                   3586: =item * &submlink()
                   3587: 
1.242     albertel 3588: Inputs: $text $uname $udom $symb $target
1.116     albertel 3589: 
                   3590: Returns: A link to grades.pm such as to see the SUBM view of a student
                   3591: 
                   3592: =cut
                   3593: 
                   3594: ###############################################
                   3595: sub submlink {
1.242     albertel 3596:     my ($text,$uname,$udom,$symb,$target)=@_;
1.116     albertel 3597:     if (!($uname && $udom)) {
                   3598: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 3599: 	    &Apache::lonnet::whichuser($symb);
1.116     albertel 3600: 	if (!$symb) { $symb=$cursymb; }
                   3601:     }
1.254     matthew  3602:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      3603:     $symb=&escape($symb);
1.242     albertel 3604:     if ($target) { $target="target=\"$target\""; }
                   3605:     return '<a href="/adm/grades?&command=submission&'.
                   3606: 	'symb='.$symb.'&student='.$uname.
                   3607: 	'&userdom='.$udom.'" '.$target.'>'.$text.'</a>';
                   3608: }
                   3609: ##############################################
                   3610: 
                   3611: =pod
                   3612: 
                   3613: =item * &pgrdlink()
                   3614: 
                   3615: Inputs: $text $uname $udom $symb $target
                   3616: 
                   3617: Returns: A link to grades.pm such as to see the PGRD view of a student
                   3618: 
                   3619: =cut
                   3620: 
                   3621: ###############################################
                   3622: sub pgrdlink {
                   3623:     my $link=&submlink(@_);
                   3624:     $link=~s/(&command=submission)/$1&showgrading=yes/;
                   3625:     return $link;
                   3626: }
                   3627: ##############################################
                   3628: 
                   3629: =pod
                   3630: 
                   3631: =item * &pprmlink()
                   3632: 
                   3633: Inputs: $text $uname $udom $symb $target
                   3634: 
                   3635: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283     albertel 3636: student and a specific resource
1.242     albertel 3637: 
                   3638: =cut
                   3639: 
                   3640: ###############################################
                   3641: sub pprmlink {
                   3642:     my ($text,$uname,$udom,$symb,$target)=@_;
                   3643:     if (!($uname && $udom)) {
                   3644: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 3645: 	    &Apache::lonnet::whichuser($symb);
1.242     albertel 3646: 	if (!$symb) { $symb=$cursymb; }
                   3647:     }
1.254     matthew  3648:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      3649:     $symb=&escape($symb);
1.242     albertel 3650:     if ($target) { $target="target=\"$target\""; }
1.595     albertel 3651:     return '<a href="/adm/parmset?command=set&amp;'.
                   3652: 	'symb='.$symb.'&amp;uname='.$uname.
                   3653: 	'&amp;udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116     albertel 3654: }
                   3655: ##############################################
1.37      matthew  3656: 
1.112     bowersj2 3657: =pod
                   3658: 
                   3659: =back
                   3660: 
                   3661: =cut
                   3662: 
1.37      matthew  3663: ###############################################
1.51      www      3664: 
                   3665: 
                   3666: sub timehash {
1.687     raeburn  3667:     my ($thistime) = @_;
                   3668:     my $timezone = &Apache::lonlocal::gettimezone();
                   3669:     my $dt = DateTime->from_epoch(epoch => $thistime)
                   3670:                      ->set_time_zone($timezone);
                   3671:     my $wday = $dt->day_of_week();
                   3672:     if ($wday == 7) { $wday = 0; }
                   3673:     return ( 'second' => $dt->second(),
                   3674:              'minute' => $dt->minute(),
                   3675:              'hour'   => $dt->hour(),
                   3676:              'day'     => $dt->day_of_month(),
                   3677:              'month'   => $dt->month(),
                   3678:              'year'    => $dt->year(),
                   3679:              'weekday' => $wday,
                   3680:              'dayyear' => $dt->day_of_year(),
                   3681:              'dlsav'   => $dt->is_dst() );
1.51      www      3682: }
                   3683: 
1.370     www      3684: sub utc_string {
                   3685:     my ($date)=@_;
1.371     www      3686:     return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370     www      3687: }
                   3688: 
1.51      www      3689: sub maketime {
                   3690:     my %th=@_;
1.687     raeburn  3691:     my ($epoch_time,$timezone,$dt);
                   3692:     $timezone = &Apache::lonlocal::gettimezone();
                   3693:     eval {
                   3694:         $dt = DateTime->new( year   => $th{'year'},
                   3695:                              month  => $th{'month'},
                   3696:                              day    => $th{'day'},
                   3697:                              hour   => $th{'hour'},
                   3698:                              minute => $th{'minute'},
                   3699:                              second => $th{'second'},
                   3700:                              time_zone => $timezone,
                   3701:                          );
                   3702:     };
                   3703:     if (!$@) {
                   3704:         $epoch_time = $dt->epoch;
                   3705:         if ($epoch_time) {
                   3706:             return $epoch_time;
                   3707:         }
                   3708:     }
1.51      www      3709:     return POSIX::mktime(
                   3710:         ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210     www      3711:          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70      www      3712: }
                   3713: 
                   3714: #########################################
1.51      www      3715: 
                   3716: sub findallcourses {
1.482     raeburn  3717:     my ($roles,$uname,$udom) = @_;
1.355     albertel 3718:     my %roles;
                   3719:     if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348     albertel 3720:     my %courses;
1.51      www      3721:     my $now=time;
1.482     raeburn  3722:     if (!defined($uname)) {
                   3723:         $uname = $env{'user.name'};
                   3724:     }
                   3725:     if (!defined($udom)) {
                   3726:         $udom = $env{'user.domain'};
                   3727:     }
                   3728:     if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
                   3729:         my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
                   3730:         if (!%roles) {
                   3731:             %roles = (
                   3732:                        cc => 1,
1.692.4.22! raeburn  3733:                        co => 1,
1.482     raeburn  3734:                        in => 1,
                   3735:                        ep => 1,
                   3736:                        ta => 1,
                   3737:                        cr => 1,
                   3738:                        st => 1,
                   3739:              );
                   3740:         }
                   3741:         foreach my $entry (keys(%roleshash)) {
                   3742:             my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
                   3743:             if ($trole =~ /^cr/) { 
                   3744:                 next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
                   3745:             } else {
                   3746:                 next if (!exists($roles{$trole}));
                   3747:             }
                   3748:             if ($tend) {
                   3749:                 next if ($tend < $now);
                   3750:             }
                   3751:             if ($tstart) {
                   3752:                 next if ($tstart > $now);
                   3753:             }
                   3754:             my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role,$realsec);
                   3755:             (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
                   3756:             if ($secpart eq '') {
                   3757:                 ($cnum,$role) = split(/_/,$cnumpart); 
                   3758:                 $sec = 'none';
                   3759:                 $realsec = '';
                   3760:             } else {
                   3761:                 $cnum = $cnumpart;
                   3762:                 ($sec,$role) = split(/_/,$secpart);
                   3763:                 $realsec = $sec;
1.490     raeburn  3764:             }
1.482     raeburn  3765:             $courses{$cdom.'_'.$cnum}{$sec} = $trole.'/'.$cdom.'/'.$cnum.'/'.$realsec;
                   3766:         }
                   3767:     } else {
                   3768:         foreach my $key (keys(%env)) {
1.483     albertel 3769: 	    if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
                   3770:                  $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482     raeburn  3771: 	        my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
                   3772: 	        next if ($role eq 'ca' || $role eq 'aa');
                   3773: 	        next if (%roles && !exists($roles{$role}));
                   3774: 	        my ($starttime,$endtime)=split(/\./,$env{$key});
                   3775:                 my $active=1;
                   3776:                 if ($starttime) {
                   3777: 		    if ($now<$starttime) { $active=0; }
                   3778:                 }
                   3779:                 if ($endtime) {
                   3780:                     if ($now>$endtime) { $active=0; }
                   3781:                 }
                   3782:                 if ($active) {
                   3783:                     if ($sec eq '') {
                   3784:                         $sec = 'none';
                   3785:                     }
                   3786:                     $courses{$cdom.'_'.$cnum}{$sec} = 
                   3787:                                      $role.'/'.$cdom.'/'.$cnum.'/'.$sec;
1.474     raeburn  3788:                 }
                   3789:             }
1.51      www      3790:         }
                   3791:     }
1.474     raeburn  3792:     return %courses;
1.51      www      3793: }
1.37      matthew  3794: 
1.54      www      3795: ###############################################
1.474     raeburn  3796: 
                   3797: sub blockcheck {
1.482     raeburn  3798:     my ($setters,$activity,$uname,$udom) = @_;
1.490     raeburn  3799: 
                   3800:     if (!defined($udom)) {
                   3801:         $udom = $env{'user.domain'};
                   3802:     }
                   3803:     if (!defined($uname)) {
                   3804:         $uname = $env{'user.name'};
                   3805:     }
                   3806: 
                   3807:     # If uname and udom are for a course, check for blocks in the course.
                   3808: 
                   3809:     if (&Apache::lonnet::is_course($udom,$uname)) {
                   3810:         my %records = &Apache::lonnet::dump('comm_block',$udom,$uname);
1.502     raeburn  3811:         my ($startblock,$endblock)=&get_blocks($setters,$activity,$udom,$uname);
1.490     raeburn  3812:         return ($startblock,$endblock);
                   3813:     }
1.474     raeburn  3814: 
1.502     raeburn  3815:     my $startblock = 0;
                   3816:     my $endblock = 0;
1.482     raeburn  3817:     my %live_courses = &findallcourses(undef,$uname,$udom);
1.474     raeburn  3818: 
1.490     raeburn  3819:     # If uname is for a user, and activity is course-specific, i.e.,
                   3820:     # boards, chat or groups, check for blocking in current course only.
1.474     raeburn  3821: 
1.490     raeburn  3822:     if (($activity eq 'boards' || $activity eq 'chat' ||
                   3823:          $activity eq 'groups') && ($env{'request.course.id'})) {
                   3824:         foreach my $key (keys(%live_courses)) {
                   3825:             if ($key ne $env{'request.course.id'}) {
                   3826:                 delete($live_courses{$key});
                   3827:             }
                   3828:         }
                   3829:     }
                   3830: 
                   3831:     my $otheruser = 0;
                   3832:     my %own_courses;
                   3833:     if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
                   3834:         # Resource belongs to user other than current user.
                   3835:         $otheruser = 1;
                   3836:         # Gather courses for current user
                   3837:         %own_courses = 
                   3838:             &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
                   3839:     }
                   3840: 
                   3841:     # Gather active course roles - course coordinator, instructor, 
                   3842:     # exam proctor, ta, student, or custom role.
1.474     raeburn  3843: 
                   3844:     foreach my $course (keys(%live_courses)) {
1.482     raeburn  3845:         my ($cdom,$cnum);
                   3846:         if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
                   3847:             $cdom = $env{'course.'.$course.'.domain'};
                   3848:             $cnum = $env{'course.'.$course.'.num'};
                   3849:         } else {
1.490     raeburn  3850:             ($cdom,$cnum) = split(/_/,$course); 
1.482     raeburn  3851:         }
                   3852:         my $no_ownblock = 0;
                   3853:         my $no_userblock = 0;
1.533     raeburn  3854:         if ($otheruser && $activity ne 'com') {
1.490     raeburn  3855:             # Check if current user has 'evb' priv for this
                   3856:             if (defined($own_courses{$course})) {
                   3857:                 foreach my $sec (keys(%{$own_courses{$course}})) {
                   3858:                     my $checkrole = 'cm./'.$cdom.'/'.$cnum;
                   3859:                     if ($sec ne 'none') {
                   3860:                         $checkrole .= '/'.$sec;
                   3861:                     }
                   3862:                     if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   3863:                         $no_ownblock = 1;
                   3864:                         last;
                   3865:                     }
                   3866:                 }
                   3867:             }
                   3868:             # if they have 'evb' priv and are currently not playing student
                   3869:             next if (($no_ownblock) &&
                   3870:                  ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
                   3871:         }
1.474     raeburn  3872:         foreach my $sec (keys(%{$live_courses{$course}})) {
1.482     raeburn  3873:             my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474     raeburn  3874:             if ($sec ne 'none') {
1.482     raeburn  3875:                 $checkrole .= '/'.$sec;
1.474     raeburn  3876:             }
1.490     raeburn  3877:             if ($otheruser) {
                   3878:                 # Resource belongs to user other than current user.
                   3879:                 # Assemble privs for that user, and check for 'evb' priv.
1.482     raeburn  3880:                 my ($trole,$tdom,$tnum,$tsec);
                   3881:                 my $entry = $live_courses{$course}{$sec};
                   3882:                 if ($entry =~ /^cr/) {
                   3883:                     ($trole,$tdom,$tnum,$tsec) = 
                   3884:                       ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
                   3885:                 } else {
                   3886:                     ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
                   3887:                 }
                   3888:                 my ($spec,$area,$trest,%allroles,%userroles);
                   3889:                 $area = '/'.$tdom.'/'.$tnum;
                   3890:                 $trest = $tnum;
                   3891:                 if ($tsec ne '') {
                   3892:                     $area .= '/'.$tsec;
                   3893:                     $trest .= '/'.$tsec;
                   3894:                 }
                   3895:                 $spec = $trole.'.'.$area;
                   3896:                 if ($trole =~ /^cr/) {
                   3897:                     &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
                   3898:                                                       $tdom,$spec,$trest,$area);
                   3899:                 } else {
                   3900:                     &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
                   3901:                                                        $tdom,$spec,$trest,$area);
                   3902:                 }
                   3903:                 my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
1.486     raeburn  3904:                 if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
                   3905:                     if ($1) {
                   3906:                         $no_userblock = 1;
                   3907:                         last;
                   3908:                     }
                   3909:                 }
1.490     raeburn  3910:             } else {
                   3911:                 # Resource belongs to current user
                   3912:                 # Check for 'evb' priv via lonnet::allowed().
1.482     raeburn  3913:                 if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   3914:                     $no_ownblock = 1;
                   3915:                     last;
                   3916:                 }
1.474     raeburn  3917:             }
                   3918:         }
                   3919:         # if they have the evb priv and are currently not playing student
1.482     raeburn  3920:         next if (($no_ownblock) &&
1.491     albertel 3921:                  ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482     raeburn  3922:         next if ($no_userblock);
1.474     raeburn  3923: 
1.490     raeburn  3924:         # Retrieve blocking times and identity of blocker for course
                   3925:         # of specified user, unless user has 'evb' privilege.
1.502     raeburn  3926:         
                   3927:         my ($start,$end)=&get_blocks($setters,$activity,$cdom,$cnum);
                   3928:         if (($start != 0) && 
                   3929:             (($startblock == 0) || ($startblock > $start))) {
                   3930:             $startblock = $start;
                   3931:         }
                   3932:         if (($end != 0)  &&
                   3933:             (($endblock == 0) || ($endblock < $end))) {
                   3934:             $endblock = $end;
                   3935:         }
1.490     raeburn  3936:     }
                   3937:     return ($startblock,$endblock);
                   3938: }
                   3939: 
                   3940: sub get_blocks {
                   3941:     my ($setters,$activity,$cdom,$cnum) = @_;
                   3942:     my $startblock = 0;
                   3943:     my $endblock = 0;
                   3944:     my $course = $cdom.'_'.$cnum;
                   3945:     $setters->{$course} = {};
                   3946:     $setters->{$course}{'staff'} = [];
                   3947:     $setters->{$course}{'times'} = [];
                   3948:     my %records = &Apache::lonnet::dump('comm_block',$cdom,$cnum);
                   3949:     foreach my $record (keys(%records)) {
                   3950:         my ($start,$end) = ($record =~ m/^(\d+)____(\d+)$/);
                   3951:         if ($start <= time && $end >= time) {
                   3952:             my ($staff_name,$staff_dom,$title,$blocks) =
                   3953:                 &parse_block_record($records{$record});
                   3954:             if ($blocks->{$activity} eq 'on') {
                   3955:                 push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
                   3956:                 push(@{$$setters{$course}{'times'}}, [$start,$end]);
1.491     albertel 3957:                 if ( ($startblock == 0) || ($startblock > $start) ) {
                   3958:                     $startblock = $start;
1.490     raeburn  3959:                 }
1.491     albertel 3960:                 if ( ($endblock == 0) || ($endblock < $end) ) {
                   3961:                     $endblock = $end;
1.474     raeburn  3962:                 }
                   3963:             }
                   3964:         }
                   3965:     }
                   3966:     return ($startblock,$endblock);
                   3967: }
                   3968: 
                   3969: sub parse_block_record {
                   3970:     my ($record) = @_;
                   3971:     my ($setuname,$setudom,$title,$blocks);
                   3972:     if (ref($record) eq 'HASH') {
                   3973:         ($setuname,$setudom) = split(/:/,$record->{'setter'});
                   3974:         $title = &unescape($record->{'event'});
                   3975:         $blocks = $record->{'blocks'};
                   3976:     } else {
                   3977:         my @data = split(/:/,$record,3);
                   3978:         if (scalar(@data) eq 2) {
                   3979:             $title = $data[1];
                   3980:             ($setuname,$setudom) = split(/@/,$data[0]);
                   3981:         } else {
                   3982:             ($setuname,$setudom,$title) = @data;
                   3983:         }
                   3984:         $blocks = { 'com' => 'on' };
                   3985:     }
                   3986:     return ($setuname,$setudom,$title,$blocks);
                   3987: }
                   3988: 
                   3989: sub build_block_table {
                   3990:     my ($startblock,$endblock,$setters) = @_;
                   3991:     my %lt = &Apache::lonlocal::texthash(
                   3992:         'cacb' => 'Currently active communication blocks',
                   3993:         'cour' => 'Course',
                   3994:         'dura' => 'Duration',
                   3995:         'blse' => 'Block set by'
                   3996:     );
                   3997:     my $output;
1.476     raeburn  3998:     $output = '<br />'.$lt{'cacb'}.':<br />';
1.474     raeburn  3999:     $output .= &start_data_table();
                   4000:     $output .= '
                   4001: <tr>
                   4002:  <th>'.$lt{'cour'}.'</th>
                   4003:  <th>'.$lt{'dura'}.'</th>
                   4004:  <th>'.$lt{'blse'}.'</th>
                   4005: </tr>
                   4006: ';
                   4007:     foreach my $course (keys(%{$setters})) {
                   4008:         my %courseinfo=&Apache::lonnet::coursedescription($course);
                   4009:         for (my $i=0; $i<@{$$setters{$course}{staff}}; $i++) {
                   4010:             my ($uname,$udom) = @{$$setters{$course}{staff}[$i]};
1.490     raeburn  4011:             my $fullname = &plainname($uname,$udom);
                   4012:             if (defined($env{'user.name'}) && defined($env{'user.domain'})
                   4013:                 && $env{'user.name'} ne 'public' 
                   4014:                 && $env{'user.domain'} ne 'public') {
                   4015:                 $fullname = &aboutmewrapper($fullname,$uname,$udom);
                   4016:             }
1.474     raeburn  4017:             my ($openblock,$closeblock) = @{$$setters{$course}{times}[$i]};
                   4018:             $openblock = &Apache::lonlocal::locallocaltime($openblock);
                   4019:             $closeblock= &Apache::lonlocal::locallocaltime($closeblock);
                   4020:             $output .= &Apache::loncommon::start_data_table_row().
                   4021:                        '<td>'.$courseinfo{'description'}.'</td>'.
                   4022:                        '<td>'.$openblock.' to '.$closeblock.'</td>'.
1.490     raeburn  4023:                        '<td>'.$fullname.'</td>'.
1.474     raeburn  4024:                         &Apache::loncommon::end_data_table_row();
                   4025:         }
                   4026:     }
                   4027:     $output .= &end_data_table();
                   4028: }
                   4029: 
1.490     raeburn  4030: sub blocking_status {
                   4031:     my ($activity,$uname,$udom) = @_;
                   4032:     my %setters;
                   4033:     my ($blocked,$output,$ownitem,$is_course);
                   4034:     my ($startblock,$endblock)=&blockcheck(\%setters,$activity,$uname,$udom);
                   4035:     if ($startblock && $endblock) {
                   4036:         $blocked = 1;
                   4037:         if (wantarray) {
                   4038:             my $category;
                   4039:             if ($activity eq 'boards') {
                   4040:                 $category = 'Discussion posts in this course';
                   4041:             } elsif ($activity eq 'blogs') {
                   4042:                 $category = 'Blogs';
                   4043:             } elsif ($activity eq 'port') {
                   4044:                 if (defined($uname) && defined($udom)) {
                   4045:                     if ($uname eq $env{'user.name'} &&
                   4046:                         $udom eq $env{'user.domain'}) {
                   4047:                         $ownitem = 1;
                   4048:                     }
                   4049:                 }
                   4050:                 $is_course = &Apache::lonnet::is_course($udom,$uname);
                   4051:                 if ($ownitem) { 
                   4052:                     $category = 'Your portfolio files';  
                   4053:                 } elsif ($is_course) {
                   4054:                     my $coursedesc;
                   4055:                     foreach my $course (keys(%setters)) {
                   4056:                         my %courseinfo =
                   4057:                              &Apache::lonnet::coursedescription($course);
                   4058:                         $coursedesc = $courseinfo{'description'};
                   4059:                     }
1.692.4.2  raeburn  4060:                     $category = "Group portfolio files in the course '$coursedesc'";
1.490     raeburn  4061:                 } else {
                   4062:                     $category = 'Portfolio files belonging to ';
                   4063:                     if ($env{'user.name'} eq 'public' && 
                   4064:                         $env{'user.domain'} eq 'public') {
                   4065:                         $category .= &plainname($uname,$udom);
                   4066:                     } else {
                   4067:                         $category .= &aboutmewrapper(&plainname($uname,$udom),$uname,$udom);  
                   4068:                     }
                   4069:                 }
                   4070:             } elsif ($activity eq 'groups') {
                   4071:                 $category = 'Groups in this course';
                   4072:             }
                   4073:             my $showstart = &Apache::lonlocal::locallocaltime($startblock);
                   4074:             my $showend = &Apache::lonlocal::locallocaltime($endblock);
                   4075:             $output = '<br />'.&mt('[_1] will be inaccessible between [_2] and [_3] because communication is being blocked.',$category,$showstart,$showend).'<br />';
                   4076:             if (!($activity eq 'port' && !($ownitem) && !($is_course))) { 
                   4077:                 $output .= &build_block_table($startblock,$endblock,\%setters);
                   4078:             }
                   4079:         }
                   4080:     }
                   4081:     if (wantarray) {
                   4082:         return ($blocked,$output);
                   4083:     } else {
                   4084:         return $blocked;
                   4085:     }
                   4086: }
                   4087: 
1.60      matthew  4088: ###############################################
                   4089: 
1.682     raeburn  4090: sub check_ip_acc {
                   4091:     my ($acc)=@_;
                   4092:     &Apache::lonxml::debug("acc is $acc");
                   4093:     if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
                   4094:         return 1;
                   4095:     }
                   4096:     my $allowed=0;
                   4097:     my $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'};
                   4098: 
                   4099:     my $name;
                   4100:     foreach my $pattern (split(',',$acc)) {
                   4101:         $pattern =~ s/^\s*//;
                   4102:         $pattern =~ s/\s*$//;
                   4103:         if ($pattern =~ /\*$/) {
                   4104:             #35.8.*
                   4105:             $pattern=~s/\*//;
                   4106:             if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
                   4107:         } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
                   4108:             #35.8.3.[34-56]
                   4109:             my $low=$2;
                   4110:             my $high=$3;
                   4111:             $pattern=$1;
                   4112:             if ($ip =~ /^\Q$pattern\E/) {
                   4113:                 my $last=(split(/\./,$ip))[3];
                   4114:                 if ($last <=$high && $last >=$low) { $allowed=1; }
                   4115:             }
                   4116:         } elsif ($pattern =~ /^\*/) {
                   4117:             #*.msu.edu
                   4118:             $pattern=~s/\*//;
                   4119:             if (!defined($name)) {
                   4120:                 use Socket;
                   4121:                 my $netaddr=inet_aton($ip);
                   4122:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   4123:             }
                   4124:             if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
                   4125:         } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
                   4126:             #127.0.0.1
                   4127:             if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
                   4128:         } else {
                   4129:             #some.name.com
                   4130:             if (!defined($name)) {
                   4131:                 use Socket;
                   4132:                 my $netaddr=inet_aton($ip);
                   4133:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   4134:             }
                   4135:             if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
                   4136:         }
                   4137:         if ($allowed) { last; }
                   4138:     }
                   4139:     return $allowed;
                   4140: }
                   4141: 
                   4142: ###############################################
                   4143: 
1.60      matthew  4144: =pod
                   4145: 
1.112     bowersj2 4146: =head1 Domain Template Functions
                   4147: 
                   4148: =over 4
                   4149: 
                   4150: =item * &determinedomain()
1.60      matthew  4151: 
                   4152: Inputs: $domain (usually will be undef)
                   4153: 
1.63      www      4154: Returns: Determines which domain should be used for designs
1.60      matthew  4155: 
                   4156: =cut
1.54      www      4157: 
1.60      matthew  4158: ###############################################
1.63      www      4159: sub determinedomain {
                   4160:     my $domain=shift;
1.531     albertel 4161:     if (! $domain) {
1.60      matthew  4162:         # Determine domain if we have not been given one
1.692.4.18  raeburn  4163:         $domain = &Apache::lonnet::default_login_domain();
1.258     albertel 4164:         if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
                   4165:         if ($env{'request.role.domain'}) { 
                   4166:             $domain=$env{'request.role.domain'}; 
1.60      matthew  4167:         }
                   4168:     }
1.63      www      4169:     return $domain;
                   4170: }
                   4171: ###############################################
1.517     raeburn  4172: 
1.518     albertel 4173: sub devalidate_domconfig_cache {
                   4174:     my ($udom)=@_;
                   4175:     &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
                   4176: }
                   4177: 
                   4178: # ---------------------- Get domain configuration for a domain
                   4179: sub get_domainconf {
                   4180:     my ($udom) = @_;
                   4181:     my $cachetime=1800;
                   4182:     my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
                   4183:     if (defined($cached)) { return %{$result}; }
                   4184: 
                   4185:     my %domconfig = &Apache::lonnet::get_dom('configuration',
                   4186: 					     ['login','rolecolors'],$udom);
1.632     raeburn  4187:     my (%designhash,%legacy);
1.518     albertel 4188:     if (keys(%domconfig) > 0) {
                   4189:         if (ref($domconfig{'login'}) eq 'HASH') {
1.632     raeburn  4190:             if (keys(%{$domconfig{'login'}})) {
                   4191:                 foreach my $key (keys(%{$domconfig{'login'}})) {
1.692.4.2  raeburn  4192:                     if (ref($domconfig{'login'}{$key}) eq 'HASH') {
                   4193:                         foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
                   4194:                             $designhash{$udom.'.login.'.$key.'_'.$img} =
                   4195:                                 $domconfig{'login'}{$key}{$img};
                   4196:                         }
                   4197:                     } else {
                   4198:                         $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
                   4199:                     }
1.632     raeburn  4200:                 }
                   4201:             } else {
                   4202:                 $legacy{'login'} = 1;
1.518     albertel 4203:             }
1.632     raeburn  4204:         } else {
                   4205:             $legacy{'login'} = 1;
1.518     albertel 4206:         }
                   4207:         if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632     raeburn  4208:             if (keys(%{$domconfig{'rolecolors'}})) {
                   4209:                 foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
                   4210:                     if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
                   4211:                         foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
                   4212:                             $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
                   4213:                         }
1.518     albertel 4214:                     }
                   4215:                 }
1.632     raeburn  4216:             } else {
                   4217:                 $legacy{'rolecolors'} = 1;
1.518     albertel 4218:             }
1.632     raeburn  4219:         } else {
                   4220:             $legacy{'rolecolors'} = 1;
1.518     albertel 4221:         }
1.632     raeburn  4222:         if (keys(%legacy) > 0) {
                   4223:             my %legacyhash = &get_legacy_domconf($udom);
                   4224:             foreach my $item (keys(%legacyhash)) {
                   4225:                 if ($item =~ /^\Q$udom\E\.login/) {
                   4226:                     if ($legacy{'login'}) { 
                   4227:                         $designhash{$item} = $legacyhash{$item};
                   4228:                     }
                   4229:                 } else {
                   4230:                     if ($legacy{'rolecolors'}) {
                   4231:                         $designhash{$item} = $legacyhash{$item};
                   4232:                     }
1.518     albertel 4233:                 }
                   4234:             }
                   4235:         }
1.632     raeburn  4236:     } else {
                   4237:         %designhash = &get_legacy_domconf($udom); 
1.518     albertel 4238:     }
                   4239:     &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
                   4240: 				  $cachetime);
                   4241:     return %designhash;
                   4242: }
                   4243: 
1.632     raeburn  4244: sub get_legacy_domconf {
                   4245:     my ($udom) = @_;
                   4246:     my %legacyhash;
                   4247:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
                   4248:     my $designfile =  $designdir.'/'.$udom.'.tab';
                   4249:     if (-e $designfile) {
                   4250:         if ( open (my $fh,"<$designfile") ) {
                   4251:             while (my $line = <$fh>) {
                   4252:                 next if ($line =~ /^\#/);
                   4253:                 chomp($line);
                   4254:                 my ($key,$val)=(split(/\=/,$line));
                   4255:                 if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
                   4256:             }
                   4257:             close($fh);
                   4258:         }
                   4259:     }
                   4260:     if (-e '/home/httpd/html/adm/lonDomLogos/'.$udom.'.gif') {
                   4261:         $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
                   4262:     }
                   4263:     return %legacyhash;
                   4264: }
                   4265: 
1.63      www      4266: =pod
                   4267: 
1.112     bowersj2 4268: =item * &domainlogo()
1.63      www      4269: 
                   4270: Inputs: $domain (usually will be undef)
                   4271: 
                   4272: Returns: A link to a domain logo, if the domain logo exists.
                   4273: If the domain logo does not exist, a description of the domain.
                   4274: 
                   4275: =cut
1.112     bowersj2 4276: 
1.63      www      4277: ###############################################
                   4278: sub domainlogo {
1.517     raeburn  4279:     my $domain = &determinedomain(shift);
1.518     albertel 4280:     my %designhash = &get_domainconf($domain);    
1.517     raeburn  4281:     # See if there is a logo
                   4282:     if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519     raeburn  4283:         my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538     albertel 4284:         if ($imgsrc =~ m{^/(adm|res)/}) {
                   4285: 	    if ($imgsrc =~ m{^/res/}) {
                   4286: 		my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
                   4287: 		&Apache::lonnet::repcopy($local_name);
                   4288: 	    }
                   4289: 	   $imgsrc = &lonhttpdurl($imgsrc);
1.519     raeburn  4290:         } 
                   4291:         return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514     albertel 4292:     } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
                   4293:         return &Apache::lonnet::domain($domain,'description');
1.59      www      4294:     } else {
1.60      matthew  4295:         return '';
1.59      www      4296:     }
                   4297: }
1.63      www      4298: ##############################################
                   4299: 
                   4300: =pod
                   4301: 
1.112     bowersj2 4302: =item * &designparm()
1.63      www      4303: 
                   4304: Inputs: $which parameter; $domain (usually will be undef)
                   4305: 
                   4306: Returns: value of designparamter $which
                   4307: 
                   4308: =cut
1.112     bowersj2 4309: 
1.397     albertel 4310: 
1.400     albertel 4311: ##############################################
1.397     albertel 4312: sub designparm {
                   4313:     my ($which,$domain)=@_;
1.258     albertel 4314:     if ($env{'browser.blackwhite'} eq 'on') {
1.635     raeburn  4315: 	if ($which=~/\.(font|alink|vlink|link|textcol)$/) {
1.110     www      4316: 	    return '#000000';
                   4317: 	}
1.635     raeburn  4318: 	if ($which=~/\.(pgbg|sidebg|bgcol)$/) {
1.110     www      4319: 	    return '#FFFFFF';
                   4320: 	}
                   4321: 	if ($which=~/\.tabbg$/) {
                   4322: 	    return '#CCCCCC';
                   4323: 	}
                   4324:     }
1.397     albertel 4325:     if (exists($env{'environment.color.'.$which})) {
1.258     albertel 4326: 	return $env{'environment.color.'.$which};
1.96      www      4327:     }
1.63      www      4328:     $domain=&determinedomain($domain);
1.518     albertel 4329:     my %domdesign = &get_domainconf($domain);
1.520     raeburn  4330:     my $output;
1.517     raeburn  4331:     if ($domdesign{$domain.'.'.$which} ne '') {
1.520     raeburn  4332: 	$output = $domdesign{$domain.'.'.$which};
1.63      www      4333:     } else {
1.520     raeburn  4334:         $output = $defaultdesign{$which};
                   4335:     }
                   4336:     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635     raeburn  4337:         ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538     albertel 4338:         if ($output =~ m{^/(adm|res)/}) {
                   4339: 	    if ($output =~ m{^/res/}) {
                   4340: 		my $local_name = &Apache::lonnet::filelocation('',$output);
                   4341: 		&Apache::lonnet::repcopy($local_name);
                   4342: 	    }
1.520     raeburn  4343:             $output = &lonhttpdurl($output);
                   4344:         }
1.63      www      4345:     }
1.520     raeburn  4346:     return $output;
1.63      www      4347: }
1.59      www      4348: 
1.60      matthew  4349: ###############################################
                   4350: ###############################################
                   4351: 
                   4352: =pod
                   4353: 
1.112     bowersj2 4354: =back
                   4355: 
1.549     albertel 4356: =head1 HTML Helpers
1.112     bowersj2 4357: 
                   4358: =over 4
                   4359: 
                   4360: =item * &bodytag()
1.60      matthew  4361: 
                   4362: Returns a uniform header for LON-CAPA web pages.
                   4363: 
                   4364: Inputs: 
                   4365: 
1.112     bowersj2 4366: =over 4
                   4367: 
                   4368: =item * $title, A title to be displayed on the page.
                   4369: 
                   4370: =item * $function, the current role (can be undef).
                   4371: 
                   4372: =item * $addentries, extra parameters for the <body> tag.
                   4373: 
                   4374: =item * $bodyonly, if defined, only return the <body> tag.
                   4375: 
                   4376: =item * $domain, if defined, force a given domain.
                   4377: 
                   4378: =item * $forcereg, if page should register as content page (relevant for 
1.86      www      4379:             text interface only)
1.60      matthew  4380: 
1.326     albertel 4381: =item * $customtitle, alternate text to use instead of $title
                   4382:                       in the title box that appears, this text
                   4383:                       is not auto translated like the $title is
1.309     albertel 4384: 
                   4385: =item * $notopbar, if true, keep the 'what is this' info but remove the
                   4386:                    navigational links
1.317     albertel 4387: 
1.338     albertel 4388: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
                   4389: 
                   4390: =item * $notitle, if true keep the nav controls, but remove the title bar
                   4391: 
1.361     albertel 4392: =item * $no_inline_link, if true and in remote mode, don't show the 
                   4393:          'Switch To Inline Menu' link
                   4394: 
1.460     albertel 4395: =item * $args, optional argument valid values are
                   4396:             no_auto_mt_title -> prevents &mt()ing the title arg
1.562     albertel 4397:             inherit_jsmath -> when creating popup window in a page,
                   4398:                               should it have jsmath forced on by the
                   4399:                               current page
1.460     albertel 4400: 
1.112     bowersj2 4401: =back
                   4402: 
1.60      matthew  4403: Returns: A uniform header for LON-CAPA web pages.  
                   4404: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
                   4405: If $bodyonly is undef or zero, an html string containing a <body> tag and 
                   4406: other decorations will be returned.
                   4407: 
                   4408: =cut
                   4409: 
1.54      www      4410: sub bodytag {
1.309     albertel 4411:     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,$customtitle,
1.460     albertel 4412: 	$notopbar,$bgcolor,$notitle,$no_inline_link,$args)=@_;
1.339     albertel 4413: 
1.460     albertel 4414:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.339     albertel 4415: 
1.183     matthew  4416:     $function = &get_users_function() if (!$function);
1.339     albertel 4417:     my $img =    &designparm($function.'.img',$domain);
                   4418:     my $font =   &designparm($function.'.font',$domain);
                   4419:     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
                   4420: 
1.692.4.2  raeburn  4421:     my %design = ( 'style'   => 'margin-top: 0',
1.535     albertel 4422: 		   'bgcolor' => $pgbg,
1.339     albertel 4423: 		   'text'    => $font,
                   4424:                    'alink'   => &designparm($function.'.alink',$domain),
                   4425: 		   'vlink'   => &designparm($function.'.vlink',$domain),
                   4426: 		   'link'    => &designparm($function.'.link',$domain),);
1.438     albertel 4427:     @design{keys(%$addentries)} = @$addentries{keys(%$addentries)}; 
1.339     albertel 4428: 
1.63      www      4429:  # role and realm
1.378     raeburn  4430:     my ($role,$realm) = split(/\./,$env{'request.role'},2);
                   4431:     if ($role  eq 'ca') {
1.479     albertel 4432:         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500     albertel 4433:         $realm = &plainname($rname,$rdom);
1.378     raeburn  4434:     } 
1.55      www      4435: # realm
1.258     albertel 4436:     if ($env{'request.course.id'}) {
1.378     raeburn  4437:         if ($env{'request.role'} !~ /^cr/) {
                   4438:             $role = &Apache::lonnet::plaintext($role,&course_type());
                   4439:         }
1.359     albertel 4440: 	$realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378     raeburn  4441:     } else {
                   4442:         $role = &Apache::lonnet::plaintext($role);
1.54      www      4443:     }
1.433     albertel 4444: 
1.359     albertel 4445:     if (!$realm) { $realm='&nbsp;'; }
1.55      www      4446: # Set messages
1.60      matthew  4447:     my $messages=&domainlogo($domain);
1.330     albertel 4448: 
1.438     albertel 4449:     my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329     albertel 4450: 
1.101     www      4451: # construct main body tag
1.359     albertel 4452:     my $bodytag = "<body $extra_body_attr>".
1.562     albertel 4453: 	&Apache::lontexconvert::init_math_support($args->{'inherit_jsmath'});
1.252     albertel 4454: 
1.530     albertel 4455:     if ($bodyonly) {
1.60      matthew  4456:         return $bodytag;
1.258     albertel 4457:     } elsif ($env{'browser.interface'} eq 'textual') {
1.95      www      4458: # Accessibility
1.224     raeburn  4459:           
1.337     albertel 4460: 	$bodytag.=&Apache::lonmenu::menubuttons($forcereg,$forcereg);
1.338     albertel 4461: 	if (!$notitle) {
1.337     albertel 4462: 	    $bodytag.='<h1>LON-CAPA: '.$title.'</h1>';
                   4463: 	}
                   4464: 	return $bodytag;
1.359     albertel 4465:     }
                   4466: 
1.410     albertel 4467:     my $name = &plainname($env{'user.name'},$env{'user.domain'});
1.433     albertel 4468:     if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
                   4469: 	undef($role);
1.434     albertel 4470:     } else {
                   4471: 	$name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'});
1.433     albertel 4472:     }
1.359     albertel 4473:     
                   4474:     my $roleinfo=(<<ENDROLE);
                   4475: <td class="LC_title_bar_who">
                   4476: <div class="LC_title_bar_name">
1.410     albertel 4477:     $name
1.361     albertel 4478:     &nbsp;
1.359     albertel 4479: </div>
                   4480: <div class="LC_title_bar_role">
1.361     albertel 4481: $role&nbsp;
1.359     albertel 4482: </div>
                   4483: <div class="LC_title_bar_realm">
1.361     albertel 4484: $realm&nbsp;
1.359     albertel 4485: </div>
1.206     albertel 4486: </td>
                   4487: ENDROLE
1.235     raeburn  4488: 
1.359     albertel 4489:     my $titleinfo = '<span class="LC_title_bar_title">'.$title.'</span>';
                   4490:     if ($customtitle) {
                   4491:         $titleinfo = $customtitle;
                   4492:     }
                   4493:     #
                   4494:     # Extra info if you are the DC
                   4495:     my $dc_info = '';
                   4496:     if ($env{'user.adv'} && exists($env{'user.role.dc./'.
                   4497:                         $env{'course.'.$env{'request.course.id'}.
                   4498:                                  '.domain'}.'/'})) {
                   4499:         my $cid = $env{'request.course.id'};
                   4500:         $dc_info.= $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380     www      4501:         $dc_info =~ s/\s+$//;
1.359     albertel 4502:         $dc_info = '('.$dc_info.')';
                   4503:     }
                   4504: 
1.644     www      4505:     if (($env{'environment.remote'} eq 'off') || ($args->{'suppress_header_logos'})) {
1.359     albertel 4506:         # No Remote
1.258     albertel 4507: 	if ($env{'request.state'} eq 'construct') {
1.359     albertel 4508: 	    $forcereg=1;
                   4509: 	}
                   4510: 
                   4511: 	if (!$customtitle && $env{'request.state'} eq 'construct') {
                   4512: 	    # this is for resources; directories have customtitle, and crumbs
                   4513:             # and select recent are created in lonpubdir.pm  
1.229     albertel 4514: 	    my ($uname,$thisdisfn)=
1.258     albertel 4515: 		($env{'request.filename'} =~ m|^/home/([^/]+)/public_html/(.*)|);
1.229     albertel 4516: 	    my $formaction='/priv/'.$uname.'/'.$thisdisfn;
                   4517: 	    $formaction=~s/\/+/\//g;
                   4518: 
1.359     albertel 4519: 	    my $parentpath = '';
                   4520: 	    my $lastitem = '';
                   4521: 	    if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
                   4522: 		$parentpath = $1;
                   4523: 		$lastitem = $2;
                   4524: 	    } else {
                   4525: 		$lastitem = $thisdisfn;
                   4526: 	    }
                   4527: 	    $titleinfo = 
1.640     bisitz   4528: 		&Apache::loncommon::help_open_menu('','',3,'Authoring')
                   4529: 		.'<b>'.&mt('Construction Space').'</b>:&nbsp;'
                   4530: 		.'<form name="dirs" method="post" action="'.$formaction
1.359     albertel 4531: 		.'" target="_top"><tt><b>'
                   4532: 		.&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."<font size=\"+1\">$lastitem</font></b></tt><br />"
                   4533: 		.&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
                   4534: 		.'</form>'
                   4535: 		.&Apache::lonmenu::constspaceform();
1.235     raeburn  4536:         }
1.359     albertel 4537: 
1.337     albertel 4538:         my $titletable;
1.338     albertel 4539: 	if (!$notitle) {
1.337     albertel 4540: 	    $titletable =
1.359     albertel 4541: 		'<table id="LC_title_bar">'.
                   4542:                          "<tr><td> $titleinfo $dc_info</td>".$roleinfo.
                   4543: 			 '</tr></table>';
1.337     albertel 4544: 	}
1.359     albertel 4545: 	if ($notopbar) {
                   4546: 	    $bodytag .= $titletable;
                   4547: 	} else {
                   4548: 	    if ($env{'request.state'} eq 'construct') {
1.337     albertel 4549:                 $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg,
                   4550: 							  $titletable);
1.272     raeburn  4551:             } else {
1.336     albertel 4552:                 $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg).
1.359     albertel 4553: 		    $titletable;
1.272     raeburn  4554:             }
1.235     raeburn  4555:         }
                   4556:         return $bodytag;
1.94      www      4557:     }
1.95      www      4558: 
1.93      www      4559: #
1.95      www      4560: # Top frame rendering, Remote is up
1.93      www      4561: #
1.359     albertel 4562: 
1.517     raeburn  4563:     my $imgsrc = $img;
                   4564:     if ($img =~ /^\/adm/) {
1.575     albertel 4565:         $imgsrc = &lonhttpdurl($img);
1.517     raeburn  4566:     }
                   4567:     my $upperleft='<img src="'.$imgsrc.'" alt="'.$function.'" />';
1.359     albertel 4568: 
1.305     www      4569:     # Explicit link to get inline menu
1.361     albertel 4570:     my $menu= ($no_inline_link?''
                   4571: 	       :'<br /><a href="/adm/remote?action=collapse">'.&mt('Switch to Inline Menu Mode').'</a>');
1.245     matthew  4572:     #
1.338     albertel 4573:     if ($notitle) {
1.337     albertel 4574: 	return $bodytag;
                   4575:     }
1.94      www      4576:     return(<<ENDBODY);
1.60      matthew  4577: $bodytag
1.359     albertel 4578: <table id="LC_title_bar" class="LC_with_remote">
1.368     albertel 4579: <tr><td class="LC_title_bar_role_logo">$upperleft</td>
1.359     albertel 4580:     <td class="LC_title_bar_domain_logo">$messages&nbsp;</td>
1.54      www      4581: </tr>
1.359     albertel 4582: <tr><td>$titleinfo $dc_info $menu</td>
                   4583: $roleinfo
1.368     albertel 4584: </tr>
1.356     albertel 4585: </table>
1.54      www      4586: ENDBODY
1.182     matthew  4587: }
                   4588: 
1.330     albertel 4589: sub make_attr_string {
                   4590:     my ($register,$attr_ref) = @_;
                   4591: 
                   4592:     if ($attr_ref && !ref($attr_ref)) {
                   4593: 	die("addentries Must be a hash ref ".
                   4594: 	    join(':',caller(1))." ".
                   4595: 	    join(':',caller(0))." ");
                   4596:     }
                   4597: 
                   4598:     if ($register) {
1.339     albertel 4599: 	my ($on_load,$on_unload);
                   4600: 	foreach my $key (keys(%{$attr_ref})) {
                   4601: 	    if      (lc($key) eq 'onload') {
                   4602: 		$on_load.=$attr_ref->{$key}.';';
                   4603: 		delete($attr_ref->{$key});
                   4604: 
                   4605: 	    } elsif (lc($key) eq 'onunload') {
                   4606: 		$on_unload.=$attr_ref->{$key}.';';
                   4607: 		delete($attr_ref->{$key});
                   4608: 	    }
                   4609: 	}
                   4610: 	$attr_ref->{'onload'}  =
                   4611: 	    &Apache::lonmenu::loadevents().  $on_load;
                   4612: 	$attr_ref->{'onunload'}=
                   4613: 	    &Apache::lonmenu::unloadevents().$on_unload;
                   4614:     }
                   4615: 
                   4616: # Accessibility font enhance
                   4617:     if ($env{'browser.fontenhance'} eq 'on') {
                   4618: 	my $style;
                   4619: 	foreach my $key (keys(%{$attr_ref})) {
                   4620: 	    if (lc($key) eq 'style') {
                   4621: 		$style.=$attr_ref->{$key}.';';
                   4622: 		delete($attr_ref->{$key});
                   4623: 	    }
                   4624: 	}
                   4625: 	$attr_ref->{'style'}=$style.'; font-size: x-large;';
1.330     albertel 4626:     }
1.339     albertel 4627: 
                   4628:     if ($env{'browser.blackwhite'} eq 'on') {
                   4629: 	delete($attr_ref->{'font'});
                   4630: 	delete($attr_ref->{'link'});
                   4631: 	delete($attr_ref->{'alink'});
                   4632: 	delete($attr_ref->{'vlink'});
                   4633: 	delete($attr_ref->{'bgcolor'});
                   4634: 	delete($attr_ref->{'background'});
                   4635:     }
                   4636: 
1.330     albertel 4637:     my $attr_string;
                   4638:     foreach my $attr (keys(%$attr_ref)) {
                   4639: 	$attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
                   4640:     }
                   4641:     return $attr_string;
                   4642: }
                   4643: 
                   4644: 
1.182     matthew  4645: ###############################################
1.251     albertel 4646: ###############################################
                   4647: 
                   4648: =pod
                   4649: 
                   4650: =item * &endbodytag()
                   4651: 
                   4652: Returns a uniform footer for LON-CAPA web pages.
                   4653: 
1.635     raeburn  4654: Inputs: 1 - optional reference to an args hash
                   4655: If in the hash, key for noredirectlink has a value which evaluates to true,
                   4656: a 'Continue' link is not displayed if the page contains an
                   4657: internal redirect in the <head></head> section,
                   4658: i.e., $env{'internal.head.redirect'} exists   
1.251     albertel 4659: 
                   4660: =cut
                   4661: 
                   4662: sub endbodytag {
1.635     raeburn  4663:     my ($args) = @_;
1.251     albertel 4664:     my $endbodytag='</body>';
1.269     albertel 4665:     $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
1.315     albertel 4666:     if ( exists( $env{'internal.head.redirect'} ) ) {
1.635     raeburn  4667:         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
                   4668: 	    $endbodytag=
                   4669: 	        "<br /><a href=\"$env{'internal.head.redirect'}\">".
                   4670: 	        &mt('Continue').'</a>'.
                   4671: 	        $endbodytag;
                   4672:         }
1.315     albertel 4673:     }
1.251     albertel 4674:     return $endbodytag;
                   4675: }
                   4676: 
1.352     albertel 4677: =pod
                   4678: 
                   4679: =item * &standard_css()
                   4680: 
                   4681: Returns a style sheet
                   4682: 
                   4683: Inputs: (all optional)
                   4684:             domain         -> force to color decorate a page for a specific
                   4685:                                domain
                   4686:             function       -> force usage of a specific rolish color scheme
                   4687:             bgcolor        -> override the default page bgcolor
                   4688: 
                   4689: =cut
                   4690: 
1.343     albertel 4691: sub standard_css {
1.345     albertel 4692:     my ($function,$domain,$bgcolor) = @_;
1.352     albertel 4693:     $function  = &get_users_function() if (!$function);
                   4694:     my $img    = &designparm($function.'.img',   $domain);
                   4695:     my $tabbg  = &designparm($function.'.tabbg', $domain);
                   4696:     my $font   = &designparm($function.'.font',  $domain);
1.345     albertel 4697:     my $sidebg = &designparm($function.'.sidebg',$domain);
1.382     albertel 4698:     my $pgbg_or_bgcolor =
                   4699: 	         $bgcolor ||
1.352     albertel 4700: 	         &designparm($function.'.pgbg',  $domain);
1.382     albertel 4701:     my $pgbg   = &designparm($function.'.pgbg',  $domain);
1.352     albertel 4702:     my $alink  = &designparm($function.'.alink', $domain);
                   4703:     my $vlink  = &designparm($function.'.vlink', $domain);
                   4704:     my $link   = &designparm($function.'.link',  $domain);
                   4705: 
1.602     albertel 4706:     my $sans                 = 'Verdana,Arial,Helvetica,sans-serif';
1.395     albertel 4707:     my $mono                 = 'monospace';
1.692.4.13  raeburn  4708:     my $data_table_head      = $tabbg;
1.692.4.6  raeburn  4709:     my $data_table_light     = '#FAFAFA';
                   4710:     my $data_table_dark      = '#F0F0F0';
1.470     banghart 4711:     my $data_table_darker    = '#CCCCCC';
1.349     albertel 4712:     my $data_table_highlight = '#FFFF00';
1.352     albertel 4713:     my $mail_new             = '#FFBB77';
                   4714:     my $mail_new_hover       = '#DD9955';
                   4715:     my $mail_read            = '#BBBB77';
                   4716:     my $mail_read_hover      = '#999944';
                   4717:     my $mail_replied         = '#AAAA88';
                   4718:     my $mail_replied_hover   = '#888855';
                   4719:     my $mail_other           = '#99BBBB';
                   4720:     my $mail_other_hover     = '#669999';
1.391     albertel 4721:     my $table_header         = '#DDDDDD';
1.489     raeburn  4722:     my $feedback_link_bg     = '#BBBBBB';
1.692.4.3  raeburn  4723:     my $lg_border_color      = '#C8C8C8';
1.392     albertel 4724: 
1.608     albertel 4725:     my $border = ($env{'browser.type'} eq 'explorer' ||
1.692.4.2  raeburn  4726: 		  $env{'browser.type'} eq 'safari'     ) ? '0 2px 0 2px'
                   4727: 	                                                 : '0 3px 0 4px';
1.448     albertel 4728: 
1.523     albertel 4729: 
1.343     albertel 4730:     return <<END;
1.345     albertel 4731: h1, h2, h3, th { font-family: $sans }
1.343     albertel 4732: a:focus { color: red; background: yellow } 
1.692.4.6  raeburn  4733: 
                   4734: hr {
                   4735:   clear: both;
                   4736:   color: $tabbg;
                   4737:   background-color: $tabbg;
                   4738:   height: 3px;
                   4739:   border: none;
                   4740: }
                   4741: 
1.510     albertel 4742: table.thinborder,
1.523     albertel 4743: 
1.510     albertel 4744: table.thinborder tr th {
                   4745:   border-style: solid;
                   4746:   border-width: 1px;
                   4747:   background: $tabbg;
                   4748: }
1.523     albertel 4749: table.thinborder tr td {
1.510     albertel 4750:   border-style: solid;
                   4751:   border-width: 1px
                   4752: }
1.426     albertel 4753: 
1.343     albertel 4754: form, .inline { display: inline; }
                   4755: .center { text-align: center; }
1.593     albertel 4756: .LC_filename {font-family: $mono; white-space:pre;}
1.350     albertel 4757: .LC_error {
                   4758:   color: red;
                   4759:   font-size: larger;
                   4760: }
1.457     albertel 4761: .LC_warning,
                   4762: .LC_diff_removed {
1.394     albertel 4763:   color: red;
                   4764: }
1.532     albertel 4765: 
                   4766: .LC_info,
1.457     albertel 4767: .LC_success,
                   4768: .LC_diff_added {
1.350     albertel 4769:   color: green;
                   4770: }
1.692.4.2  raeburn  4771: 
                   4772: div.LC_confirm_box {
                   4773:   background-color: #FAFAFA;
                   4774:   border: 1px solid $lg_border_color;
                   4775:   margin-right: 0;
                   4776:   padding: 5px;
                   4777: }
                   4778: 
                   4779: div.LC_confirm_box .LC_error img,
                   4780: div.LC_confirm_box .LC_success img {
                   4781:   vertical-align: middle;
1.543     albertel 4782: }
                   4783: 
1.440     albertel 4784: .LC_icon {
1.692.4.2  raeburn  4785:   border: none;
1.440     albertel 4786: }
1.539     albertel 4787: .LC_indexer_icon {
1.692.4.2  raeburn  4788:   border: 0;
1.539     albertel 4789:   height: 22px;
                   4790: }
1.543     albertel 4791: .LC_docs_spacer {
                   4792:   width: 25px;
                   4793:   height: 1px;
1.692.4.2  raeburn  4794:   border: none;
1.543     albertel 4795: }
1.346     albertel 4796: 
1.532     albertel 4797: .LC_internal_info {
1.692.4.2  raeburn  4798:   color: #999999;
1.532     albertel 4799: }
                   4800: 
1.692.4.19  raeburn  4801: .LC_discussion {
                   4802:    background: $tabbg;
                   4803:    border: 1px solid black;
                   4804:    margin: 2px;
                   4805: }
                   4806: 
                   4807: .LC_disc_action_links_bar {
                   4808:    background: $tabbg;
                   4809:    border: none;
                   4810:    margin: 4px;
                   4811: }
                   4812: 
                   4813: .LC_disc_action_left {
                   4814:    text-align: left;
                   4815: }
                   4816: 
                   4817: .LC_disc_action_right {
                   4818:    text-align: right;
                   4819: }
                   4820: 
                   4821: .LC_disc_new_item {
                   4822:    background: white;
                   4823:    border: 2px solid red;
                   4824:    margin: 2px;
                   4825: }
                   4826: 
                   4827: .LC_disc_old_item {
                   4828:    background: white;
                   4829:    border: 1px solid black;
                   4830:    margin: 2px;
                   4831: }
                   4832: 
1.458     albertel 4833: table.LC_pastsubmission {
                   4834:   border: 1px solid black;
                   4835:   margin: 2px;
                   4836: }
                   4837: 
1.606     albertel 4838: table#LC_top_nav, table#LC_menubuttons,table#LC_nav_location {
1.345     albertel 4839:   width: 100%;
                   4840:   background: $pgbg;
1.392     albertel 4841:   border: 2px;
1.402     albertel 4842:   border-collapse: separate;
1.692.4.2  raeburn  4843:   padding: 0;
1.345     albertel 4844: }
1.392     albertel 4845: 
1.606     albertel 4846: table#LC_title_bar, table.LC_breadcrumbs, 
1.393     albertel 4847: table#LC_title_bar.LC_with_remote {
1.359     albertel 4848:   width: 100%;
1.392     albertel 4849:   border-color: $pgbg;
                   4850:   border-style: solid;
                   4851:   border-width: $border;
                   4852: 
1.379     albertel 4853:   background: $pgbg;
                   4854:   font-family: $sans;
1.392     albertel 4855:   border-collapse: collapse;
1.692.4.2  raeburn  4856:   padding: 0;
1.359     albertel 4857: }
1.392     albertel 4858: 
1.409     albertel 4859: table.LC_docs_path {
                   4860:   width: 100%;
                   4861:   border: 0;
                   4862:   background: $pgbg;
                   4863:   font-family: $sans;
                   4864:   border-collapse: collapse;
1.692.4.2  raeburn  4865:   padding: 0;
1.409     albertel 4866: }
                   4867: 
1.359     albertel 4868: table#LC_title_bar td {
                   4869:   background: $tabbg;
                   4870: }
                   4871: table#LC_title_bar td.LC_title_bar_who {
                   4872:   background: $tabbg;
                   4873:   color: $font;
1.427     albertel 4874:   font: small $sans;
1.359     albertel 4875:   text-align: right;
                   4876: }
1.469     banghart 4877: span.LC_metadata {
                   4878:     font-family: $sans;
                   4879: }
1.359     albertel 4880: span.LC_title_bar_title {
1.416     albertel 4881:   font: bold x-large $sans;
1.359     albertel 4882: }
                   4883: table#LC_title_bar td.LC_title_bar_domain_logo {
                   4884:   background: $sidebg;
                   4885:   text-align: right;
1.692.4.2  raeburn  4886:   padding: 0;
1.368     albertel 4887: }
                   4888: table#LC_title_bar td.LC_title_bar_role_logo {
                   4889:   background: $sidebg;
1.692.4.2  raeburn  4890:   padding: 0;
1.359     albertel 4891: }
                   4892: 
1.346     albertel 4893: table#LC_menubuttons_mainmenu {
1.526     www      4894:   width: 100%;
1.692.4.2  raeburn  4895:   border: 0;
1.346     albertel 4896:   border-spacing: 1px;
1.692.4.2  raeburn  4897:   padding: 0 1px;
                   4898:   margin: 0;
1.346     albertel 4899:   border-collapse: separate;
                   4900: }
                   4901: table#LC_menubuttons img, table#LC_menubuttons_mainmenu img {
1.692.4.2  raeburn  4902:   border: none;
1.346     albertel 4903: }
1.345     albertel 4904: table#LC_top_nav td {
                   4905:   background: $tabbg;
1.692.4.2  raeburn  4906:   border: none;
1.407     albertel 4907:   font-size: small;
1.345     albertel 4908: }
                   4909: table#LC_top_nav td a, div#LC_top_nav a {
                   4910:   color: $font;
                   4911:   font-family: $sans;
                   4912: }
1.364     albertel 4913: table#LC_top_nav td.LC_top_nav_logo {
                   4914:   background: $tabbg;
1.432     albertel 4915:   text-align: left;
1.408     albertel 4916:   white-space: nowrap;
1.432     albertel 4917:   width: 31px;
1.408     albertel 4918: }
                   4919: table#LC_top_nav td.LC_top_nav_logo img {
1.692.4.2  raeburn  4920:   border: none;
1.408     albertel 4921:   vertical-align: bottom;
1.364     albertel 4922: }
1.432     albertel 4923: table#LC_top_nav td.LC_top_nav_exit,
                   4924: table#LC_top_nav td.LC_top_nav_help {
                   4925:   width: 2.0em;
                   4926: }
1.442     albertel 4927: table#LC_top_nav td.LC_top_nav_login {
                   4928:   width: 4.0em;
                   4929:   text-align: center;
                   4930: }
1.409     albertel 4931: table.LC_breadcrumbs td, table.LC_docs_path td  {
1.357     albertel 4932:   background: $tabbg;
                   4933:   color: $font;
                   4934:   font-family: $sans;
1.358     albertel 4935:   font-size: smaller;
1.357     albertel 4936: }
1.411     albertel 4937: table.LC_breadcrumbs td.LC_breadcrumbs_component,
1.409     albertel 4938: table.LC_docs_path td.LC_docs_path_component {
1.357     albertel 4939:   background: $tabbg;
                   4940:   color: $font;
                   4941:   font-family: $sans;
                   4942:   font-size: larger;
                   4943:   text-align: right;
                   4944: }
1.383     albertel 4945: td.LC_table_cell_checkbox {
                   4946:   text-align: center;
                   4947: }
1.522     albertel 4948: table#LC_mainmenu td.LC_mainmenu_column {
                   4949:     vertical-align: top;
                   4950: }
                   4951: 
1.346     albertel 4952: .LC_menubuttons_inline_text {
                   4953:   color: $font;
                   4954:   font-family: $sans;
                   4955:   font-size: smaller;
                   4956: }
                   4957: 
1.526     www      4958: .LC_menubuttons_link {
                   4959:   text-decoration: none;
                   4960: }
1.692.4.2  raeburn  4961: /*2008--9-5: new menu style sheet.Changed category*/
1.522     albertel 4962: .LC_menubuttons_category {
1.521     www      4963:   color: $font;
1.526     www      4964:   background: $pgbg;
1.521     www      4965:   font-family: $sans;
                   4966:   font-size: larger;
                   4967:   font-weight: bold;
                   4968: }
                   4969: 
1.346     albertel 4970: td.LC_menubuttons_text {
1.526     www      4971:   width: 90%;
1.346     albertel 4972:   color: $font;
                   4973:   font-family: $sans;
                   4974: }
1.526     www      4975: 
1.346     albertel 4976: td.LC_menubuttons_img {
                   4977: }
1.526     www      4978: 
1.346     albertel 4979: .LC_current_location {
                   4980:   font-family: $sans;
                   4981:   background: $tabbg;
                   4982: }
                   4983: .LC_new_mail {
                   4984:   font-family: $sans;
1.634     www      4985:   background: $tabbg;
1.346     albertel 4986:   font-weight: bold;
                   4987: }
1.347     albertel 4988: 
1.527     www      4989: .LC_dropadd_labeltext {
                   4990:   font-family: $sans;
                   4991:   text-align: right;
                   4992: }
                   4993: 
                   4994: .LC_preferences_labeltext {
                   4995:   font-family: $sans;
                   4996:   text-align: right;
                   4997: }
                   4998: 
1.666     raeburn  4999: .LC_roleslog_note {
                   5000:   font-size: smaller;
                   5001: }
                   5002: 
1.692.4.2  raeburn  5003: .LC_mail_functions {
                   5004:     font-weight: bold;
                   5005: }
                   5006: 
1.440     albertel 5007: table.LC_aboutme_port {
1.692.4.2  raeburn  5008:   border: none;
1.440     albertel 5009:   border-collapse: collapse;
1.692.4.2  raeburn  5010:   border-spacing: 0;
1.440     albertel 5011: }
1.349     albertel 5012: table.LC_data_table, table.LC_mail_list {
1.347     albertel 5013:   border: 1px solid #000000;
1.402     albertel 5014:   border-collapse: separate;
1.426     albertel 5015:   border-spacing: 1px;
1.610     albertel 5016:   background: $pgbg;
1.347     albertel 5017: }
1.422     albertel 5018: .LC_data_table_dense {
                   5019:   font-size: small;
                   5020: }
1.507     raeburn  5021: table.LC_nested_outer {
                   5022:   border: 1px solid #000000;
1.589     raeburn  5023:   border-collapse: collapse;
1.692.4.2  raeburn  5024:   border-spacing: 0;
1.507     raeburn  5025:   width: 100%;
                   5026: }
1.692.4.11  raeburn  5027: table.LC_innerpickbox,
1.507     raeburn  5028: table.LC_nested {
1.692.4.2  raeburn  5029:   border: none;
1.589     raeburn  5030:   border-collapse: collapse;
1.692.4.2  raeburn  5031:   border-spacing: 0;
1.507     raeburn  5032:   width: 100%;
                   5033: }
1.523     albertel 5034: table.LC_data_table tr th, table.LC_calendar tr th, table.LC_mail_list tr th,
1.692.4.11  raeburn  5035: table.LC_prior_tries tr th,
                   5036: table.LC_innerpickbox tr th {
1.349     albertel 5037:   font-weight: bold;
                   5038:   background-color: $data_table_head;
1.421     albertel 5039:   font-size: smaller;
1.347     albertel 5040: }
1.692.4.11  raeburn  5041: table.LC_innerpickbox tr th,
                   5042: table.LC_innerpickbox tr td {
                   5043:   vertical-align: top;
                   5044: }
1.692.4.2  raeburn  5045: table.LC_data_table tr.LC_info_row > td {
                   5046:   background-color: #CCCCCC;
                   5047:   font-weight: bold;
                   5048:   text-align: left;
                   5049: }
1.610     albertel 5050: table.LC_data_table tr.LC_odd_row > td, 
1.692.4.2  raeburn  5051: table.LC_pick_box tr > td.LC_odd_row,
1.440     albertel 5052: table.LC_aboutme_port tr td {
1.349     albertel 5053:   background-color: $data_table_light;
1.425     albertel 5054:   padding: 2px;
1.347     albertel 5055: }
1.610     albertel 5056: table.LC_data_table tr.LC_even_row > td,
1.692.4.2  raeburn  5057: table.LC_pick_box tr > td.LC_even_row,
1.440     albertel 5058: table.LC_aboutme_port tr.LC_even_row td {
1.349     albertel 5059:   background-color: $data_table_dark;
1.692.4.2  raeburn  5060:   padding: 2px;
1.347     albertel 5061: }
1.425     albertel 5062: table.LC_data_table tr.LC_data_table_highlight td {
                   5063:   background-color: $data_table_darker;
                   5064: }
1.639     raeburn  5065: table.LC_data_table tr td.LC_leftcol_header {
                   5066:   background-color: $data_table_head;
                   5067:   font-weight: bold;
                   5068: }
1.451     albertel 5069: table.LC_data_table tr.LC_empty_row td,
1.507     raeburn  5070: table.LC_nested tr.LC_empty_row td {
1.347     albertel 5071:   background-color: #FFFFFF;
1.421     albertel 5072:   font-weight: bold;
                   5073:   font-style: italic;
                   5074:   text-align: center;
                   5075:   padding: 8px;
1.347     albertel 5076: }
1.507     raeburn  5077: table.LC_nested tr.LC_empty_row td {
1.465     albertel 5078:   padding: 4ex
                   5079: }
1.507     raeburn  5080: table.LC_nested_outer tr th {
                   5081:   font-weight: bold;
                   5082:   background-color: $data_table_head;
                   5083:   font-size: smaller;
                   5084:   border-bottom: 1px solid #000000;
                   5085: }
                   5086: table.LC_nested_outer tr td.LC_subheader {
                   5087:   background-color: $data_table_head;
                   5088:   font-weight: bold;
                   5089:   font-size: small;
                   5090:   border-bottom: 1px solid #000000;
                   5091:   text-align: right;
1.451     albertel 5092: }
1.507     raeburn  5093: table.LC_nested tr.LC_info_row td {
1.692.4.2  raeburn  5094:   background-color: #CCCCCC;
1.451     albertel 5095:   font-weight: bold;
                   5096:   font-size: small;
1.507     raeburn  5097:   text-align: center;
                   5098: }
1.589     raeburn  5099: table.LC_nested tr.LC_info_row td.LC_left_item,
                   5100: table.LC_nested_outer tr th.LC_left_item {
1.507     raeburn  5101:   text-align: left;
1.451     albertel 5102: }
1.507     raeburn  5103: table.LC_nested td {
1.692.4.2  raeburn  5104:   background-color: #FFFFFF;
1.451     albertel 5105:   font-size: small;
1.507     raeburn  5106: }
                   5107: table.LC_nested_outer tr th.LC_right_item,
                   5108: table.LC_nested tr.LC_info_row td.LC_right_item,
                   5109: table.LC_nested tr.LC_odd_row td.LC_right_item,
                   5110: table.LC_nested tr td.LC_right_item {
1.451     albertel 5111:   text-align: right;
                   5112: }
                   5113: 
1.507     raeburn  5114: table.LC_nested tr.LC_odd_row td {
1.692.4.2  raeburn  5115:   background-color: #EEEEEE;
1.451     albertel 5116: }
                   5117: 
1.473     raeburn  5118: table.LC_createuser {
                   5119: }
                   5120: 
                   5121: table.LC_createuser tr.LC_section_row td {
                   5122:   font-size: smaller;
                   5123: }
                   5124: 
                   5125: table.LC_createuser tr.LC_info_row td  {
1.692.4.2  raeburn  5126:   background-color: #CCCCCC;
1.473     raeburn  5127:   font-weight: bold;
                   5128:   text-align: center;
                   5129: }
                   5130: 
1.349     albertel 5131: table.LC_calendar {
                   5132:   border: 1px solid #000000;
                   5133:   border-collapse: collapse;
                   5134: }
                   5135: table.LC_calendar_pickdate {
                   5136:   font-size: xx-small;
                   5137: }
                   5138: table.LC_calendar tr td {
                   5139:   border: 1px solid #000000;
                   5140:   vertical-align: top;
                   5141: }
                   5142: table.LC_calendar tr td.LC_calendar_day_empty {
                   5143:   background-color: $data_table_dark;
                   5144: }
                   5145: table.LC_calendar tr td.LC_calendar_day_current {
                   5146:   background-color: $data_table_highlight;
                   5147: }
                   5148: 
                   5149: table.LC_mail_list tr.LC_mail_new {
                   5150:   background-color: $mail_new;
                   5151: }
                   5152: table.LC_mail_list tr.LC_mail_new:hover {
                   5153:   background-color: $mail_new_hover;
                   5154: }
                   5155: table.LC_mail_list tr.LC_mail_read {
                   5156:   background-color: $mail_read;
                   5157: }
                   5158: table.LC_mail_list tr.LC_mail_read:hover {
                   5159:   background-color: $mail_read_hover;
                   5160: }
                   5161: table.LC_mail_list tr.LC_mail_replied {
                   5162:   background-color: $mail_replied;
                   5163: }
                   5164: table.LC_mail_list tr.LC_mail_replied:hover {
                   5165:   background-color: $mail_replied_hover;
                   5166: }
                   5167: table.LC_mail_list tr.LC_mail_other {
                   5168:   background-color: $mail_other;
                   5169: }
                   5170: table.LC_mail_list tr.LC_mail_other:hover {
                   5171:   background-color: $mail_other_hover;
                   5172: }
1.494     raeburn  5173: table.LC_mail_list tr.LC_mail_even {
                   5174: }
                   5175: table.LC_mail_list tr.LC_mail_odd {
                   5176: }
                   5177: 
1.385     albertel 5178: 
1.386     albertel 5179: table#LC_portfolio_actions {
                   5180:   width: auto;
                   5181:   background: $pgbg;
1.692.4.2  raeburn  5182:   border: none;
1.386     albertel 5183:   border-spacing: 2px 2px;
1.692.4.2  raeburn  5184:   padding: 0;
                   5185:   margin: 0;
1.386     albertel 5186:   border-collapse: separate;
                   5187: }
                   5188: table#LC_portfolio_actions td.LC_label {
                   5189:   background: $tabbg;
                   5190:   text-align: right;
                   5191: }
                   5192: table#LC_portfolio_actions td.LC_value {
                   5193:   background: $tabbg;
                   5194: }
1.385     albertel 5195: 
1.391     albertel 5196: table#LC_cstr_controls {
                   5197:   width: 100%;
                   5198:   border-collapse: collapse;
                   5199: }
                   5200: table#LC_cstr_controls tr td {
                   5201:   border: 4px solid $pgbg;
                   5202:   padding: 4px;
                   5203:   text-align: center;
                   5204:   background: $tabbg;
                   5205: }
                   5206: table#LC_cstr_controls tr th {
                   5207:   border: 4px solid $pgbg;
                   5208:   background: $table_header;
                   5209:   text-align: center;
                   5210:   font-family: $sans;
                   5211:   font-size: smaller;
                   5212: }
                   5213: 
1.389     albertel 5214: table#LC_browser {
                   5215:  
                   5216: }
                   5217: table#LC_browser tr th {
1.391     albertel 5218:   background: $table_header;
1.389     albertel 5219: }
1.390     albertel 5220: table#LC_browser tr td {
                   5221:   padding: 2px;
                   5222: }
1.389     albertel 5223: table#LC_browser tr.LC_browser_file,
                   5224: table#LC_browser tr.LC_browser_file_published {
                   5225:   background: #CCFF88;
                   5226: }
                   5227: table#LC_browser tr.LC_browser_file_locked,
                   5228: table#LC_browser tr.LC_browser_file_unpublished {
                   5229:   background: #FFAA99;
1.387     albertel 5230: }
1.389     albertel 5231: table#LC_browser tr.LC_browser_file_obsolete {
                   5232:   background: #AAAAAA;
1.387     albertel 5233: }
1.455     albertel 5234: table#LC_browser tr.LC_browser_file_modified,
                   5235: table#LC_browser tr.LC_browser_file_metamodified {
1.389     albertel 5236:   background: #FFFF77;
1.387     albertel 5237: }
1.389     albertel 5238: table#LC_browser tr.LC_browser_folder {
                   5239:   background: #CCCCFF;
1.387     albertel 5240: }
1.692.4.2  raeburn  5241: 
                   5242: table.LC_data_table tr > td.LC_roles_is {
                   5243: /*  background: #77FF77; */
                   5244: }
                   5245: table.LC_data_table tr > td.LC_roles_future {
                   5246:   background: #FFFF77;
                   5247: }
                   5248: table.LC_data_table tr > td.LC_roles_will {
                   5249:   background: #FFAA77;
                   5250: }
                   5251: table.LC_data_table tr > td.LC_roles_expired {
                   5252:   background: #FF7777;
                   5253: }
                   5254: table.LC_data_table tr > td.LC_roles_will_not {
                   5255:   background: #AAFF77;
                   5256: }
                   5257: table.LC_data_table tr > td.LC_roles_selected {
                   5258:   background: #11CC55;
                   5259: }
                   5260: 
1.388     albertel 5261: span.LC_current_location {
                   5262:   font-size: x-large;
                   5263:   background: $pgbg;
                   5264: }
1.387     albertel 5265: 
1.395     albertel 5266: span.LC_parm_menu_item {
                   5267:   font-size: larger;
                   5268:   font-family: $sans;
                   5269: }
                   5270: span.LC_parm_scope_all {
                   5271:   color: red;
                   5272: }
                   5273: span.LC_parm_scope_folder {
                   5274:   color: green;
                   5275: }
                   5276: span.LC_parm_scope_resource {
                   5277:   color: orange;
                   5278: }
                   5279: span.LC_parm_part {
                   5280:   color: blue;
                   5281: }
                   5282: span.LC_parm_folder, span.LC_parm_symb {
                   5283:   font-size: x-small;
                   5284:   font-family: $mono;
                   5285:   color: #AAAAAA;
                   5286: }
                   5287: 
1.396     albertel 5288: td.LC_parm_overview_level_menu, td.LC_parm_overview_map_menu,
                   5289: td.LC_parm_overview_parm_selectors, td.LC_parm_overview_parm_restrictions {
                   5290:   border: 1px solid black;
                   5291:   border-collapse: collapse;
                   5292: }
                   5293: table.LC_parm_overview_restrictions td {
                   5294:   border-width: 1px 4px 1px 4px;
                   5295:   border-style: solid;
                   5296:   border-color: $pgbg;
                   5297:   text-align: center;
                   5298: }
                   5299: table.LC_parm_overview_restrictions th {
                   5300:   background: $tabbg;
                   5301:   border-width: 1px 4px 1px 4px;
                   5302:   border-style: solid;
                   5303:   border-color: $pgbg;
                   5304: }
1.398     albertel 5305: table#LC_helpmenu {
1.692.4.2  raeburn  5306:   border: none;
1.398     albertel 5307:   height: 55px;
1.692.4.2  raeburn  5308:   border-spacing: 0;
1.398     albertel 5309: }
                   5310: 
                   5311: table#LC_helpmenu fieldset legend {
                   5312:   font-size: larger;
                   5313:   font-weight: bold;
                   5314: }
1.397     albertel 5315: table#LC_helpmenu_links {
                   5316:   width: 100%;
                   5317:   border: 1px solid black;
                   5318:   background: $pgbg;
1.692.4.2  raeburn  5319:   padding: 0;
1.397     albertel 5320:   border-spacing: 1px;
                   5321: }
                   5322: table#LC_helpmenu_links tr td {
                   5323:   padding: 1px;
                   5324:   background: $tabbg;
1.399     albertel 5325:   text-align: center;
                   5326:   font-weight: bold;
1.397     albertel 5327: }
1.396     albertel 5328: 
1.397     albertel 5329: table#LC_helpmenu_links a:link, table#LC_helpmenu_links a:visited,
                   5330: table#LC_helpmenu_links a:active {
                   5331:   text-decoration: none;
                   5332:   color: $font;
                   5333: }
                   5334: table#LC_helpmenu_links a:hover {
                   5335:   text-decoration: underline;
                   5336:   color: $vlink;
                   5337: }
1.396     albertel 5338: 
1.417     albertel 5339: .LC_chrt_popup_exists {
                   5340:   border: 1px solid #339933;
                   5341:   margin: -1px;
                   5342: }
                   5343: .LC_chrt_popup_up {
                   5344:   border: 1px solid yellow;
                   5345:   margin: -1px;
                   5346: }
                   5347: .LC_chrt_popup {
                   5348:   border: 1px solid #8888FF;
                   5349:   background: #CCCCFF;
                   5350: }
1.421     albertel 5351: table.LC_pick_box {
                   5352:   border-collapse: separate;
                   5353:   background: white;
                   5354:   border: 1px solid black;
                   5355:   border-spacing: 1px;
                   5356: }
                   5357: table.LC_pick_box td.LC_pick_box_title {
1.692.4.16  raeburn  5358:   background: $tabbg;
1.421     albertel 5359:   font-weight: bold;
                   5360:   text-align: right;
1.692.4.2  raeburn  5361:   vertical-align: top;
1.421     albertel 5362:   width: 184px;
                   5363:   padding: 8px;
                   5364: }
1.645     raeburn  5365: table.LC_pick_box td.LC_selfenroll_pick_box_title {
1.692.4.16  raeburn  5366:   background: $tabbg;
1.645     raeburn  5367:   font-weight: bold;
                   5368:   text-align: right;
                   5369:   width: 350px;
                   5370:   padding: 8px;
                   5371: }
                   5372: 
1.579     raeburn  5373: table.LC_pick_box td.LC_pick_box_value {
                   5374:   text-align: left;
                   5375:   padding: 8px;
                   5376: }
                   5377: table.LC_pick_box td.LC_pick_box_select {
                   5378:   text-align: left;
                   5379:   padding: 8px;
                   5380: }
1.424     albertel 5381: table.LC_pick_box td.LC_pick_box_separator {
1.692.4.2  raeburn  5382:   padding: 0;
1.421     albertel 5383:   height: 1px;
                   5384:   background: black;
                   5385: }
                   5386: table.LC_pick_box td.LC_pick_box_submit {
                   5387:   text-align: right;
                   5388: }
1.579     raeburn  5389: table.LC_pick_box td.LC_evenrow_value {
                   5390:   text-align: left;
                   5391:   padding: 8px;
                   5392:   background-color: $data_table_light;
                   5393: }
                   5394: table.LC_pick_box td.LC_oddrow_value {
                   5395:   text-align: left;
                   5396:   padding: 8px;
                   5397:   background-color: $data_table_light;
                   5398: }
                   5399: table.LC_helpform_receipt {
                   5400:   width: 620px;
                   5401:   border-collapse: separate;
                   5402:   background: white;
                   5403:   border: 1px solid black;
                   5404:   border-spacing: 1px;
                   5405: }
                   5406: table.LC_helpform_receipt td.LC_pick_box_title {
                   5407:   background: $tabbg;
                   5408:   font-weight: bold;
                   5409:   text-align: right;
                   5410:   width: 184px;
                   5411:   padding: 8px;
                   5412: }
                   5413: table.LC_helpform_receipt td.LC_evenrow_value {
                   5414:   text-align: left;
                   5415:   padding: 8px;
                   5416:   background-color: $data_table_light;
                   5417: }
                   5418: table.LC_helpform_receipt td.LC_oddrow_value {
                   5419:   text-align: left;
                   5420:   padding: 8px;
                   5421:   background-color: $data_table_light;
                   5422: }
                   5423: table.LC_helpform_receipt td.LC_pick_box_separator {
1.692.4.2  raeburn  5424:   padding: 0;
1.579     raeburn  5425:   height: 1px;
                   5426:   background: black;
                   5427: }
                   5428: span.LC_helpform_receipt_cat {
                   5429:   font-weight: bold;
                   5430: }
1.424     albertel 5431: table.LC_group_priv_box {
                   5432:   background: white;
                   5433:   border: 1px solid black;
                   5434:   border-spacing: 1px;
                   5435: }
                   5436: table.LC_group_priv_box td.LC_pick_box_title {
                   5437:   background: $tabbg;
                   5438:   font-weight: bold;
                   5439:   text-align: right;
                   5440:   width: 184px;
                   5441: }
                   5442: table.LC_group_priv_box td.LC_groups_fixed {
                   5443:   background: $data_table_light;
                   5444:   text-align: center;
                   5445: }
                   5446: table.LC_group_priv_box td.LC_groups_optional {
                   5447:   background: $data_table_dark;
                   5448:   text-align: center;
                   5449: }
                   5450: table.LC_group_priv_box td.LC_groups_functionality {
                   5451:   background: $data_table_darker;
                   5452:   text-align: center;
                   5453:   font-weight: bold;
                   5454: }
                   5455: table.LC_group_priv td {
                   5456:   text-align: left;
1.692.4.2  raeburn  5457:   padding: 0;
1.424     albertel 5458: }
                   5459: 
1.421     albertel 5460: table.LC_notify_front_page {
                   5461:   background: white;
                   5462:   border: 1px solid black;
                   5463:   padding: 8px;
                   5464: }
                   5465: table.LC_notify_front_page td {
                   5466:   padding: 8px;
                   5467: }
1.424     albertel 5468: .LC_navbuttons {
                   5469:   margin: 2ex 0ex 2ex 0ex;
                   5470: }
1.423     albertel 5471: .LC_topic_bar {
                   5472:   font-family: $sans;
                   5473:   font-weight: bold;
                   5474:   width: 100%;
                   5475:   background: $tabbg;
                   5476:   vertical-align: middle;
                   5477:   margin: 2ex 0ex 2ex 0ex;
1.692.4.2  raeburn  5478:   padding: 3px;
1.423     albertel 5479: }
                   5480: .LC_topic_bar span {
                   5481:   vertical-align: middle;
                   5482: }
                   5483: .LC_topic_bar img {
                   5484:   vertical-align: bottom;
                   5485: }
                   5486: table.LC_course_group_status {
                   5487:   margin: 20px;
                   5488: }
                   5489: table.LC_status_selector td {
                   5490:   vertical-align: top;
                   5491:   text-align: center;
1.424     albertel 5492:   padding: 4px;
                   5493: }
                   5494: table.LC_descriptive_input td.LC_description {
                   5495:   vertical-align: top;
                   5496:   text-align: right;
                   5497:   font-weight: bold;
1.423     albertel 5498: }
1.599     albertel 5499: div.LC_feedback_link {
1.616     albertel 5500:   clear: both;
1.599     albertel 5501:   background: white;
                   5502:   width: 100%;  
1.489     raeburn  5503: }
                   5504: span.LC_feedback_link {
1.599     albertel 5505:   background: $feedback_link_bg;
                   5506:   font-size: larger;
                   5507: }
                   5508: span.LC_message_link {
                   5509:   background: $feedback_link_bg;
                   5510:   font-size: larger;
                   5511:   position: absolute;
                   5512:   right: 1em;
1.489     raeburn  5513: }
1.421     albertel 5514: 
1.515     albertel 5515: table.LC_prior_tries {
1.524     albertel 5516:   border: 1px solid #000000;
                   5517:   border-collapse: separate;
                   5518:   border-spacing: 1px;
1.515     albertel 5519: }
1.523     albertel 5520: 
1.515     albertel 5521: table.LC_prior_tries td {
1.524     albertel 5522:   padding: 2px;
1.515     albertel 5523: }
1.523     albertel 5524: 
                   5525: .LC_answer_correct {
                   5526:   background: #AAFFAA;
                   5527:   color: black;
                   5528: }
                   5529: .LC_answer_charged_try {
                   5530:   background: #FFAAAA ! important;
                   5531:   color: black;
                   5532: }
                   5533: .LC_answer_not_charged_try, 
                   5534: .LC_answer_no_grade,
                   5535: .LC_answer_late {
                   5536:   background: #FFFFAA;
                   5537:   color: black;
                   5538: }
                   5539: .LC_answer_previous {
                   5540:   background: #AAAAFF;
                   5541:   color: black;
                   5542: }
                   5543: .LC_answer_no_message {
                   5544:   background: #FFFFFF;
                   5545:   color: black;
                   5546: }
                   5547: .LC_answer_unknown {
                   5548:   background: orange;
                   5549:   color: black;
                   5550: }
                   5551: 
                   5552: 
1.529     albertel 5553: span.LC_prior_numerical,
                   5554: span.LC_prior_string,
                   5555: span.LC_prior_custom,
                   5556: span.LC_prior_reaction,
                   5557: span.LC_prior_math {
1.523     albertel 5558:   font-family: monospace;
                   5559:   white-space: pre;
                   5560: }
                   5561: 
1.525     albertel 5562: span.LC_prior_string {
                   5563:   font-family: monospace;
                   5564:   white-space: pre;
                   5565: }
                   5566: 
1.523     albertel 5567: table.LC_prior_option {
                   5568:   width: 100%;
                   5569:   border-collapse: collapse;
                   5570: }
1.528     albertel 5571: table.LC_prior_rank, table.LC_prior_match {
                   5572:   border-collapse: collapse;
                   5573: }
                   5574: table.LC_prior_option tr td,
                   5575: table.LC_prior_rank tr td,
                   5576: table.LC_prior_match tr td {
1.524     albertel 5577:   border: 1px solid #000000;
1.515     albertel 5578: }
                   5579: 
1.519     raeburn  5580: span.LC_nobreak {
1.544     albertel 5581:   white-space: nowrap;
1.519     raeburn  5582: }
                   5583: 
1.576     raeburn  5584: span.LC_cusr_emph {
                   5585:   font-style: italic;
                   5586: }
                   5587: 
1.633     raeburn  5588: span.LC_cusr_subheading {
                   5589:   font-weight: normal;
                   5590:   font-size: 85%;
                   5591: }
                   5592: 
1.545     albertel 5593: table.LC_docs_documents {
                   5594:   background: #BBBBBB;
1.692.4.2  raeburn  5595:   border-width: 0;
1.545     albertel 5596:   border-collapse: collapse;
                   5597: }
                   5598: 
                   5599: table.LC_docs_documents td.LC_docs_document {
                   5600:   border: 2px solid black;
                   5601:   padding: 4px;
                   5602: }
                   5603: 
                   5604: .LC_docs_course_commands div {
                   5605:   float: left;
                   5606:   border: 4px solid #AAAAAA;
                   5607:   padding: 4px;
                   5608:   background: #DDDDCC;
                   5609: }
                   5610: 
                   5611: .LC_docs_entry_move {
1.692.4.2  raeburn  5612:   border: none;
1.545     albertel 5613:   border-collapse: collapse;
1.544     albertel 5614: }
                   5615: 
1.545     albertel 5616: .LC_docs_entry_move td {
                   5617:   border: 2px solid #BBBBBB;
                   5618:   background: #DDDDDD;
                   5619: }
                   5620: 
                   5621: .LC_docs_editor td.LC_docs_entry_commands {
                   5622:   background: #DDDDDD;
                   5623:   font-size: x-small;
                   5624: }
1.544     albertel 5625: .LC_docs_copy {
1.545     albertel 5626:   color: #000099;
1.544     albertel 5627: }
                   5628: .LC_docs_cut {
1.545     albertel 5629:   color: #550044;
1.544     albertel 5630: }
                   5631: .LC_docs_rename {
1.545     albertel 5632:   color: #009900;
1.544     albertel 5633: }
                   5634: .LC_docs_remove {
1.545     albertel 5635:   color: #990000;
                   5636: }
                   5637: 
1.547     albertel 5638: .LC_docs_reinit_warn,
                   5639: .LC_docs_ext_edit {
                   5640:   font-size: x-small;
                   5641: }
                   5642: 
1.545     albertel 5643: .LC_docs_editor td.LC_docs_entry_title,
                   5644: .LC_docs_editor td.LC_docs_entry_icon {
                   5645:   background: #FFFFBB;
                   5646: }
                   5647: .LC_docs_editor td.LC_docs_entry_parameter {
                   5648:   background: #BBBBFF;
                   5649:   font-size: x-small;
                   5650:   white-space: nowrap;
                   5651: }
                   5652: 
                   5653: table.LC_docs_adddocs td,
                   5654: table.LC_docs_adddocs th {
                   5655:   border: 1px solid #BBBBBB;
                   5656:   padding: 4px;
                   5657:   background: #DDDDDD;
1.543     albertel 5658: }
                   5659: 
1.584     albertel 5660: table.LC_sty_begin {
                   5661:   background: #BBFFBB;
                   5662: }
                   5663: table.LC_sty_end {
                   5664:   background: #FFBBBB;
                   5665: }
                   5666: 
1.589     raeburn  5667: table.LC_double_column {
1.692.4.2  raeburn  5668:   border-width: 0;
1.589     raeburn  5669:   border-collapse: collapse;
                   5670:   width: 100%;
                   5671:   padding: 2px;
                   5672: }
                   5673: 
                   5674: table.LC_double_column tr td.LC_left_col {
1.590     raeburn  5675:   top: 2px;
1.589     raeburn  5676:   left: 2px;
                   5677:   width: 47%;
                   5678:   vertical-align: top;
                   5679: }
                   5680: 
                   5681: table.LC_double_column tr td.LC_right_col {
                   5682:   top: 2px;
                   5683:   right: 2px; 
                   5684:   width: 47%;
                   5685:   vertical-align: top;
                   5686: }
                   5687: 
1.594     raeburn  5688: span.LC_role_level {
                   5689:   font-weight: bold;
                   5690: }
                   5691: 
1.591     raeburn  5692: div.LC_left_float {
                   5693:   float: left;
                   5694:   padding-right: 5%;
1.597     albertel 5695:   padding-bottom: 4px;
1.591     raeburn  5696: }
                   5697: 
                   5698: div.LC_clear_float_header {
1.597     albertel 5699:   padding-bottom: 2px;
1.591     raeburn  5700: }
                   5701: 
                   5702: div.LC_clear_float_footer {
1.597     albertel 5703:   padding-top: 10px;
1.591     raeburn  5704:   clear: both;
                   5705: }
                   5706: 
1.597     albertel 5707: 
1.601     albertel 5708: div.LC_grade_select_mode {
1.604     albertel 5709:   font-family: $sans;
1.601     albertel 5710: }
                   5711: div.LC_grade_select_mode div div {
                   5712:   margin: 5px;
                   5713: }
                   5714: div.LC_grade_select_mode_selector {
                   5715:   margin: 5px;
                   5716:   float: left;
                   5717: }
                   5718: div.LC_grade_select_mode_selector_header {
                   5719:   font: bold medium $sans;
                   5720: }
                   5721: div.LC_grade_select_mode_type {
                   5722:   clear: left;
                   5723: }
                   5724: 
1.597     albertel 5725: div.LC_grade_show_user {
                   5726:   margin-top: 20px;
                   5727:   border: 1px solid black;
                   5728: }
                   5729: div.LC_grade_user_name {
                   5730:   background: #DDDDEE;
                   5731:   border-bottom: 1px solid black;
                   5732:   font: bold large $sans;
                   5733: }
                   5734: div.LC_grade_show_user_odd_row div.LC_grade_user_name {
                   5735:   background: #DDEEDD;
                   5736: }
                   5737: 
                   5738: div.LC_grade_show_problem,
                   5739: div.LC_grade_submissions,
                   5740: div.LC_grade_message_center,
                   5741: div.LC_grade_info_links,
                   5742: div.LC_grade_assign {
                   5743:   margin: 5px;
                   5744:   width: 99%;
                   5745:   background: #FFFFFF;
                   5746: }
                   5747: div.LC_grade_show_problem_header,
                   5748: div.LC_grade_submissions_header,
                   5749: div.LC_grade_message_center_header,
                   5750: div.LC_grade_assign_header {
                   5751:   font: bold large $sans;
                   5752: }
                   5753: div.LC_grade_show_problem_problem,
                   5754: div.LC_grade_submissions_body,
                   5755: div.LC_grade_message_center_body,
                   5756: div.LC_grade_assign_body {
                   5757:   border: 1px solid black;
                   5758:   width: 99%;
                   5759:   background: #FFFFFF;
                   5760: }
1.598     albertel 5761: span.LC_grade_check_note {
                   5762:   font: normal medium $sans;
                   5763:   display: inline;
                   5764:   position: absolute;
                   5765:   right: 1em;
                   5766: }
1.597     albertel 5767: 
1.613     albertel 5768: table.LC_scantron_action {
                   5769:   width: 100%;
                   5770: }
                   5771: table.LC_scantron_action tr th {
                   5772:   font: normal bold $sans;
                   5773: }
1.600     albertel 5774: 
1.614     albertel 5775: div.LC_edit_problem_header, 
                   5776: div.LC_edit_problem_footer {
1.600     albertel 5777:   font: normal medium $sans;
1.602     albertel 5778:   margin: 2px;
1.600     albertel 5779: }
                   5780: div.LC_edit_problem_header,
1.602     albertel 5781: div.LC_edit_problem_header div,
1.614     albertel 5782: div.LC_edit_problem_footer,
                   5783: div.LC_edit_problem_footer div,
1.602     albertel 5784: div.LC_edit_problem_editxml_header,
                   5785: div.LC_edit_problem_editxml_header div {
1.600     albertel 5786:   margin-top: 5px;
                   5787: }
1.602     albertel 5788: div.LC_edit_problem_header_edit_row {
                   5789:   background: $tabbg;
                   5790:   padding: 3px;
                   5791:   margin-bottom: 5px;
                   5792: }
1.600     albertel 5793: div.LC_edit_problem_header_title {
1.602     albertel 5794:   font: larger bold $sans;
                   5795:   background: $tabbg;
                   5796:   padding: 3px;
                   5797: }
                   5798: table.LC_edit_problem_header_title {
                   5799:   font: larger bold $sans;
                   5800:   width: 100%;
                   5801:   border-color: $pgbg;
                   5802:   border-style: solid;
                   5803:   border-width: $border;
                   5804: 
1.600     albertel 5805:   background: $tabbg;
1.602     albertel 5806:   border-collapse: collapse;
1.692.4.2  raeburn  5807:   padding: 0;
1.602     albertel 5808: }
                   5809: 
                   5810: div.LC_edit_problem_discards {
                   5811:   float: left;
                   5812:   padding-bottom: 5px;
                   5813: }
                   5814: div.LC_edit_problem_saves {
                   5815:   float: right;
                   5816:   padding-bottom: 5px;
1.600     albertel 5817: }
                   5818: hr.LC_edit_problem_divide {
1.602     albertel 5819:   clear: both;
1.600     albertel 5820:   color: $tabbg;
                   5821:   background-color: $tabbg;
                   5822:   height: 3px;
1.692.4.2  raeburn  5823:   border: none;
1.600     albertel 5824: }
1.679     riegler  5825: img.stift{
1.678     riegler  5826:   border-width:0;
1.679     riegler  5827:   vertical-align:middle;
1.677     riegler  5828: }
1.680     riegler  5829: 
1.681     riegler  5830: table#LC_mainmenu{
                   5831:  margin-top:10px;
                   5832:  width:80%;
                   5833: 
                   5834: }
                   5835: 
1.680     riegler  5836: table#LC_mainmenu td.LC_mainmenu_col_fieldset{
                   5837:   vertical-align: top;
                   5838:   width: 45%;
                   5839: }
                   5840: .LC_mainmenu_fieldset_category {
                   5841:   color: $font;
                   5842:   background: $pgbg;
                   5843:   font-family: $sans;
                   5844:   font-size: small;
                   5845:   font-weight: bold;
                   5846: }
                   5847: fieldset#LC_mainmenu_fieldset {
1.692.4.2  raeburn  5848:   margin:0 10px 10px 0;
                   5849: 
                   5850: }
1.680     riegler  5851: 
1.692.4.2  raeburn  5852: div.LC_createcourse {
                   5853:     margin: 10px 10px 10px 10px;
1.680     riegler  5854: }
1.692.4.2  raeburn  5855: 
1.343     albertel 5856: END
                   5857: }
                   5858: 
1.306     albertel 5859: =pod
                   5860: 
                   5861: =item * &headtag()
                   5862: 
                   5863: Returns a uniform footer for LON-CAPA web pages.
                   5864: 
1.307     albertel 5865: Inputs: $title - optional title for the head
                   5866:         $head_extra - optional extra HTML to put inside the <head>
1.315     albertel 5867:         $args - optional arguments
1.319     albertel 5868:             force_register - if is true call registerurl so the remote is 
                   5869:                              informed
1.415     albertel 5870:             redirect       -> array ref of
                   5871:                                    1- seconds before redirect occurs
                   5872:                                    2- url to redirect to
                   5873:                                    3- whether the side effect should occur
1.315     albertel 5874:                            (side effect of setting 
                   5875:                                $env{'internal.head.redirect'} to the url 
                   5876:                                redirected too)
1.352     albertel 5877:             domain         -> force to color decorate a page for a specific
                   5878:                                domain
                   5879:             function       -> force usage of a specific rolish color scheme
                   5880:             bgcolor        -> override the default page bgcolor
1.460     albertel 5881:             no_auto_mt_title
                   5882:                            -> prevent &mt()ing the title arg
1.464     albertel 5883: 
1.306     albertel 5884: =cut
                   5885: 
                   5886: sub headtag {
1.313     albertel 5887:     my ($title,$head_extra,$args) = @_;
1.306     albertel 5888:     
1.363     albertel 5889:     my $function = $args->{'function'} || &get_users_function();
                   5890:     my $domain   = $args->{'domain'}   || &determinedomain();
                   5891:     my $bgcolor  = $args->{'bgcolor'}  || &designparm($function.'.pgbg',$domain);
1.418     albertel 5892:     my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458     albertel 5893: 		   $Apache::lonnet::perlvar{'lonVersion'},
1.531     albertel 5894: 		   #time(),
1.418     albertel 5895: 		   $env{'environment.color.timestamp'},
1.363     albertel 5896: 		   $function,$domain,$bgcolor);
                   5897: 
1.369     www      5898:     $url = '/adm/css/'.&escape($url).'.css';
1.363     albertel 5899: 
1.308     albertel 5900:     my $result =
                   5901: 	'<head>'.
1.461     albertel 5902: 	&font_settings();
1.319     albertel 5903: 
1.461     albertel 5904:     if (!$args->{'frameset'}) {
                   5905: 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
                   5906:     }
1.319     albertel 5907:     if ($args->{'force_register'}) {
                   5908: 	$result .= &Apache::lonmenu::registerurl(1);
                   5909:     }
1.436     albertel 5910:     if (!$args->{'no_nav_bar'} 
                   5911: 	&& !$args->{'only_body'}
                   5912: 	&& !$args->{'frameset'}) {
                   5913: 	$result .= &help_menu_js();
                   5914:     }
1.319     albertel 5915: 
1.314     albertel 5916:     if (ref($args->{'redirect'})) {
1.414     albertel 5917: 	my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315     albertel 5918: 	$url = &Apache::lonenc::check_encrypt($url);
1.414     albertel 5919: 	if (!$inhibit_continue) {
                   5920: 	    $env{'internal.head.redirect'} = $url;
                   5921: 	}
1.313     albertel 5922: 	$result.=<<ADDMETA
                   5923: <meta http-equiv="pragma" content="no-cache" />
1.344     albertel 5924: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313     albertel 5925: ADDMETA
                   5926:     }
1.306     albertel 5927:     if (!defined($title)) {
                   5928: 	$title = 'The LearningOnline Network with CAPA';
                   5929:     }
1.460     albertel 5930:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
                   5931:     $result .= '<title> LON-CAPA '.$title.'</title>'
1.414     albertel 5932: 	.'<link rel="stylesheet" type="text/css" href="'.$url.'" />'
                   5933: 	.$head_extra;
1.306     albertel 5934:     return $result;
                   5935: }
                   5936: 
                   5937: =pod
                   5938: 
1.340     albertel 5939: =item * &font_settings()
                   5940: 
                   5941: Returns neccessary <meta> to set the proper encoding
                   5942: 
                   5943: Inputs: none
                   5944: 
                   5945: =cut
                   5946: 
                   5947: sub font_settings {
                   5948:     my $headerstring='';
1.647     www      5949:     if (!$env{'browser.mathml'} && $env{'browser.unicode'}) {
1.340     albertel 5950: 	$headerstring.=
                   5951: 	    '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
                   5952:     }
                   5953:     return $headerstring;
                   5954: }
                   5955: 
1.341     albertel 5956: =pod
                   5957: 
                   5958: =item * &xml_begin()
                   5959: 
                   5960: Returns the needed doctype and <html>
                   5961: 
                   5962: Inputs: none
                   5963: 
                   5964: =cut
                   5965: 
                   5966: sub xml_begin {
                   5967:     my $output='';
                   5968: 
1.592     albertel 5969:     if ($env{'internal.start_page'}==1) {
                   5970: 	&Apache::lonhtmlcommon::init_htmlareafields();
                   5971:     }
1.342     albertel 5972: 
1.341     albertel 5973:     if ($env{'browser.mathml'}) {
                   5974: 	$output='<?xml version="1.0"?>'
                   5975:             #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
                   5976: #            .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
                   5977:             
                   5978: #	    .'<!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">] >'
                   5979: 	    .'<!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">'
                   5980:             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" ' 
                   5981: 	    .'xmlns="http://www.w3.org/1999/xhtml">';
                   5982:     } else {
1.692.4.6  raeburn  5983: 	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'.
                   5984:             '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1.341     albertel 5985:     }
                   5986:     return $output;
                   5987: }
1.340     albertel 5988: 
                   5989: =pod
                   5990: 
1.306     albertel 5991: =item * &endheadtag()
                   5992: 
                   5993: Returns a uniform </head> for LON-CAPA web pages.
                   5994: 
                   5995: Inputs: none
                   5996: 
                   5997: =cut
                   5998: 
                   5999: sub endheadtag {
                   6000:     return '</head>';
                   6001: }
                   6002: 
                   6003: =pod
                   6004: 
                   6005: =item * &head()
                   6006: 
                   6007: Returns a uniform complete <head>..</head> section for LON-CAPA web pages.
                   6008: 
1.648     raeburn  6009: Inputs:
                   6010: 
                   6011: =over 4
                   6012: 
                   6013: $title - optional title for the page
                   6014: 
                   6015: $head_extra - optional extra HTML to put inside the <head>
                   6016: 
                   6017: =back
1.405     albertel 6018: 
1.306     albertel 6019: =cut
                   6020: 
                   6021: sub head {
1.325     albertel 6022:     my ($title,$head_extra,$args) = @_;
                   6023:     return &headtag($title,$head_extra,$args).&endheadtag();
1.306     albertel 6024: }
                   6025: 
                   6026: =pod
                   6027: 
                   6028: =item * &start_page()
                   6029: 
                   6030: Returns a complete <html> .. <body> section for LON-CAPA web pages.
                   6031: 
1.648     raeburn  6032: Inputs:
                   6033: 
                   6034: =over 4
                   6035: 
                   6036: $title - optional title for the page
                   6037: 
                   6038: $head_extra - optional extra HTML to incude inside the <head>
                   6039: 
                   6040: $args - additional optional args supported are:
                   6041: 
                   6042: =over 8
                   6043: 
                   6044:              only_body      -> is true will set &bodytag() onlybodytag
1.317     albertel 6045:                                     arg on
1.648     raeburn  6046:              no_nav_bar     -> is true will set &bodytag() notopbar arg on
                   6047:              add_entries    -> additional attributes to add to the  <body>
                   6048:              domain         -> force to color decorate a page for a 
1.317     albertel 6049:                                     specific domain
1.648     raeburn  6050:              function       -> force usage of a specific rolish color
1.317     albertel 6051:                                     scheme
1.648     raeburn  6052:              redirect       -> see &headtag()
                   6053:              bgcolor        -> override the default page bg color
                   6054:              js_ready       -> return a string ready for being used in 
1.317     albertel 6055:                                     a javascript writeln
1.648     raeburn  6056:              html_encode    -> return a string ready for being used in 
1.320     albertel 6057:                                     a html attribute
1.648     raeburn  6058:              force_register -> if is true will turn on the &bodytag()
1.317     albertel 6059:                                     $forcereg arg
1.648     raeburn  6060:              body_title     -> alternate text to use instead of $title
1.326     albertel 6061:                                     in the title box that appears, this text
                   6062:                                     is not auto translated like the $title is
1.648     raeburn  6063:              frameset       -> if true will start with a <frameset>
1.330     albertel 6064:                                     rather than <body>
1.648     raeburn  6065:              no_title       -> if true the title bar won't be shown
                   6066:              skip_phases    -> hash ref of 
1.338     albertel 6067:                                     head -> skip the <html><head> generation
                   6068:                                     body -> skip all <body> generation
1.648     raeburn  6069:              no_inline_link -> if true and in remote mode, don't show the 
1.361     albertel 6070:                                     'Switch To Inline Menu' link
1.648     raeburn  6071:              no_auto_mt_title -> prevent &mt()ing the title arg
                   6072:              inherit_jsmath -> when creating popup window in a page,
                   6073:                                     should it have jsmath forced on by the
                   6074:                                     current page
1.361     albertel 6075: 
1.648     raeburn  6076: =back
1.460     albertel 6077: 
1.648     raeburn  6078: =back
1.562     albertel 6079: 
1.306     albertel 6080: =cut
                   6081: 
                   6082: sub start_page {
1.309     albertel 6083:     my ($title,$head_extra,$args) = @_;
1.318     albertel 6084:     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.313     albertel 6085:     my %head_args;
1.352     albertel 6086:     foreach my $arg ('redirect','force_register','domain','function',
1.460     albertel 6087: 		     'bgcolor','frameset','no_nav_bar','only_body',
                   6088: 		     'no_auto_mt_title') {
1.319     albertel 6089: 	if (defined($args->{$arg})) {
1.324     raeburn  6090: 	    $head_args{$arg} = $args->{$arg};
1.319     albertel 6091: 	}
1.313     albertel 6092:     }
1.319     albertel 6093: 
1.315     albertel 6094:     $env{'internal.start_page'}++;
1.338     albertel 6095:     my $result;
                   6096:     if (! exists($args->{'skip_phases'}{'head'}) ) {
                   6097: 	$result.=
1.341     albertel 6098: 	    &xml_begin().
1.338     albertel 6099: 	    &headtag($title,$head_extra,\%head_args).&endheadtag();
                   6100:     }
                   6101:     
                   6102:     if (! exists($args->{'skip_phases'}{'body'}) ) {
                   6103: 	if ($args->{'frameset'}) {
                   6104: 	    my $attr_string = &make_attr_string($args->{'force_register'},
                   6105: 						$args->{'add_entries'});
                   6106: 	    $result .= "\n<frameset $attr_string>\n";
                   6107: 	} else {
                   6108: 	    $result .=
                   6109: 		&bodytag($title, 
                   6110: 			 $args->{'function'},       $args->{'add_entries'},
                   6111: 			 $args->{'only_body'},      $args->{'domain'},
                   6112: 			 $args->{'force_register'}, $args->{'body_title'},
                   6113: 			 $args->{'no_nav_bar'},     $args->{'bgcolor'},
1.460     albertel 6114: 			 $args->{'no_title'},       $args->{'no_inline_link'},
                   6115: 			 $args);
1.338     albertel 6116: 	}
1.330     albertel 6117:     }
1.338     albertel 6118: 
1.315     albertel 6119:     if ($args->{'js_ready'}) {
1.317     albertel 6120: 	$result = &js_ready($result);
1.315     albertel 6121:     }
1.320     albertel 6122:     if ($args->{'html_encode'}) {
                   6123: 	$result = &html_encode($result);
                   6124:     }
1.692.4.2  raeburn  6125:     #Breadcrumbs
                   6126:     if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
                   6127:         &Apache::lonhtmlcommon::clear_breadcrumbs();
                   6128:         #if any br links exists, add them to the breadcrumbs
                   6129:         if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {
                   6130:             foreach my $crumb (@{$args->{'bread_crumbs'}}){
                   6131:                 &Apache::lonhtmlcommon::add_breadcrumb($crumb);
                   6132:             }
                   6133:         }
1.306     albertel 6134: 
1.692.4.2  raeburn  6135:         #if bread_crumbs_component exists show it as headline else show only the breadcrumbs
                   6136:         if (exists($args->{'bread_crumbs_component'})){
                   6137:             $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'});
                   6138:         } else {
                   6139:             $result .= &Apache::lonhtmlcommon::breadcrumbs();
                   6140:         }
                   6141:     }
                   6142:     return $result;
1.692.4.3  raeburn  6143: }
1.330     albertel 6144: 
1.306     albertel 6145: =pod
                   6146: 
                   6147: =item * &head()
                   6148: 
                   6149: Returns a complete </body></html> section for LON-CAPA web pages.
                   6150: 
1.315     albertel 6151: Inputs:         $args - additional optional args supported are:
                   6152:                  js_ready     -> return a string ready for being used in 
                   6153:                                  a javascript writeln
1.320     albertel 6154:                  html_encode  -> return a string ready for being used in 
                   6155:                                  a html attribute
1.330     albertel 6156:                  frameset     -> if true will start with a <frameset>
                   6157:                                  rather than <body>
1.493     albertel 6158:                  dicsussion   -> if true will get discussion from
                   6159:                                   lonxml::xmlend
                   6160:                                  (you can pass the target and parser arguments
                   6161:                                   through optional 'target' and 'parser' args
                   6162:                                   to this routine)
1.306     albertel 6163: 
                   6164: =cut
                   6165: 
                   6166: sub end_page {
1.315     albertel 6167:     my ($args) = @_;
                   6168:     $env{'internal.end_page'}++;
1.330     albertel 6169:     my $result;
1.335     albertel 6170:     if ($args->{'discussion'}) {
                   6171: 	my ($target,$parser);
                   6172: 	if (ref($args->{'discussion'})) {
                   6173: 	    ($target,$parser) =($args->{'discussion'}{'target'},
                   6174: 				$args->{'discussion'}{'parser'});
                   6175: 	}
                   6176: 	$result .= &Apache::lonxml::xmlend($target,$parser);
                   6177:     }
                   6178: 
1.330     albertel 6179:     if ($args->{'frameset'}) {
                   6180: 	$result .= '</frameset>';
                   6181:     } else {
1.635     raeburn  6182: 	$result .= &endbodytag($args);
1.330     albertel 6183:     }
                   6184:     $result .= "\n</html>";
                   6185: 
1.315     albertel 6186:     if ($args->{'js_ready'}) {
1.317     albertel 6187: 	$result = &js_ready($result);
1.315     albertel 6188:     }
1.335     albertel 6189: 
1.320     albertel 6190:     if ($args->{'html_encode'}) {
                   6191: 	$result = &html_encode($result);
                   6192:     }
1.335     albertel 6193: 
1.315     albertel 6194:     return $result;
                   6195: }
                   6196: 
1.320     albertel 6197: sub html_encode {
                   6198:     my ($result) = @_;
                   6199: 
1.322     albertel 6200:     $result = &HTML::Entities::encode($result,'<>&"');
1.320     albertel 6201:     
                   6202:     return $result;
                   6203: }
1.317     albertel 6204: sub js_ready {
                   6205:     my ($result) = @_;
                   6206: 
1.323     albertel 6207:     $result =~ s/[\n\r]/ /xmsg;
                   6208:     $result =~ s/\\/\\\\/xmsg;
                   6209:     $result =~ s/'/\\'/xmsg;
1.372     albertel 6210:     $result =~ s{</}{<\\/}xmsg;
1.317     albertel 6211:     
                   6212:     return $result;
                   6213: }
                   6214: 
1.315     albertel 6215: sub validate_page {
                   6216:     if (  exists($env{'internal.start_page'})
1.316     albertel 6217: 	  &&     $env{'internal.start_page'} > 1) {
                   6218: 	&Apache::lonnet::logthis('start_page called multiple times '.
1.318     albertel 6219: 				 $env{'internal.start_page'}.' '.
1.316     albertel 6220: 				 $ENV{'request.filename'});
1.315     albertel 6221:     }
                   6222:     if (  exists($env{'internal.end_page'})
1.316     albertel 6223: 	  &&     $env{'internal.end_page'} > 1) {
                   6224: 	&Apache::lonnet::logthis('end_page called multiple times '.
1.318     albertel 6225: 				 $env{'internal.end_page'}.' '.
1.316     albertel 6226: 				 $env{'request.filename'});
1.315     albertel 6227:     }
                   6228:     if (     exists($env{'internal.start_page'})
                   6229: 	&& ! exists($env{'internal.end_page'})) {
1.316     albertel 6230: 	&Apache::lonnet::logthis('start_page called without end_page '.
                   6231: 				 $env{'request.filename'});
1.315     albertel 6232:     }
                   6233:     if (   ! exists($env{'internal.start_page'})
                   6234: 	&&   exists($env{'internal.end_page'})) {
1.316     albertel 6235: 	&Apache::lonnet::logthis('end_page called without start_page'.
                   6236: 				 $env{'request.filename'});
1.315     albertel 6237:     }
1.306     albertel 6238: }
1.315     albertel 6239: 
1.318     albertel 6240: sub simple_error_page {
                   6241:     my ($r,$title,$msg) = @_;
                   6242:     my $page =
                   6243: 	&Apache::loncommon::start_page($title).
                   6244: 	&mt($msg).
                   6245: 	&Apache::loncommon::end_page();
                   6246:     if (ref($r)) {
                   6247: 	$r->print($page);
1.327     albertel 6248: 	return;
1.318     albertel 6249:     }
                   6250:     return $page;
                   6251: }
1.347     albertel 6252: 
                   6253: {
1.610     albertel 6254:     my @row_count;
1.347     albertel 6255:     sub start_data_table {
1.422     albertel 6256: 	my ($add_class) = @_;
                   6257: 	my $css_class = (join(' ','LC_data_table',$add_class));
1.610     albertel 6258: 	unshift(@row_count,0);
1.422     albertel 6259: 	return '<table class="'.$css_class.'">'."\n";
1.347     albertel 6260:     }
                   6261: 
                   6262:     sub end_data_table {
1.610     albertel 6263: 	shift(@row_count);
1.389     albertel 6264: 	return '</table>'."\n";;
1.347     albertel 6265:     }
                   6266: 
                   6267:     sub start_data_table_row {
1.422     albertel 6268: 	my ($add_class) = @_;
1.610     albertel 6269: 	$row_count[0]++;
                   6270: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.428     albertel 6271: 	$css_class = (join(' ',$css_class,$add_class));
1.422     albertel 6272: 	return  '<tr class="'.$css_class.'">'."\n";;
1.347     albertel 6273:     }
1.471     banghart 6274:     
                   6275:     sub continue_data_table_row {
                   6276: 	my ($add_class) = @_;
1.610     albertel 6277: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.471     banghart 6278: 	$css_class = (join(' ',$css_class,$add_class));
                   6279: 	return  '<tr class="'.$css_class.'">'."\n";;
                   6280:     }
1.347     albertel 6281: 
                   6282:     sub end_data_table_row {
1.389     albertel 6283: 	return '</tr>'."\n";;
1.347     albertel 6284:     }
1.367     www      6285: 
1.421     albertel 6286:     sub start_data_table_empty_row {
1.610     albertel 6287: 	$row_count[0]++;
1.421     albertel 6288: 	return  '<tr class="LC_empty_row" >'."\n";;
                   6289:     }
                   6290: 
                   6291:     sub end_data_table_empty_row {
                   6292: 	return '</tr>'."\n";;
                   6293:     }
                   6294: 
1.367     www      6295:     sub start_data_table_header_row {
1.389     albertel 6296: 	return  '<tr class="LC_header_row">'."\n";;
1.367     www      6297:     }
                   6298: 
                   6299:     sub end_data_table_header_row {
1.389     albertel 6300: 	return '</tr>'."\n";;
1.367     www      6301:     }
1.347     albertel 6302: }
                   6303: 
1.548     albertel 6304: =pod
                   6305: 
                   6306: =item * &inhibit_menu_check($arg)
                   6307: 
                   6308: Checks for a inhibitmenu state and generates output to preserve it
                   6309: 
                   6310: Inputs:         $arg - can be any of
                   6311:                      - undef - in which case the return value is a string 
                   6312:                                to add  into arguments list of a uri
                   6313:                      - 'input' - in which case the return value is a HTML
                   6314:                                  <form> <input> field of type hidden to
                   6315:                                  preserve the value
                   6316:                      - a url - in which case the return value is the url with
                   6317:                                the neccesary cgi args added to preserve the
                   6318:                                inhibitmenu state
                   6319:                      - a ref to a url - no return value, but the string is
                   6320:                                         updated to include the neccessary cgi
                   6321:                                         args to preserve the inhibitmenu state
                   6322: 
                   6323: =cut
                   6324: 
                   6325: sub inhibit_menu_check {
                   6326:     my ($arg) = @_;
                   6327:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   6328:     if ($arg eq 'input') {
                   6329: 	if ($env{'form.inhibitmenu'}) {
                   6330: 	    return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
                   6331: 	} else {
                   6332: 	    return
                   6333: 	}
                   6334:     }
                   6335:     if ($env{'form.inhibitmenu'}) {
                   6336: 	if (ref($arg)) {
                   6337: 	    $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   6338: 	} elsif ($arg eq '') {
                   6339: 	    $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
                   6340: 	} else {
                   6341: 	    $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   6342: 	}
                   6343:     }
                   6344:     if (!ref($arg)) {
                   6345: 	return $arg;
                   6346:     }
                   6347: }
                   6348: 
1.251     albertel 6349: ###############################################
1.182     matthew  6350: 
                   6351: =pod
                   6352: 
1.549     albertel 6353: =back
                   6354: 
                   6355: =head1 User Information Routines
                   6356: 
                   6357: =over 4
                   6358: 
1.405     albertel 6359: =item * &get_users_function()
1.182     matthew  6360: 
                   6361: Used by &bodytag to determine the current users primary role.
                   6362: Returns either 'student','coordinator','admin', or 'author'.
                   6363: 
                   6364: =cut
                   6365: 
                   6366: ###############################################
                   6367: sub get_users_function {
                   6368:     my $function = 'student';
1.692.4.22! raeburn  6369:     if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/
1.182     matthew  6370:         $function='coordinator';
                   6371:     }
1.258     albertel 6372:     if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182     matthew  6373:         $function='admin';
                   6374:     }
1.692.4.5  raeburn  6375:     if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.182     matthew  6376:         ($ENV{'REQUEST_URI'}=~/^(\/priv|\~)/)) {
                   6377:         $function='author';
                   6378:     }
                   6379:     return $function;
1.54      www      6380: }
1.99      www      6381: 
                   6382: ###############################################
                   6383: 
1.233     raeburn  6384: =pod
                   6385: 
1.692.4.2  raeburn  6386: =item * &show_course()
                   6387: 
                   6388: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
                   6389: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
                   6390: Inputs:
                   6391: None
                   6392: 
                   6393: Outputs:
                   6394: Scalar: 1 if 'Course' to be used, 0 otherwise.
                   6395: 
                   6396: =cut
                   6397: 
                   6398: ###############################################
                   6399: sub show_course {
                   6400:     my $course = !$env{'user.adv'};
                   6401:     if (!$env{'user.adv'}) {
                   6402:         foreach my $env (keys(%env)) {
                   6403:             next if ($env !~ m/^user\.priv\./);
                   6404:             if ($env !~ m/^user\.priv\.(?:st|cm)/) {
                   6405:                 $course = 0;
                   6406:                 last;
                   6407:             }
                   6408:         }
                   6409:     }
                   6410:     return $course;
                   6411: }
                   6412: 
                   6413: ###############################################
                   6414: 
                   6415: =pod
                   6416: 
1.542     raeburn  6417: =item * &check_user_status()
1.274     raeburn  6418: 
                   6419: Determines current status of supplied role for a
                   6420: specific user. Roles can be active, previous or future.
                   6421: 
                   6422: Inputs: 
                   6423: user's domain, user's username, course's domain,
1.375     raeburn  6424: course's number, optional section ID.
1.274     raeburn  6425: 
                   6426: Outputs:
                   6427: role status: active, previous or future. 
                   6428: 
                   6429: =cut
                   6430: 
                   6431: sub check_user_status {
1.412     raeburn  6432:     my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.274     raeburn  6433:     my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
                   6434:     my @uroles = keys %userinfo;
                   6435:     my $srchstr;
                   6436:     my $active_chk = 'none';
1.412     raeburn  6437:     my $now = time;
1.274     raeburn  6438:     if (@uroles > 0) {
1.692.4.22! raeburn  6439:         if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
1.274     raeburn  6440:             $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
                   6441:         } else {
1.412     raeburn  6442:             $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
                   6443:         }
                   6444:         if (grep/^\Q$srchstr\E$/,@uroles) {
1.274     raeburn  6445:             my $role_end = 0;
                   6446:             my $role_start = 0;
                   6447:             $active_chk = 'active';
1.412     raeburn  6448:             if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
                   6449:                 $role_end = $1;
                   6450:                 if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
                   6451:                     $role_start = $1;
1.274     raeburn  6452:                 }
                   6453:             }
                   6454:             if ($role_start > 0) {
1.412     raeburn  6455:                 if ($now < $role_start) {
1.274     raeburn  6456:                     $active_chk = 'future';
                   6457:                 }
                   6458:             }
                   6459:             if ($role_end > 0) {
1.412     raeburn  6460:                 if ($now > $role_end) {
1.274     raeburn  6461:                     $active_chk = 'previous';
                   6462:                 }
                   6463:             }
                   6464:         }
                   6465:     }
                   6466:     return $active_chk;
                   6467: }
                   6468: 
                   6469: ###############################################
                   6470: 
                   6471: =pod
                   6472: 
1.405     albertel 6473: =item * &get_sections()
1.233     raeburn  6474: 
                   6475: Determines all the sections for a course including
                   6476: sections with students and sections containing other roles.
1.419     raeburn  6477: Incoming parameters: 
                   6478: 
                   6479: 1. domain
                   6480: 2. course number 
                   6481: 3. reference to array containing roles for which sections should 
                   6482: be gathered (optional).
                   6483: 4. reference to array containing status types for which sections 
                   6484: should be gathered (optional).
                   6485: 
                   6486: If the third argument is undefined, sections are gathered for any role. 
                   6487: If the fourth argument is undefined, sections are gathered for any status.
                   6488: Permissible values are 'active' or 'future' or 'previous'.
1.233     raeburn  6489:  
1.374     raeburn  6490: Returns section hash (keys are section IDs, values are
                   6491: number of users in each section), subject to the
1.419     raeburn  6492: optional roles filter, optional status filter 
1.233     raeburn  6493: 
                   6494: =cut
                   6495: 
                   6496: ###############################################
                   6497: sub get_sections {
1.419     raeburn  6498:     my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366     albertel 6499:     if (!defined($cdom) || !defined($cnum)) {
                   6500:         my $cid =  $env{'request.course.id'};
                   6501: 
                   6502: 	return if (!defined($cid));
                   6503: 
                   6504:         $cdom = $env{'course.'.$cid.'.domain'};
                   6505:         $cnum = $env{'course.'.$cid.'.num'};
                   6506:     }
                   6507: 
                   6508:     my %sectioncount;
1.419     raeburn  6509:     my $now = time;
1.240     albertel 6510: 
1.366     albertel 6511:     if (!defined($possible_roles) || (grep(/^st$/,@$possible_roles))) {
1.276     albertel 6512: 	my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240     albertel 6513: 	my $sec_index = &Apache::loncoursedata::CL_SECTION();
                   6514: 	my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419     raeburn  6515:         my $start_index = &Apache::loncoursedata::CL_START();
                   6516:         my $end_index = &Apache::loncoursedata::CL_END();
                   6517:         my $status;
1.366     albertel 6518: 	while (my ($student,$data) = each(%$classlist)) {
1.419     raeburn  6519: 	    my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
                   6520: 				                     $data->[$status_index],
                   6521:                                                      $data->[$start_index],
                   6522:                                                      $data->[$end_index]);
                   6523:             if ($stu_status eq 'Active') {
                   6524:                 $status = 'active';
                   6525:             } elsif ($end < $now) {
                   6526:                 $status = 'previous';
                   6527:             } elsif ($start > $now) {
                   6528:                 $status = 'future';
                   6529:             } 
                   6530: 	    if ($section ne '-1' && $section !~ /^\s*$/) {
                   6531:                 if ((!defined($possible_status)) || (($status ne '') && 
                   6532:                     (grep/^\Q$status\E$/,@{$possible_status}))) { 
                   6533: 		    $sectioncount{$section}++;
                   6534:                 }
1.240     albertel 6535: 	    }
                   6536: 	}
                   6537:     }
                   6538:     my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   6539:     foreach my $user (sort(keys(%courseroles))) {
                   6540: 	if ($user !~ /^(\w{2})/) { next; }
                   6541: 	my ($role) = ($user =~ /^(\w{2})/);
                   6542: 	if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419     raeburn  6543: 	my ($section,$status);
1.240     albertel 6544: 	if ($role eq 'cr' &&
                   6545: 	    $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
                   6546: 	    $section=$1;
                   6547: 	}
                   6548: 	if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
                   6549: 	if (!defined($section) || $section eq '-1') { next; }
1.419     raeburn  6550:         my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
                   6551:         if ($end == -1 && $start == -1) {
                   6552:             next; #deleted role
                   6553:         }
                   6554:         if (!defined($possible_status)) { 
                   6555:             $sectioncount{$section}++;
                   6556:         } else {
                   6557:             if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
                   6558:                 $status = 'active';
                   6559:             } elsif ($end < $now) {
                   6560:                 $status = 'future';
                   6561:             } elsif ($start > $now) {
                   6562:                 $status = 'previous';
                   6563:             }
                   6564:             if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
                   6565:                 $sectioncount{$section}++;
                   6566:             }
                   6567:         }
1.233     raeburn  6568:     }
1.366     albertel 6569:     return %sectioncount;
1.233     raeburn  6570: }
                   6571: 
1.274     raeburn  6572: ###############################################
1.294     raeburn  6573: 
                   6574: =pod
1.405     albertel 6575: 
                   6576: =item * &get_course_users()
                   6577: 
1.275     raeburn  6578: Retrieves usernames:domains for users in the specified course
                   6579: with specific role(s), and access status. 
                   6580: 
                   6581: Incoming parameters:
1.277     albertel 6582: 1. course domain
                   6583: 2. course number
                   6584: 3. access status: users must have - either active, 
1.275     raeburn  6585: previous, future, or all.
1.277     albertel 6586: 4. reference to array of permissible roles
1.288     raeburn  6587: 5. reference to array of section restrictions (optional)
                   6588: 6. reference to results object (hash of hashes).
                   6589: 7. reference to optional userdata hash
1.609     raeburn  6590: 8. reference to optional statushash
1.630     raeburn  6591: 9. flag if privileged users (except those set to unhide in
                   6592:    course settings) should be excluded    
1.609     raeburn  6593: Keys of top level results hash are roles.
1.275     raeburn  6594: Keys of inner hashes are username:domain, with 
                   6595: values set to access type.
1.288     raeburn  6596: Optional userdata hash returns an array with arguments in the 
                   6597: same order as loncoursedata::get_classlist() for student data.
                   6598: 
1.609     raeburn  6599: Optional statushash returns
                   6600: 
1.288     raeburn  6601: Entries for end, start, section and status are blank because
                   6602: of the possibility of multiple values for non-student roles.
                   6603: 
1.275     raeburn  6604: =cut
1.405     albertel 6605: 
1.275     raeburn  6606: ###############################################
1.405     albertel 6607: 
1.275     raeburn  6608: sub get_course_users {
1.630     raeburn  6609:     my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288     raeburn  6610:     my %idx = ();
1.419     raeburn  6611:     my %seclists;
1.288     raeburn  6612: 
                   6613:     $idx{udom} = &Apache::loncoursedata::CL_SDOM();
                   6614:     $idx{uname} =  &Apache::loncoursedata::CL_SNAME();
                   6615:     $idx{end} = &Apache::loncoursedata::CL_END();
                   6616:     $idx{start} = &Apache::loncoursedata::CL_START();
                   6617:     $idx{id} = &Apache::loncoursedata::CL_ID();
                   6618:     $idx{section} = &Apache::loncoursedata::CL_SECTION();
                   6619:     $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
                   6620:     $idx{status} = &Apache::loncoursedata::CL_STATUS();
                   6621: 
1.290     albertel 6622:     if (grep(/^st$/,@{$roles})) {
1.276     albertel 6623:         my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278     raeburn  6624:         my $now = time;
1.277     albertel 6625:         foreach my $student (keys(%{$classlist})) {
1.288     raeburn  6626:             my $match = 0;
1.412     raeburn  6627:             my $secmatch = 0;
1.419     raeburn  6628:             my $section = $$classlist{$student}[$idx{section}];
1.609     raeburn  6629:             my $status = $$classlist{$student}[$idx{status}];
1.419     raeburn  6630:             if ($section eq '') {
                   6631:                 $section = 'none';
                   6632:             }
1.291     albertel 6633:             if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 6634:                 if (grep(/^all$/,@{$sections})) {
1.412     raeburn  6635:                     $secmatch = 1;
                   6636:                 } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420     albertel 6637:                     if (grep(/^none$/,@{$sections})) {
1.412     raeburn  6638:                         $secmatch = 1;
                   6639:                     }
                   6640:                 } else {  
1.419     raeburn  6641: 		    if (grep(/^\Q$section\E$/,@{$sections})) {
1.412     raeburn  6642: 		        $secmatch = 1;
                   6643:                     }
1.290     albertel 6644: 		}
1.412     raeburn  6645:                 if (!$secmatch) {
                   6646:                     next;
                   6647:                 }
1.419     raeburn  6648:             }
1.275     raeburn  6649:             if (defined($$types{'active'})) {
1.288     raeburn  6650:                 if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275     raeburn  6651:                     push(@{$$users{st}{$student}},'active');
1.288     raeburn  6652:                     $match = 1;
1.275     raeburn  6653:                 }
                   6654:             }
                   6655:             if (defined($$types{'previous'})) {
1.609     raeburn  6656:                 if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275     raeburn  6657:                     push(@{$$users{st}{$student}},'previous');
1.288     raeburn  6658:                     $match = 1;
1.275     raeburn  6659:                 }
                   6660:             }
                   6661:             if (defined($$types{'future'})) {
1.609     raeburn  6662:                 if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275     raeburn  6663:                     push(@{$$users{st}{$student}},'future');
1.288     raeburn  6664:                     $match = 1;
1.275     raeburn  6665:                 }
                   6666:             }
1.609     raeburn  6667:             if ($match) {
                   6668:                 push(@{$seclists{$student}},$section);
                   6669:                 if (ref($userdata) eq 'HASH') {
                   6670:                     $$userdata{$student} = $$classlist{$student};
                   6671:                 }
                   6672:                 if (ref($statushash) eq 'HASH') {
                   6673:                     $statushash->{$student}{'st'}{$section} = $status;
                   6674:                 }
1.288     raeburn  6675:             }
1.275     raeburn  6676:         }
                   6677:     }
1.412     raeburn  6678:     if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439     raeburn  6679:         my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   6680:         my $now = time;
1.609     raeburn  6681:         my %displaystatus = ( previous => 'Expired',
                   6682:                               active   => 'Active',
                   6683:                               future   => 'Future',
                   6684:                             );
1.630     raeburn  6685:         my %nothide;
                   6686:         if ($hidepriv) {
                   6687:             my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
                   6688:             foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
                   6689:                 if ($user !~ /:/) {
                   6690:                     $nothide{join(':',split(/[\@]/,$user))}=1;
                   6691:                 } else {
                   6692:                     $nothide{$user} = 1;
                   6693:                 }
                   6694:             }
                   6695:         }
1.439     raeburn  6696:         foreach my $person (sort(keys(%coursepersonnel))) {
1.288     raeburn  6697:             my $match = 0;
1.412     raeburn  6698:             my $secmatch = 0;
1.439     raeburn  6699:             my $status;
1.412     raeburn  6700:             my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275     raeburn  6701:             $user =~ s/:$//;
1.439     raeburn  6702:             my ($end,$start) = split(/:/,$coursepersonnel{$person});
                   6703:             if ($end == -1 || $start == -1) {
                   6704:                 next;
                   6705:             }
                   6706:             if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
                   6707:                 (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412     raeburn  6708:                 my ($uname,$udom) = split(/:/,$user);
                   6709:                 if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 6710:                     if (grep(/^all$/,@{$sections})) {
1.412     raeburn  6711:                         $secmatch = 1;
                   6712:                     } elsif ($usec eq '') {
1.420     albertel 6713:                         if (grep(/^none$/,@{$sections})) {
1.412     raeburn  6714:                             $secmatch = 1;
                   6715:                         }
                   6716:                     } else {
                   6717:                         if (grep(/^\Q$usec\E$/,@{$sections})) {
                   6718:                             $secmatch = 1;
                   6719:                         }
                   6720:                     }
                   6721:                     if (!$secmatch) {
                   6722:                         next;
                   6723:                     }
1.288     raeburn  6724:                 }
1.419     raeburn  6725:                 if ($usec eq '') {
                   6726:                     $usec = 'none';
                   6727:                 }
1.275     raeburn  6728:                 if ($uname ne '' && $udom ne '') {
1.630     raeburn  6729:                     if ($hidepriv) {
                   6730:                         if ((&Apache::lonnet::privileged($uname,$udom)) &&
                   6731:                             (!$nothide{$uname.':'.$udom})) {
                   6732:                             next;
                   6733:                         }
                   6734:                     }
1.503     raeburn  6735:                     if ($end > 0 && $end < $now) {
1.439     raeburn  6736:                         $status = 'previous';
                   6737:                     } elsif ($start > $now) {
                   6738:                         $status = 'future';
                   6739:                     } else {
                   6740:                         $status = 'active';
                   6741:                     }
1.277     albertel 6742:                     foreach my $type (keys(%{$types})) { 
1.275     raeburn  6743:                         if ($status eq $type) {
1.420     albertel 6744:                             if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419     raeburn  6745:                                 push(@{$$users{$role}{$user}},$type);
                   6746:                             }
1.288     raeburn  6747:                             $match = 1;
                   6748:                         }
                   6749:                     }
1.419     raeburn  6750:                     if (($match) && (ref($userdata) eq 'HASH')) {
                   6751:                         if (!exists($$userdata{$uname.':'.$udom})) {
                   6752: 			    &get_user_info($udom,$uname,\%idx,$userdata);
                   6753:                         }
1.420     albertel 6754:                         if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419     raeburn  6755:                             push(@{$seclists{$uname.':'.$udom}},$usec);
                   6756:                         }
1.609     raeburn  6757:                         if (ref($statushash) eq 'HASH') {
                   6758:                             $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
                   6759:                         }
1.275     raeburn  6760:                     }
                   6761:                 }
                   6762:             }
                   6763:         }
1.290     albertel 6764:         if (grep(/^ow$/,@{$roles})) {
1.279     raeburn  6765:             if ((defined($cdom)) && (defined($cnum))) {
                   6766:                 my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
                   6767:                 if ( defined($csettings{'internal.courseowner'}) ) {
                   6768:                     my $owner = $csettings{'internal.courseowner'};
1.609     raeburn  6769:                     next if ($owner eq '');
                   6770:                     my ($ownername,$ownerdom);
                   6771:                     if ($owner =~ /^([^:]+):([^:]+)$/) {
                   6772:                         $ownername = $1;
                   6773:                         $ownerdom = $2;
                   6774:                     } else {
                   6775:                         $ownername = $owner;
                   6776:                         $ownerdom = $cdom;
                   6777:                         $owner = $ownername.':'.$ownerdom;
1.439     raeburn  6778:                     }
                   6779:                     @{$$users{'ow'}{$owner}} = 'any';
1.290     albertel 6780:                     if (defined($userdata) && 
1.609     raeburn  6781: 			!exists($$userdata{$owner})) {
                   6782: 			&get_user_info($ownerdom,$ownername,\%idx,$userdata);
                   6783:                         if (!grep(/^none$/,@{$seclists{$owner}})) {
                   6784:                             push(@{$seclists{$owner}},'none');
                   6785:                         }
                   6786:                         if (ref($statushash) eq 'HASH') {
                   6787:                             $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419     raeburn  6788:                         }
1.290     albertel 6789: 		    }
1.279     raeburn  6790:                 }
                   6791:             }
                   6792:         }
1.419     raeburn  6793:         foreach my $user (keys(%seclists)) {
                   6794:             @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
                   6795:             $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
                   6796:         }
1.275     raeburn  6797:     }
                   6798:     return;
                   6799: }
                   6800: 
1.288     raeburn  6801: sub get_user_info {
                   6802:     my ($udom,$uname,$idx,$userdata) = @_;
1.289     albertel 6803:     $$userdata{$uname.':'.$udom}[$$idx{fullname}] = 
                   6804: 	&plainname($uname,$udom,'lastname');
1.291     albertel 6805:     $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297     raeburn  6806:     $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609     raeburn  6807:     my %idhash =  &Apache::lonnet::idrget($udom,($uname));
                   6808:     $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname}; 
1.288     raeburn  6809:     return;
                   6810: }
1.275     raeburn  6811: 
1.472     raeburn  6812: ###############################################
                   6813: 
                   6814: =pod
                   6815: 
                   6816: =item * &get_user_quota()
                   6817: 
                   6818: Retrieves quota assigned for storage of portfolio files for a user  
                   6819: 
                   6820: Incoming parameters:
                   6821: 1. user's username
                   6822: 2. user's domain
                   6823: 
                   6824: Returns:
1.536     raeburn  6825: 1. Disk quota (in Mb) assigned to student.
                   6826: 2. (Optional) Type of setting: custom or default
                   6827:    (individually assigned or default for user's 
                   6828:    institutional status).
                   6829: 3. (Optional) - User's institutional status (e.g., faculty, staff
                   6830:    or student - types as defined in localenroll::inst_usertypes 
                   6831:    for user's domain, which determines default quota for user.
                   6832: 4. (Optional) - Default quota which would apply to the user.
1.472     raeburn  6833: 
                   6834: If a value has been stored in the user's environment, 
1.536     raeburn  6835: it will return that, otherwise it returns the maximal default
                   6836: defined for the user's instituional status(es) in the domain.
1.472     raeburn  6837: 
                   6838: =cut
                   6839: 
                   6840: ###############################################
                   6841: 
                   6842: 
                   6843: sub get_user_quota {
                   6844:     my ($uname,$udom) = @_;
1.536     raeburn  6845:     my ($quota,$quotatype,$settingstatus,$defquota);
1.472     raeburn  6846:     if (!defined($udom)) {
                   6847:         $udom = $env{'user.domain'};
                   6848:     }
                   6849:     if (!defined($uname)) {
                   6850:         $uname = $env{'user.name'};
                   6851:     }
                   6852:     if (($udom eq '' || $uname eq '') ||
                   6853:         ($udom eq 'public') && ($uname eq 'public')) {
                   6854:         $quota = 0;
1.536     raeburn  6855:         $quotatype = 'default';
                   6856:         $defquota = 0; 
1.472     raeburn  6857:     } else {
1.536     raeburn  6858:         my $inststatus;
1.472     raeburn  6859:         if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
                   6860:             $quota = $env{'environment.portfolioquota'};
1.536     raeburn  6861:             $inststatus = $env{'environment.inststatus'};
1.472     raeburn  6862:         } else {
1.536     raeburn  6863:             my %userenv = 
                   6864:                 &Apache::lonnet::get('environment',['portfolioquota',
                   6865:                                      'inststatus'],$udom,$uname);
1.472     raeburn  6866:             my ($tmp) = keys(%userenv);
                   6867:             if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   6868:                 $quota = $userenv{'portfolioquota'};
1.536     raeburn  6869:                 $inststatus = $userenv{'inststatus'};
1.472     raeburn  6870:             } else {
                   6871:                 undef(%userenv);
                   6872:             }
                   6873:         }
1.536     raeburn  6874:         ($defquota,$settingstatus) = &default_quota($udom,$inststatus);
1.472     raeburn  6875:         if ($quota eq '') {
1.536     raeburn  6876:             $quota = $defquota;
                   6877:             $quotatype = 'default';
                   6878:         } else {
                   6879:             $quotatype = 'custom';
1.472     raeburn  6880:         }
                   6881:     }
1.536     raeburn  6882:     if (wantarray) {
                   6883:         return ($quota,$quotatype,$settingstatus,$defquota);
                   6884:     } else {
                   6885:         return $quota;
                   6886:     }
1.472     raeburn  6887: }
                   6888: 
                   6889: ###############################################
                   6890: 
                   6891: =pod
                   6892: 
                   6893: =item * &default_quota()
                   6894: 
1.536     raeburn  6895: Retrieves default quota assigned for storage of user portfolio files,
                   6896: given an (optional) user's institutional status.
1.472     raeburn  6897: 
                   6898: Incoming parameters:
                   6899: 1. domain
1.536     raeburn  6900: 2. (Optional) institutional status(es).  This is a : separated list of 
                   6901:    status types (e.g., faculty, staff, student etc.)
                   6902:    which apply to the user for whom the default is being retrieved.
                   6903:    If the institutional status string in undefined, the domain
                   6904:    default quota will be returned. 
1.472     raeburn  6905: 
                   6906: Returns:
                   6907: 1. Default disk quota (in Mb) for user portfolios in the domain.
1.536     raeburn  6908: 2. (Optional) institutional type which determined the value of the
                   6909:    default quota.
1.472     raeburn  6910: 
                   6911: If a value has been stored in the domain's configuration db,
                   6912: it will return that, otherwise it returns 20 (for backwards 
                   6913: compatibility with domains which have not set up a configuration
                   6914: db file; the original statically defined portfolio quota was 20 Mb). 
                   6915: 
1.536     raeburn  6916: If the user's status includes multiple types (e.g., staff and student),
                   6917: the largest default quota which applies to the user determines the
                   6918: default quota returned.
                   6919: 
1.692.4.15  raeburn  6920: =back
                   6921: 
1.472     raeburn  6922: =cut
                   6923: 
                   6924: ###############################################
                   6925: 
                   6926: 
                   6927: sub default_quota {
1.536     raeburn  6928:     my ($udom,$inststatus) = @_;
                   6929:     my ($defquota,$settingstatus);
                   6930:     my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622     raeburn  6931:                                             ['quotas'],$udom);
                   6932:     if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536     raeburn  6933:         if ($inststatus ne '') {
1.692.4.2  raeburn  6934:             my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536     raeburn  6935:             foreach my $item (@statuses) {
1.692.4.2  raeburn  6936:                 if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') {
                   6937:                     if ($quotahash{'quotas'}{'defaultquota'}{$item} ne '') {
                   6938:                         if ($defquota eq '') {
                   6939:                             $defquota = $quotahash{'quotas'}{'defaultquota'}{$item};
                   6940:                             $settingstatus = $item;
                   6941:                         } elsif ($quotahash{'quotas'}{'defaultquota'}{$item} > $defquota) {
                   6942:                             $defquota = $quotahash{'quotas'}{'defaultquota'}{$item};
                   6943:                             $settingstatus = $item;
                   6944:                         }
                   6945:                     }
                   6946:                 } else {
                   6947:                     if ($quotahash{'quotas'}{$item} ne '') {
                   6948:                         if ($defquota eq '') {
                   6949:                             $defquota = $quotahash{'quotas'}{$item};
                   6950:                             $settingstatus = $item;
                   6951:                         } elsif ($quotahash{'quotas'}{$item} > $defquota) {
                   6952:                             $defquota = $quotahash{'quotas'}{$item};
                   6953:                             $settingstatus = $item;
                   6954:                         }
1.536     raeburn  6955:                     }
                   6956:                 }
                   6957:             }
                   6958:         }
                   6959:         if ($defquota eq '') {
1.692.4.2  raeburn  6960:             if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') {
                   6961:                 $defquota = $quotahash{'quotas'}{'defaultquota'}{'default'};
                   6962:             } else {
                   6963:                 $defquota = $quotahash{'quotas'}{'default'};
                   6964:             }
1.536     raeburn  6965:             $settingstatus = 'default';
                   6966:         }
                   6967:     } else {
                   6968:         $settingstatus = 'default';
                   6969:         $defquota = 20;
                   6970:     }
                   6971:     if (wantarray) {
                   6972:         return ($defquota,$settingstatus);
1.472     raeburn  6973:     } else {
1.536     raeburn  6974:         return $defquota;
1.472     raeburn  6975:     }
                   6976: }
                   6977: 
1.384     raeburn  6978: sub get_secgrprole_info {
                   6979:     my ($cdom,$cnum,$needroles,$type)  = @_;
                   6980:     my %sections_count = &get_sections($cdom,$cnum);
                   6981:     my @sections =  (sort {$a <=> $b} keys(%sections_count));
                   6982:     my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
                   6983:     my @groups = sort(keys(%curr_groups));
                   6984:     my $allroles = [];
                   6985:     my $rolehash;
                   6986:     my $accesshash = {
                   6987:                      active => 'Currently has access',
                   6988:                      future => 'Will have future access',
                   6989:                      previous => 'Previously had access',
                   6990:                   };
                   6991:     if ($needroles) {
                   6992:         $rolehash = {'all' => 'all'};
1.385     albertel 6993:         my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   6994: 	if (&Apache::lonnet::error(%user_roles)) {
                   6995: 	    undef(%user_roles);
                   6996: 	}
                   6997:         foreach my $item (keys(%user_roles)) {
1.384     raeburn  6998:             my ($role)=split(/\:/,$item,2);
                   6999:             if ($role eq 'cr') { next; }
                   7000:             if ($role =~ /^cr/) {
                   7001:                 $$rolehash{$role} = (split('/',$role))[3];
                   7002:             } else {
                   7003:                 $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
                   7004:             }
                   7005:         }
                   7006:         foreach my $key (sort(keys(%{$rolehash}))) {
                   7007:             push(@{$allroles},$key);
                   7008:         }
                   7009:         push (@{$allroles},'st');
                   7010:         $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
                   7011:     }
                   7012:     return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
                   7013: }
                   7014: 
1.555     raeburn  7015: sub user_picker {
1.627     raeburn  7016:     my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype) = @_;
1.555     raeburn  7017:     my $currdom = $dom;
                   7018:     my %curr_selected = (
                   7019:                         srchin => 'dom',
1.580     raeburn  7020:                         srchby => 'lastname',
1.555     raeburn  7021:                       );
                   7022:     my $srchterm;
1.625     raeburn  7023:     if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555     raeburn  7024:         if ($srch->{'srchby'} ne '') {
                   7025:             $curr_selected{'srchby'} = $srch->{'srchby'};
                   7026:         }
                   7027:         if ($srch->{'srchin'} ne '') {
                   7028:             $curr_selected{'srchin'} = $srch->{'srchin'};
                   7029:         }
                   7030:         if ($srch->{'srchtype'} ne '') {
                   7031:             $curr_selected{'srchtype'} = $srch->{'srchtype'};
                   7032:         }
                   7033:         if ($srch->{'srchdomain'} ne '') {
                   7034:             $currdom = $srch->{'srchdomain'};
                   7035:         }
                   7036:         $srchterm = $srch->{'srchterm'};
                   7037:     }
                   7038:     my %lt=&Apache::lonlocal::texthash(
1.573     raeburn  7039:                     'usr'       => 'Search criteria',
1.563     raeburn  7040:                     'doma'      => 'Domain/institution to search',
1.558     albertel 7041:                     'uname'     => 'username',
                   7042:                     'lastname'  => 'last name',
1.555     raeburn  7043:                     'lastfirst' => 'last name, first name',
1.558     albertel 7044:                     'crs'       => 'in this course',
1.576     raeburn  7045:                     'dom'       => 'in selected LON-CAPA domain', 
1.558     albertel 7046:                     'alc'       => 'all LON-CAPA',
1.573     raeburn  7047:                     'instd'     => 'in institutional directory for selected domain',
1.558     albertel 7048:                     'exact'     => 'is',
                   7049:                     'contains'  => 'contains',
1.569     raeburn  7050:                     'begins'    => 'begins with',
1.571     raeburn  7051:                     'youm'      => "You must include some text to search for.",
                   7052:                     'thte'      => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
                   7053:                     'thet'      => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
                   7054:                     'yomc'      => "You must choose a domain when using an institutional directory search.",
                   7055:                     'ymcd'      => "You must choose a domain when using a domain search.",
                   7056:                     'whus'      => "When using searching by last,first you must include a comma as separator between last name and first name.",
                   7057:                     'whse'      => "When searching by last,first you must include at least one character in the first name.",
                   7058:                      'thfo'     => "The following need to be corrected before the search can be run:",
1.555     raeburn  7059:                                        );
1.563     raeburn  7060:     my $domform = &select_dom_form($currdom,'srchdomain',1,1);
                   7061:     my $srchinsel = ' <select name="srchin">';
1.555     raeburn  7062: 
                   7063:     my @srchins = ('crs','dom','alc','instd');
                   7064: 
                   7065:     foreach my $option (@srchins) {
                   7066:         # FIXME 'alc' option unavailable until 
                   7067:         #       loncreateuser::print_user_query_page()
                   7068:         #       has been completed.
                   7069:         next if ($option eq 'alc');
1.692.4.11  raeburn  7070:         next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));
1.555     raeburn  7071:         next if ($option eq 'crs' && !$env{'request.course.id'});
1.563     raeburn  7072:         if ($curr_selected{'srchin'} eq $option) {
                   7073:             $srchinsel .= ' 
                   7074:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   7075:         } else {
                   7076:             $srchinsel .= '
                   7077:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   7078:         }
1.555     raeburn  7079:     }
1.563     raeburn  7080:     $srchinsel .= "\n  </select>\n";
1.555     raeburn  7081: 
                   7082:     my $srchbysel =  ' <select name="srchby">';
1.580     raeburn  7083:     foreach my $option ('lastname','lastfirst','uname') {
1.555     raeburn  7084:         if ($curr_selected{'srchby'} eq $option) {
                   7085:             $srchbysel .= '
                   7086:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   7087:         } else {
                   7088:             $srchbysel .= '
                   7089:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   7090:          }
                   7091:     }
                   7092:     $srchbysel .= "\n  </select>\n";
                   7093: 
                   7094:     my $srchtypesel = ' <select name="srchtype">';
1.580     raeburn  7095:     foreach my $option ('begins','contains','exact') {
1.555     raeburn  7096:         if ($curr_selected{'srchtype'} eq $option) {
                   7097:             $srchtypesel .= '
                   7098:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   7099:         } else {
                   7100:             $srchtypesel .= '
                   7101:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   7102:         }
                   7103:     }
                   7104:     $srchtypesel .= "\n  </select>\n";
                   7105: 
1.558     albertel 7106:     my ($newuserscript,$new_user_create);
1.556     raeburn  7107: 
                   7108:     if ($forcenewuser) {
1.576     raeburn  7109:         if (ref($srch) eq 'HASH') {
                   7110:             if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $env{'request.role.domain'}) {
1.627     raeburn  7111:                 if ($cancreate) {
                   7112:                     $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>';
                   7113:                 } else {
1.692.4.2  raeburn  7114:                     my $helplink = 'javascript:helpMenu('."'display'".')';
1.627     raeburn  7115:                     my %usertypetext = (
                   7116:                         official   => 'institutional',
                   7117:                         unofficial => 'non-institutional',
                   7118:                     );
1.692.4.2  raeburn  7119:                     $new_user_create = '<p class="LC_warning">'.
                   7120:                                        &mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.").' '.
                   7121:                                        &mt('Please contact the [_1]helpdesk[_2] for assistance.','<a href="'.$helplink.'">','</a>').'</p><br />';
1.627     raeburn  7122:                 }
1.576     raeburn  7123:             }
                   7124:         }
                   7125: 
1.556     raeburn  7126:         $newuserscript = <<"ENDSCRIPT";
                   7127: 
1.570     raeburn  7128: function setSearch(createnew,callingForm) {
1.556     raeburn  7129:     if (createnew == 1) {
1.570     raeburn  7130:         for (var i=0; i<callingForm.srchby.length; i++) {
                   7131:             if (callingForm.srchby.options[i].value == 'uname') {
                   7132:                 callingForm.srchby.selectedIndex = i;
1.556     raeburn  7133:             }
                   7134:         }
1.570     raeburn  7135:         for (var i=0; i<callingForm.srchin.length; i++) {
                   7136:             if ( callingForm.srchin.options[i].value == 'dom') {
                   7137: 		callingForm.srchin.selectedIndex = i;
1.556     raeburn  7138:             }
                   7139:         }
1.570     raeburn  7140:         for (var i=0; i<callingForm.srchtype.length; i++) {
                   7141:             if (callingForm.srchtype.options[i].value == 'exact') {
                   7142:                 callingForm.srchtype.selectedIndex = i;
1.556     raeburn  7143:             }
                   7144:         }
1.570     raeburn  7145:         for (var i=0; i<callingForm.srchdomain.length; i++) {
                   7146:             if (callingForm.srchdomain.options[i].value == '$env{'request.role.domain'}') {
                   7147:                 callingForm.srchdomain.selectedIndex = i;
1.556     raeburn  7148:             }
                   7149:         }
                   7150:     }
                   7151: }
                   7152: ENDSCRIPT
1.558     albertel 7153: 
1.556     raeburn  7154:     }
                   7155: 
1.555     raeburn  7156:     my $output = <<"END_BLOCK";
1.556     raeburn  7157: <script type="text/javascript">
1.692.4.4  raeburn  7158: // <![CDATA[
1.570     raeburn  7159: function validateEntry(callingForm) {
1.558     albertel 7160: 
1.556     raeburn  7161:     var checkok = 1;
1.558     albertel 7162:     var srchin;
1.570     raeburn  7163:     for (var i=0; i<callingForm.srchin.length; i++) {
                   7164: 	if ( callingForm.srchin[i].checked ) {
                   7165: 	    srchin = callingForm.srchin[i].value;
1.558     albertel 7166: 	}
                   7167:     }
                   7168: 
1.570     raeburn  7169:     var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
                   7170:     var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
                   7171:     var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
                   7172:     var srchterm =  callingForm.srchterm.value;
                   7173:     var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556     raeburn  7174:     var msg = "";
                   7175: 
                   7176:     if (srchterm == "") {
                   7177:         checkok = 0;
1.571     raeburn  7178:         msg += "$lt{'youm'}\\n";
1.556     raeburn  7179:     }
                   7180: 
1.569     raeburn  7181:     if (srchtype== 'begins') {
                   7182:         if (srchterm.length < 2) {
                   7183:             checkok = 0;
1.571     raeburn  7184:             msg += "$lt{'thte'}\\n";
1.569     raeburn  7185:         }
                   7186:     }
                   7187: 
1.556     raeburn  7188:     if (srchtype== 'contains') {
                   7189:         if (srchterm.length < 3) {
                   7190:             checkok = 0;
1.571     raeburn  7191:             msg += "$lt{'thet'}\\n";
1.556     raeburn  7192:         }
                   7193:     }
                   7194:     if (srchin == 'instd') {
                   7195:         if (srchdomain == '') {
                   7196:             checkok = 0;
1.571     raeburn  7197:             msg += "$lt{'yomc'}\\n";
1.556     raeburn  7198:         }
                   7199:     }
                   7200:     if (srchin == 'dom') {
                   7201:         if (srchdomain == '') {
                   7202:             checkok = 0;
1.571     raeburn  7203:             msg += "$lt{'ymcd'}\\n";
1.556     raeburn  7204:         }
                   7205:     }
                   7206:     if (srchby == 'lastfirst') {
                   7207:         if (srchterm.indexOf(",") == -1) {
                   7208:             checkok = 0;
1.571     raeburn  7209:             msg += "$lt{'whus'}\\n";
1.556     raeburn  7210:         }
                   7211:         if (srchterm.indexOf(",") == srchterm.length -1) {
                   7212:             checkok = 0;
1.571     raeburn  7213:             msg += "$lt{'whse'}\\n";
1.556     raeburn  7214:         }
                   7215:     }
                   7216:     if (checkok == 0) {
1.571     raeburn  7217:         alert("$lt{'thfo'}\\n"+msg);
1.556     raeburn  7218:         return;
                   7219:     }
                   7220:     if (checkok == 1) {
1.570     raeburn  7221:         callingForm.submit();
1.556     raeburn  7222:     }
                   7223: }
                   7224: 
                   7225: $newuserscript
                   7226: 
1.692.4.4  raeburn  7227: // ]]>
1.556     raeburn  7228: </script>
1.558     albertel 7229: 
                   7230: $new_user_create
                   7231: 
1.555     raeburn  7232: END_BLOCK
1.558     albertel 7233: 
1.692.4.9  raeburn  7234:     $output .= &Apache::lonhtmlcommon::start_pick_box().
                   7235:                &Apache::lonhtmlcommon::row_title($lt{'doma'}).
                   7236:                $domform.
                   7237:                &Apache::lonhtmlcommon::row_closure().
                   7238:                &Apache::lonhtmlcommon::row_title($lt{'usr'}).
                   7239:                $srchbysel.
                   7240:                $srchtypesel.
                   7241:                '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
                   7242:                $srchinsel.
                   7243:                &Apache::lonhtmlcommon::row_closure(1).
                   7244:                &Apache::lonhtmlcommon::end_pick_box().
                   7245:                '<br />';
1.555     raeburn  7246:     return $output;
                   7247: }
                   7248: 
1.612     raeburn  7249: sub user_rule_check {
1.615     raeburn  7250:     my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.612     raeburn  7251:     my $response;
                   7252:     if (ref($usershash) eq 'HASH') {
                   7253:         foreach my $user (keys(%{$usershash})) {
                   7254:             my ($uname,$udom) = split(/:/,$user);
                   7255:             next if ($udom eq '' || $uname eq '');
1.615     raeburn  7256:             my ($id,$newuser);
1.612     raeburn  7257:             if (ref($usershash->{$user}) eq 'HASH') {
1.615     raeburn  7258:                 $newuser = $usershash->{$user}->{'newuser'};
1.612     raeburn  7259:                 $id = $usershash->{$user}->{'id'};
                   7260:             }
                   7261:             my $inst_response;
                   7262:             if (ref($checks) eq 'HASH') {
                   7263:                 if (defined($checks->{'username'})) {
1.615     raeburn  7264:                     ($inst_response,%{$inst_results->{$user}}) = 
1.612     raeburn  7265:                         &Apache::lonnet::get_instuser($udom,$uname);
                   7266:                 } elsif (defined($checks->{'id'})) {
1.615     raeburn  7267:                     ($inst_response,%{$inst_results->{$user}}) =
1.612     raeburn  7268:                         &Apache::lonnet::get_instuser($udom,undef,$id);
                   7269:                 }
1.615     raeburn  7270:             } else {
                   7271:                 ($inst_response,%{$inst_results->{$user}}) =
                   7272:                     &Apache::lonnet::get_instuser($udom,$uname);
                   7273:                 return;
1.612     raeburn  7274:             }
1.615     raeburn  7275:             if (!$got_rules->{$udom}) {
1.612     raeburn  7276:                 my %domconfig = &Apache::lonnet::get_dom('configuration',
                   7277:                                                   ['usercreation'],$udom);
                   7278:                 if (ref($domconfig{'usercreation'}) eq 'HASH') {
1.615     raeburn  7279:                     foreach my $item ('username','id') {
1.612     raeburn  7280:                         if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
                   7281:                             $$curr_rules{$udom}{$item} = 
                   7282:                                 $domconfig{'usercreation'}{$item.'_rule'};
1.585     raeburn  7283:                         }
                   7284:                     }
                   7285:                 }
1.615     raeburn  7286:                 $got_rules->{$udom} = 1;  
1.585     raeburn  7287:             }
1.612     raeburn  7288:             foreach my $item (keys(%{$checks})) {
                   7289:                 if (ref($$curr_rules{$udom}) eq 'HASH') {
                   7290:                     if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
                   7291:                         if (@{$$curr_rules{$udom}{$item}} > 0) {
                   7292:                             my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,$$curr_rules{$udom}{$item});
                   7293:                             foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
                   7294:                                 if ($rule_check{$rule}) {
                   7295:                                     $$rulematch{$user}{$item} = $rule;
                   7296:                                     if ($inst_response eq 'ok') {
1.615     raeburn  7297:                                         if (ref($inst_results) eq 'HASH') {
                   7298:                                             if (ref($inst_results->{$user}) eq 'HASH') {
                   7299:                                                 if (keys(%{$inst_results->{$user}}) == 0) {
                   7300:                                                     $$alerts{$item}{$udom}{$uname} = 1;
                   7301:                                                 }
1.612     raeburn  7302:                                             }
                   7303:                                         }
1.615     raeburn  7304:                                     }
                   7305:                                     last;
1.585     raeburn  7306:                                 }
                   7307:                             }
                   7308:                         }
                   7309:                     }
                   7310:                 }
                   7311:             }
                   7312:         }
                   7313:     }
1.612     raeburn  7314:     return;
                   7315: }
                   7316: 
                   7317: sub user_rule_formats {
                   7318:     my ($domain,$domdesc,$curr_rules,$check) = @_;
                   7319:     my %text = ( 
                   7320:                  'username' => 'Usernames',
                   7321:                  'id'       => 'IDs',
                   7322:                );
                   7323:     my $output;
                   7324:     my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
                   7325:     if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
                   7326:         if (@{$ruleorder} > 0) {
                   7327:             $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>';
                   7328:             foreach my $rule (@{$ruleorder}) {
                   7329:                 if (ref($curr_rules) eq 'ARRAY') {
                   7330:                     if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
                   7331:                         if (ref($rules->{$rule}) eq 'HASH') {
                   7332:                             $output .= '<li>'.$rules->{$rule}{'name'}.': '.
                   7333:                                         $rules->{$rule}{'desc'}.'</li>';
                   7334:                         }
                   7335:                     }
                   7336:                 }
                   7337:             }
                   7338:             $output .= '</ul>';
                   7339:         }
                   7340:     }
                   7341:     return $output;
                   7342: }
                   7343: 
                   7344: sub instrule_disallow_msg {
1.615     raeburn  7345:     my ($checkitem,$domdesc,$count,$mode) = @_;
1.612     raeburn  7346:     my $response;
                   7347:     my %text = (
                   7348:                   item   => 'username',
                   7349:                   items  => 'usernames',
                   7350:                   match  => 'matches',
                   7351:                   do     => 'does',
                   7352:                   action => 'a username',
                   7353:                   one    => 'one',
                   7354:                );
                   7355:     if ($count > 1) {
                   7356:         $text{'item'} = 'usernames';
                   7357:         $text{'match'} ='match';
                   7358:         $text{'do'} = 'do';
                   7359:         $text{'action'} = 'usernames',
                   7360:         $text{'one'} = 'ones';
                   7361:     }
                   7362:     if ($checkitem eq 'id') {
                   7363:         $text{'items'} = 'IDs';
                   7364:         $text{'item'} = 'ID';
                   7365:         $text{'action'} = 'an ID';
1.615     raeburn  7366:         if ($count > 1) {
                   7367:             $text{'item'} = 'IDs';
                   7368:             $text{'action'} = 'IDs';
                   7369:         }
1.612     raeburn  7370:     }
1.674     bisitz   7371:     $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for [_1], but the $text{'item'} $text{'do'} not exist in the institutional directory.",'<span class="LC_cusr_emph">'.$domdesc.'</span>').'<br />';
1.615     raeburn  7372:     if ($mode eq 'upload') {
                   7373:         if ($checkitem eq 'username') {
                   7374:             $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'}.");
                   7375:         } elsif ($checkitem eq 'id') {
1.674     bisitz   7376:             $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 Student/Employee ID field.");
1.615     raeburn  7377:         }
1.669     raeburn  7378:     } elsif ($mode eq 'selfcreate') {
                   7379:         if ($checkitem eq 'id') {
                   7380:             $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.");
                   7381:         }
1.615     raeburn  7382:     } else {
                   7383:         if ($checkitem eq 'username') {
                   7384:             $response .= &mt("You must choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
                   7385:         } elsif ($checkitem eq 'id') {
                   7386:             $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.");
                   7387:         }
1.612     raeburn  7388:     }
                   7389:     return $response;
1.585     raeburn  7390: }
                   7391: 
1.624     raeburn  7392: sub personal_data_fieldtitles {
                   7393:     my %fieldtitles = &Apache::lonlocal::texthash (
                   7394:                         id => 'Student/Employee ID',
                   7395:                         permanentemail => 'E-mail address',
                   7396:                         lastname => 'Last Name',
                   7397:                         firstname => 'First Name',
                   7398:                         middlename => 'Middle Name',
                   7399:                         generation => 'Generation',
                   7400:                         gen => 'Generation',
1.692.4.2  raeburn  7401:                         inststatus => 'Affiliation',
1.624     raeburn  7402:                    );
                   7403:     return %fieldtitles;
                   7404: }
                   7405: 
1.642     raeburn  7406: sub sorted_inst_types {
                   7407:     my ($dom) = @_;
                   7408:     my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
                   7409:     my $othertitle = &mt('All users');
                   7410:     if ($env{'request.course.id'}) {
1.668     raeburn  7411:         $othertitle  = &mt('Any users');
1.642     raeburn  7412:     }
                   7413:     my @types;
                   7414:     if (ref($order) eq 'ARRAY') {
                   7415:         @types = @{$order};
                   7416:     }
                   7417:     if (@types == 0) {
                   7418:         if (ref($usertypes) eq 'HASH') {
                   7419:             @types = sort(keys(%{$usertypes}));
                   7420:         }
                   7421:     }
                   7422:     if (keys(%{$usertypes}) > 0) {
                   7423:         $othertitle = &mt('Other users');
                   7424:     }
                   7425:     return ($othertitle,$usertypes,\@types);
                   7426: }
                   7427: 
1.645     raeburn  7428: sub get_institutional_codes {
                   7429:     my ($settings,$allcourses,$LC_code) = @_;
                   7430: # Get complete list of course sections to update
                   7431:     my @currsections = ();
                   7432:     my @currxlists = ();
                   7433:     my $coursecode = $$settings{'internal.coursecode'};
                   7434: 
                   7435:     if ($$settings{'internal.sectionnums'} ne '') {
                   7436:         @currsections = split(/,/,$$settings{'internal.sectionnums'});
                   7437:     }
                   7438: 
                   7439:     if ($$settings{'internal.crosslistings'} ne '') {
                   7440:         @currxlists = split(/,/,$$settings{'internal.crosslistings'});
                   7441:     }
                   7442: 
                   7443:     if (@currxlists > 0) {
                   7444:         foreach (@currxlists) {
                   7445:             if (m/^([^:]+):(\w*)$/) {
                   7446:                 unless (grep/^$1$/,@{$allcourses}) {
                   7447:                     push @{$allcourses},$1;
                   7448:                     $$LC_code{$1} = $2;
                   7449:                 }
                   7450:             }
                   7451:         }
                   7452:     }
                   7453:  
                   7454:     if (@currsections > 0) {
                   7455:         foreach (@currsections) {
                   7456:             if (m/^(\w+):(\w*)$/) {
                   7457:                 my $sec = $coursecode.$1;
                   7458:                 my $lc_sec = $2;
                   7459:                 unless (grep/^$sec$/,@{$allcourses}) {
                   7460:                     push @{$allcourses},$sec;
                   7461:                     $$LC_code{$sec} = $lc_sec;
                   7462:                 }
                   7463:             }
                   7464:         }
                   7465:     }
                   7466:     return;
                   7467: }
                   7468: 
1.112     bowersj2 7469: =pod
                   7470: 
1.692.4.2  raeburn  7471: =head1 Slot Helpers
                   7472: 
                   7473: =over 4
                   7474: 
                   7475: =item * sorted_slots()
                   7476: 
                   7477: Sorts an array of slot names in order of slot start time (earliest first).
                   7478: 
                   7479: Inputs:
                   7480: 
                   7481: =over 4
                   7482: 
                   7483: slotsarr  - Reference to array of unsorted slot names.
                   7484: 
                   7485: slots     - Reference to hash of hash, where outer hash keys are slot names.
                   7486: 
                   7487: =back
                   7488: 
                   7489: Returns:
                   7490: 
                   7491: =over 4
                   7492: 
                   7493: sorted   - An array of slot names sorted by the start time of the slot.
                   7494: 
                   7495: =back
                   7496: 
                   7497: =back
                   7498: 
                   7499: =cut
                   7500: 
                   7501: 
                   7502: sub sorted_slots {
                   7503:     my ($slotsarr,$slots) = @_;
                   7504:     my @sorted;
                   7505:     if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
                   7506:         @sorted =
                   7507:             sort {
                   7508:                      if (ref($slots->{$a}) && ref($slots->{$b})) {
                   7509:                          return $slots->{$a}{'starttime'} <=> $slots->{$b}{'starttime'}
                   7510:                      }
                   7511:                      if (ref($slots->{$a})) { return -1;}
                   7512:                      if (ref($slots->{$b})) { return 1;}
                   7513:                      return 0;
                   7514:                  } @{$slotsarr};
                   7515:     }
                   7516:     return @sorted;
                   7517: }
                   7518: 
                   7519: =pod
                   7520: 
1.549     albertel 7521: =head1 HTTP Helpers
                   7522: 
                   7523: =over 4
                   7524: 
1.648     raeburn  7525: =item * &get_unprocessed_cgi($query,$possible_names)
1.112     bowersj2 7526: 
1.258     albertel 7527: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112     bowersj2 7528: $query.  The parameters listed in $possible_names (an array reference),
1.258     albertel 7529: will be set in $env{'form.name'} if they do not already exist.
1.112     bowersj2 7530: 
                   7531: Typically called with $ENV{'QUERY_STRING'} as the first parameter.  
                   7532: $possible_names is an ref to an array of form element names.  As an example:
                   7533: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258     albertel 7534: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112     bowersj2 7535: 
                   7536: =cut
1.1       albertel 7537: 
1.6       albertel 7538: sub get_unprocessed_cgi {
1.25      albertel 7539:   my ($query,$possible_names)= @_;
1.26      matthew  7540:   # $Apache::lonxml::debug=1;
1.356     albertel 7541:   foreach my $pair (split(/&/,$query)) {
                   7542:     my ($name, $value) = split(/=/,$pair);
1.369     www      7543:     $name = &unescape($name);
1.25      albertel 7544:     if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
                   7545:       $value =~ tr/+/ /;
                   7546:       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258     albertel 7547:       unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25      albertel 7548:     }
1.16      harris41 7549:   }
1.6       albertel 7550: }
                   7551: 
1.112     bowersj2 7552: =pod
                   7553: 
1.648     raeburn  7554: =item * &cacheheader() 
1.112     bowersj2 7555: 
                   7556: returns cache-controlling header code
                   7557: 
                   7558: =cut
                   7559: 
1.7       albertel 7560: sub cacheheader {
1.258     albertel 7561:     unless ($env{'request.method'} eq 'GET') { return ''; }
1.216     albertel 7562:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
                   7563:     my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7       albertel 7564:                 <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
                   7565:                 <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216     albertel 7566:     return $output;
1.7       albertel 7567: }
                   7568: 
1.112     bowersj2 7569: =pod
                   7570: 
1.648     raeburn  7571: =item * &no_cache($r) 
1.112     bowersj2 7572: 
                   7573: specifies header code to not have cache
                   7574: 
                   7575: =cut
                   7576: 
1.9       albertel 7577: sub no_cache {
1.216     albertel 7578:     my ($r) = @_;
                   7579:     if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258     albertel 7580: 	$env{'request.method'} ne 'GET') { return ''; }
1.216     albertel 7581:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
                   7582:     $r->no_cache(1);
                   7583:     $r->header_out("Expires" => $date);
                   7584:     $r->header_out("Pragma" => "no-cache");
1.123     www      7585: }
                   7586: 
                   7587: sub content_type {
1.181     albertel 7588:     my ($r,$type,$charset) = @_;
1.299     foxr     7589:     if ($r) {
                   7590: 	#  Note that printout.pl calls this with undef for $r.
                   7591: 	&no_cache($r);
                   7592:     }
1.258     albertel 7593:     if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181     albertel 7594:     unless ($charset) {
                   7595: 	$charset=&Apache::lonlocal::current_encoding;
                   7596:     }
                   7597:     if ($charset) { $type.='; charset='.$charset; }
                   7598:     if ($r) {
                   7599: 	$r->content_type($type);
                   7600:     } else {
                   7601: 	print("Content-type: $type\n\n");
                   7602:     }
1.9       albertel 7603: }
1.25      albertel 7604: 
1.112     bowersj2 7605: =pod
                   7606: 
1.648     raeburn  7607: =item * &add_to_env($name,$value) 
1.112     bowersj2 7608: 
1.258     albertel 7609: adds $name to the %env hash with value
1.112     bowersj2 7610: $value, if $name already exists, the entry is converted to an array
                   7611: reference and $value is added to the array.
                   7612: 
                   7613: =cut
                   7614: 
1.25      albertel 7615: sub add_to_env {
                   7616:   my ($name,$value)=@_;
1.258     albertel 7617:   if (defined($env{$name})) {
                   7618:     if (ref($env{$name})) {
1.25      albertel 7619:       #already have multiple values
1.258     albertel 7620:       push(@{ $env{$name} },$value);
1.25      albertel 7621:     } else {
                   7622:       #first time seeing multiple values, convert hash entry to an arrayref
1.258     albertel 7623:       my $first=$env{$name};
                   7624:       undef($env{$name});
                   7625:       push(@{ $env{$name} },$first,$value);
1.25      albertel 7626:     }
                   7627:   } else {
1.258     albertel 7628:     $env{$name}=$value;
1.25      albertel 7629:   }
1.31      albertel 7630: }
1.149     albertel 7631: 
                   7632: =pod
                   7633: 
1.648     raeburn  7634: =item * &get_env_multiple($name) 
1.149     albertel 7635: 
1.258     albertel 7636: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149     albertel 7637: values may be defined and end up as an array ref.
                   7638: 
                   7639: returns an array of values
                   7640: 
                   7641: =cut
                   7642: 
                   7643: sub get_env_multiple {
                   7644:     my ($name) = @_;
                   7645:     my @values;
1.258     albertel 7646:     if (defined($env{$name})) {
1.149     albertel 7647:         # exists is it an array
1.258     albertel 7648:         if (ref($env{$name})) {
                   7649:             @values=@{ $env{$name} };
1.149     albertel 7650:         } else {
1.258     albertel 7651:             $values[0]=$env{$name};
1.149     albertel 7652:         }
                   7653:     }
                   7654:     return(@values);
                   7655: }
                   7656: 
1.660     raeburn  7657: sub ask_for_embedded_content {
                   7658:     my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
                   7659:     my $upload_output = '
                   7660:    <form name="upload_embedded" action="'.$actionurl.'"
                   7661:                   method="post" enctype="multipart/form-data">';
                   7662:     $upload_output .= $state;
1.661     raeburn  7663:     $upload_output .= '<b>Upload embedded files</b>:<br />'.&start_data_table();
1.660     raeburn  7664: 
                   7665:     my $num = 0;
                   7666:     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%{$allfiles})) {
                   7667:         $upload_output .= &start_data_table_row().
                   7668:             '<td>'.$embed_file.'</td><td>';
                   7669:         if ($args->{'ignore_remote_references'}
                   7670:             && $embed_file =~ m{^\w+://}) {
                   7671:             $upload_output.='<span class="LC_warning">'.&mt("URL points to other server.").'</span>';
                   7672:         } elsif ($args->{'error_on_invalid_names'}
                   7673:             && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
                   7674: 
                   7675:             $upload_output.='<span class="LC_warning">'.&mt("Invalid characters").'</span>';
                   7676: 
                   7677:         } else {
                   7678:             $upload_output .='
1.661     raeburn  7679:            <input name="embedded_item_'.$num.'" type="file" value="" />
1.660     raeburn  7680:            <input name="embedded_orig_'.$num.'" type="hidden" value="'.&escape($embed_file).'" />';
                   7681:             my $attrib = join(':',@{$$allfiles{$embed_file}});
                   7682:             $upload_output .=
                   7683:                 "\n\t\t".
                   7684:                 '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
                   7685:                 $attrib.'" />';
                   7686:             if (exists($$codebase{$embed_file})) {
                   7687:                 $upload_output .=
                   7688:                     "\n\t\t".
                   7689:                     '<input name="codebase_'.$num.'" type="hidden" value="'.
                   7690:                     &escape($$codebase{$embed_file}).'" />';
                   7691:             }
                   7692:         }
                   7693:         $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row();
                   7694:         $num++;
                   7695:     }
                   7696:     $upload_output .= &Apache::loncommon::end_data_table().'<br />
                   7697:    <input type ="hidden" name="number_embedded_items" value="'.$num.'" />
                   7698:    <input type ="submit" value="'.&mt('Upload Listed Files').'" />
                   7699:    '.&mt('(only files for which a location has been provided will be uploaded)').'
                   7700:    </form>';
                   7701:     return $upload_output;
                   7702: }
                   7703: 
1.661     raeburn  7704: sub upload_embedded {
                   7705:     my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
                   7706:         $current_disk_usage) = @_;
                   7707:     my $output;
                   7708:     for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
                   7709:         next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
                   7710:         my $orig_uploaded_filename =
                   7711:             $env{'form.embedded_item_'.$i.'.filename'};
                   7712: 
                   7713:         $env{'form.embedded_orig_'.$i} =
                   7714:             &unescape($env{'form.embedded_orig_'.$i});
                   7715:         my ($path,$fname) =
                   7716:             ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
                   7717:         # no path, whole string is fname
                   7718:         if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
                   7719: 
                   7720:         $path = $env{'form.currentpath'}.$path;
                   7721:         $fname = &Apache::lonnet::clean_filename($fname);
                   7722:         # See if there is anything left
                   7723:         next if ($fname eq '');
                   7724: 
                   7725:         # Check if file already exists as a file or directory.
                   7726:         my ($state,$msg);
                   7727:         if ($context eq 'portfolio') {
                   7728:             my $port_path = $dirpath;
                   7729:             if ($group ne '') {
                   7730:                 $port_path = "groups/$group/$port_path";
                   7731:             }
                   7732:             ($state,$msg) = &check_for_upload($path,$fname,$group,'embedded_item_'.$i,
                   7733:                                               $dir_root,$port_path,$disk_quota,
                   7734:                                               $current_disk_usage,$uname,$udom);
                   7735:             if ($state eq 'will_exceed_quota'
                   7736:                 || $state eq 'file_locked'
                   7737:                 || $state eq 'file_exists' ) {
                   7738:                 $output .= $msg;
                   7739:                 next;
                   7740:             }
                   7741:         } elsif (($context eq 'author') || ($context eq 'testbank')) {
                   7742:             ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
                   7743:             if ($state eq 'exists') {
                   7744:                 $output .= $msg;
                   7745:                 next;
                   7746:             }
                   7747:         }
                   7748:         # Check if extension is valid
                   7749:         if (($fname =~ /\.(\w+)$/) &&
                   7750:             (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
                   7751:             $output .= &mt('Invalid file extension ([_1]) - reserved for LONCAPA use - rename the file with a different extension and re-upload. ',$1);
                   7752:             next;
                   7753:         } elsif (($fname =~ /\.(\w+)$/) &&
                   7754:                  (!defined(&Apache::loncommon::fileembstyle($1)))) {
                   7755:             $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1);
                   7756:             next;
                   7757:         } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
                   7758:             $output .= &mt('File name not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2);
                   7759:             next;
                   7760:         }
                   7761: 
                   7762:         $env{'form.embedded_item_'.$i.'.filename'}=$fname;
                   7763:         if ($context eq 'portfolio') {
                   7764:             my $result=
                   7765:                 &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
                   7766:                                                 $dirpath.$path);
                   7767:             if ($result !~ m|^/uploaded/|) {
                   7768:                 $output .= '<span class="LC_error">'
                   7769:                       .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
                   7770:                            ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
                   7771:                       .'</span><br />';
                   7772:                 next;
                   7773:             } else {
                   7774:                 $output .= '<p>'.&mt('Uploaded [_1]','<span class="LC_filename">'.
                   7775:                            $path.$fname.'</span>').'</p>';     
                   7776:             }
                   7777:         } else {
                   7778: # Save the file
                   7779:             my $target = $env{'form.embedded_item_'.$i};
                   7780:             my $fullpath = $dir_root.$dirpath.'/'.$path;
                   7781:             my $dest = $fullpath.$fname;
                   7782:             my $url = $url_root.$dirpath.'/'.$path.$fname;
                   7783:             my @parts=split(/\//,$fullpath);
                   7784:             my $count;
                   7785:             my $filepath = $dir_root;
                   7786:             for ($count=4;$count<=$#parts;$count++) {
                   7787:                 $filepath .= "/$parts[$count]";
                   7788:                 if ((-e $filepath)!=1) {
                   7789:                     mkdir($filepath,0770);
                   7790:                 }
                   7791:             }
                   7792:             my $fh;
                   7793:             if (!open($fh,'>'.$dest)) {
                   7794:                 &Apache::lonnet::logthis('Failed to create '.$dest);
                   7795:                 $output .= '<span class="LC_error">'.
                   7796:                            &mt('An error occurred while trying to upload [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
                   7797:                            '</span><br />';
                   7798:             } else {
                   7799:                 if (!print $fh $env{'form.embedded_item_'.$i}) {
                   7800:                     &Apache::lonnet::logthis('Failed to write to '.$dest);
                   7801:                     $output .= '<span class="LC_error">'.
                   7802:                               &mt('An error occurred while writing the file [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
                   7803:                               '</span><br />';
                   7804:                 } else {
                   7805:                     if ($context eq 'testbank') {
                   7806:                         $output .= &mt('Embedded file uploaded successfully:').
                   7807:                                    '&nbsp;<a href="'.$url.'">'.
                   7808:                                    $orig_uploaded_filename.'</a><br />';
                   7809:                     } else {
                   7810:                         $output .= '<font size="+2">'.
                   7811:                                    &mt('View embedded file: [_1]','<a href="'.$url.'">'.
                   7812:                                    $orig_uploaded_filename.'</a>').'</font><br />';
                   7813:                     }
                   7814:                 }
                   7815:                 close($fh);
                   7816:             }
                   7817:         }
                   7818:     }
                   7819:     return $output;
                   7820: }
                   7821: 
                   7822: sub check_for_existing {
                   7823:     my ($path,$fname,$element) = @_;
                   7824:     my ($state,$msg);
                   7825:     if (-d $path.'/'.$fname) {
                   7826:         $state = 'exists';
                   7827:         $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   7828:     } elsif (-e $path.'/'.$fname) {
                   7829:         $state = 'exists';
                   7830:         $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   7831:     }
                   7832:     if ($state eq 'exists') {
                   7833:         $msg = '<span class="LC_error">'.$msg.'</span><br />';
                   7834:     }
                   7835:     return ($state,$msg);
                   7836: }
                   7837: 
                   7838: sub check_for_upload {
                   7839:     my ($path,$fname,$group,$element,$portfolio_root,$port_path,
                   7840:         $disk_quota,$current_disk_usage,$uname,$udom) = @_;
                   7841:     my $filesize = (length($env{'form.'.$element})) / 1000; #express in k (1024?)
                   7842:     my $getpropath = 1;
                   7843:     my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,
                   7844:                                             $getpropath);
                   7845:     my $found_file = 0;
                   7846:     my $locked_file = 0;
                   7847:     foreach my $line (@dir_list) {
                   7848:         my ($file_name)=split(/\&/,$line,2);
                   7849:         if ($file_name eq $fname){
                   7850:             $file_name = $path.$file_name;
                   7851:             if ($group ne '') {
                   7852:                 $file_name = $group.$file_name;
                   7853:             }
                   7854:             $found_file = 1;
                   7855:             if (&Apache::lonnet::is_locked($file_name,$udom,$uname) eq 'true') {
                   7856:                 $locked_file = 1;
                   7857:             }
                   7858:         }
                   7859:     }
                   7860:     if (($current_disk_usage + $filesize) > $disk_quota){
                   7861:         my $msg = '<span class="LC_error">'.
                   7862:                 &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</span>'.
                   7863:                   '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage);
                   7864:         return ('will_exceed_quota',$msg);
                   7865:     } elsif ($found_file) {
                   7866:         if ($locked_file) {
                   7867:             my $msg = '<span class="LC_error">';
                   7868:             $msg .= &mt('Unable to upload [_1]. A locked file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>','<span class="LC_filename">'.$port_path.$env{'form.currentpath'}.'</span>');
                   7869:             $msg .= '</span><br />';
                   7870:             $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
                   7871:             return ('file_locked',$msg);
                   7872:         } else {
                   7873:             my $msg = '<span class="LC_error">';
                   7874:             $msg .= &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$port_path.$env{'form.currentpath'});
                   7875:             $msg .= '</span>';
                   7876:             $msg .= '<br />';
                   7877:             $msg .= &mt('To upload, rename or delete existing [_1] in [_2].','<span class="LC_filename">'.$fname.'</span>', $port_path.$env{'form.currentpath'});
                   7878:             return ('file_exists',$msg);
                   7879:         }
                   7880:     }
                   7881: }
                   7882: 
1.31      albertel 7883: 
1.41      ng       7884: =pod
1.45      matthew  7885: 
1.464     albertel 7886: =back
1.41      ng       7887: 
1.112     bowersj2 7888: =head1 CSV Upload/Handling functions
1.38      albertel 7889: 
1.41      ng       7890: =over 4
                   7891: 
1.648     raeburn  7892: =item * &upfile_store($r)
1.41      ng       7893: 
                   7894: Store uploaded file, $r should be the HTTP Request object,
1.258     albertel 7895: needs $env{'form.upfile'}
1.41      ng       7896: returns $datatoken to be put into hidden field
                   7897: 
                   7898: =cut
1.31      albertel 7899: 
                   7900: sub upfile_store {
                   7901:     my $r=shift;
1.258     albertel 7902:     $env{'form.upfile'}=~s/\r/\n/gs;
                   7903:     $env{'form.upfile'}=~s/\f/\n/gs;
                   7904:     $env{'form.upfile'}=~s/\n+/\n/gs;
                   7905:     $env{'form.upfile'}=~s/\n+$//gs;
1.31      albertel 7906: 
1.258     albertel 7907:     my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
                   7908: 	'_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31      albertel 7909:     {
1.158     raeburn  7910:         my $datafile = $r->dir_config('lonDaemons').
                   7911:                            '/tmp/'.$datatoken.'.tmp';
                   7912:         if ( open(my $fh,">$datafile") ) {
1.258     albertel 7913:             print $fh $env{'form.upfile'};
1.158     raeburn  7914:             close($fh);
                   7915:         }
1.31      albertel 7916:     }
                   7917:     return $datatoken;
                   7918: }
                   7919: 
1.56      matthew  7920: =pod
                   7921: 
1.648     raeburn  7922: =item * &load_tmp_file($r)
1.41      ng       7923: 
                   7924: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258     albertel 7925: needs $env{'form.datatoken'},
                   7926: sets $env{'form.upfile'} to the contents of the file
1.41      ng       7927: 
                   7928: =cut
1.31      albertel 7929: 
                   7930: sub load_tmp_file {
                   7931:     my $r=shift;
                   7932:     my @studentdata=();
                   7933:     {
1.158     raeburn  7934:         my $studentfile = $r->dir_config('lonDaemons').
1.258     albertel 7935:                               '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158     raeburn  7936:         if ( open(my $fh,"<$studentfile") ) {
                   7937:             @studentdata=<$fh>;
                   7938:             close($fh);
                   7939:         }
1.31      albertel 7940:     }
1.258     albertel 7941:     $env{'form.upfile'}=join('',@studentdata);
1.31      albertel 7942: }
                   7943: 
1.56      matthew  7944: =pod
                   7945: 
1.648     raeburn  7946: =item * &upfile_record_sep()
1.41      ng       7947: 
                   7948: Separate uploaded file into records
                   7949: returns array of records,
1.258     albertel 7950: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41      ng       7951: 
                   7952: =cut
1.31      albertel 7953: 
                   7954: sub upfile_record_sep {
1.258     albertel 7955:     if ($env{'form.upfiletype'} eq 'xml') {
1.31      albertel 7956:     } else {
1.248     albertel 7957: 	my @records;
1.258     albertel 7958: 	foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248     albertel 7959: 	    if ($line=~/^\s*$/) { next; }
                   7960: 	    push(@records,$line);
                   7961: 	}
                   7962: 	return @records;
1.31      albertel 7963:     }
                   7964: }
                   7965: 
1.56      matthew  7966: =pod
                   7967: 
1.648     raeburn  7968: =item * &record_sep($record)
1.41      ng       7969: 
1.258     albertel 7970: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41      ng       7971: 
                   7972: =cut
                   7973: 
1.263     www      7974: sub takeleft {
                   7975:     my $index=shift;
                   7976:     return substr('0000'.$index,-4,4);
                   7977: }
                   7978: 
1.31      albertel 7979: sub record_sep {
                   7980:     my $record=shift;
                   7981:     my %components=();
1.258     albertel 7982:     if ($env{'form.upfiletype'} eq 'xml') {
                   7983:     } elsif ($env{'form.upfiletype'} eq 'space') {
1.31      albertel 7984:         my $i=0;
1.356     albertel 7985:         foreach my $field (split(/\s+/,$record)) {
1.31      albertel 7986:             $field=~s/^(\"|\')//;
                   7987:             $field=~s/(\"|\')$//;
1.263     www      7988:             $components{&takeleft($i)}=$field;
1.31      albertel 7989:             $i++;
                   7990:         }
1.258     albertel 7991:     } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31      albertel 7992:         my $i=0;
1.356     albertel 7993:         foreach my $field (split(/\t/,$record)) {
1.31      albertel 7994:             $field=~s/^(\"|\')//;
                   7995:             $field=~s/(\"|\')$//;
1.263     www      7996:             $components{&takeleft($i)}=$field;
1.31      albertel 7997:             $i++;
                   7998:         }
                   7999:     } else {
1.561     www      8000:         my $separator=',';
1.480     banghart 8001:         if ($env{'form.upfiletype'} eq 'semisv') {
1.561     www      8002:             $separator=';';
1.480     banghart 8003:         }
1.31      albertel 8004:         my $i=0;
1.561     www      8005: # the character we are looking for to indicate the end of a quote or a record 
                   8006:         my $looking_for=$separator;
                   8007: # do not add the characters to the fields
                   8008:         my $ignore=0;
                   8009: # we just encountered a separator (or the beginning of the record)
                   8010:         my $just_found_separator=1;
                   8011: # store the field we are working on here
                   8012:         my $field='';
                   8013: # work our way through all characters in record
                   8014:         foreach my $character ($record=~/(.)/g) {
                   8015:             if ($character eq $looking_for) {
                   8016:                if ($character ne $separator) {
                   8017: # Found the end of a quote, again looking for separator
                   8018:                   $looking_for=$separator;
                   8019:                   $ignore=1;
                   8020:                } else {
                   8021: # Found a separator, store away what we got
                   8022:                   $components{&takeleft($i)}=$field;
                   8023: 	          $i++;
                   8024:                   $just_found_separator=1;
                   8025:                   $ignore=0;
                   8026:                   $field='';
                   8027:                }
                   8028:                next;
                   8029:             }
                   8030: # single or double quotation marks after a separator indicate beginning of a quote
                   8031: # we are now looking for the end of the quote and need to ignore separators
                   8032:             if ((($character eq '"') || ($character eq "'")) && ($just_found_separator))  {
                   8033:                $looking_for=$character;
                   8034:                next;
                   8035:             }
                   8036: # ignore would be true after we reached the end of a quote
                   8037:             if ($ignore) { next; }
                   8038:             if (($just_found_separator) && ($character=~/\s/)) { next; }
                   8039:             $field.=$character;
                   8040:             $just_found_separator=0; 
1.31      albertel 8041:         }
1.561     www      8042: # catch the very last entry, since we never encountered the separator
                   8043:         $components{&takeleft($i)}=$field;
1.31      albertel 8044:     }
                   8045:     return %components;
                   8046: }
                   8047: 
1.144     matthew  8048: ######################################################
                   8049: ######################################################
                   8050: 
1.56      matthew  8051: =pod
                   8052: 
1.648     raeburn  8053: =item * &upfile_select_html()
1.41      ng       8054: 
1.144     matthew  8055: Return HTML code to select a file from the users machine and specify 
                   8056: the file type.
1.41      ng       8057: 
                   8058: =cut
                   8059: 
1.144     matthew  8060: ######################################################
                   8061: ######################################################
1.31      albertel 8062: sub upfile_select_html {
1.144     matthew  8063:     my %Types = (
                   8064:                  csv   => &mt('CSV (comma separated values, spreadsheet)'),
1.480     banghart 8065:                  semisv => &mt('Semicolon separated values'),
1.144     matthew  8066:                  space => &mt('Space separated'),
                   8067:                  tab   => &mt('Tabulator separated'),
                   8068: #                 xml   => &mt('HTML/XML'),
                   8069:                  );
                   8070:     my $Str = '<input type="file" name="upfile" size="50" />'.
1.692.4.2  raeburn  8071:         '<br />'.&mt('Type').': <select name="upfiletype">';
1.144     matthew  8072:     foreach my $type (sort(keys(%Types))) {
                   8073:         $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
                   8074:     }
                   8075:     $Str .= "</select>\n";
                   8076:     return $Str;
1.31      albertel 8077: }
                   8078: 
1.301     albertel 8079: sub get_samples {
                   8080:     my ($records,$toget) = @_;
                   8081:     my @samples=({});
                   8082:     my $got=0;
                   8083:     foreach my $rec (@$records) {
                   8084: 	my %temp = &record_sep($rec);
                   8085: 	if (! grep(/\S/, values(%temp))) { next; }
                   8086: 	if (%temp) {
                   8087: 	    $samples[$got]=\%temp;
                   8088: 	    $got++;
                   8089: 	    if ($got == $toget) { last; }
                   8090: 	}
                   8091:     }
                   8092:     return \@samples;
                   8093: }
                   8094: 
1.144     matthew  8095: ######################################################
                   8096: ######################################################
                   8097: 
1.56      matthew  8098: =pod
                   8099: 
1.648     raeburn  8100: =item * &csv_print_samples($r,$records)
1.41      ng       8101: 
                   8102: Prints a table of sample values from each column uploaded $r is an
                   8103: Apache Request ref, $records is an arrayref from
                   8104: &Apache::loncommon::upfile_record_sep
                   8105: 
                   8106: =cut
                   8107: 
1.144     matthew  8108: ######################################################
                   8109: ######################################################
1.31      albertel 8110: sub csv_print_samples {
                   8111:     my ($r,$records) = @_;
1.662     bisitz   8112:     my $samples = &get_samples($records,5);
1.301     albertel 8113: 
1.594     raeburn  8114:     $r->print(&mt('Samples').'<br />'.&start_data_table().
                   8115:               &start_data_table_header_row());
1.356     albertel 8116:     foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { 
1.692.4.6  raeburn  8117:         $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>');
                   8118:     }
1.594     raeburn  8119:     $r->print(&end_data_table_header_row());
1.301     albertel 8120:     foreach my $hash (@$samples) {
1.594     raeburn  8121: 	$r->print(&start_data_table_row());
1.356     albertel 8122: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31      albertel 8123: 	    $r->print('<td>');
1.356     albertel 8124: 	    if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31      albertel 8125: 	    $r->print('</td>');
                   8126: 	}
1.594     raeburn  8127: 	$r->print(&end_data_table_row());
1.31      albertel 8128:     }
1.594     raeburn  8129:     $r->print(&end_data_table().'<br />'."\n");
1.31      albertel 8130: }
                   8131: 
1.144     matthew  8132: ######################################################
                   8133: ######################################################
                   8134: 
1.56      matthew  8135: =pod
                   8136: 
1.648     raeburn  8137: =item * &csv_print_select_table($r,$records,$d)
1.41      ng       8138: 
                   8139: Prints a table to create associations between values and table columns.
1.144     matthew  8140: 
1.41      ng       8141: $r is an Apache Request ref,
                   8142: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174     matthew  8143: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41      ng       8144: 
                   8145: =cut
                   8146: 
1.144     matthew  8147: ######################################################
                   8148: ######################################################
1.31      albertel 8149: sub csv_print_select_table {
                   8150:     my ($r,$records,$d) = @_;
1.301     albertel 8151:     my $i=0;
                   8152:     my $samples = &get_samples($records,1);
1.144     matthew  8153:     $r->print(&mt('Associate columns with student attributes.')."\n".
1.594     raeburn  8154: 	      &start_data_table().&start_data_table_header_row().
1.144     matthew  8155:               '<th>'.&mt('Attribute').'</th>'.
1.594     raeburn  8156:               '<th>'.&mt('Column').'</th>'.
                   8157:               &end_data_table_header_row()."\n");
1.356     albertel 8158:     foreach my $array_ref (@$d) {
                   8159: 	my ($value,$display,$defaultcol)=@{ $array_ref };
1.689     bisitz   8160: 	$r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31      albertel 8161: 
1.692.4.8  raeburn  8162: 	$r->print('<td><select name"f'.$i.'"'.
1.32      matthew  8163: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 8164: 	$r->print('<option value="none"></option>');
1.356     albertel 8165: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
                   8166: 	    $r->print('<option value="'.$sample.'"'.
                   8167:                       ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662     bisitz   8168:                       '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31      albertel 8169: 	}
1.594     raeburn  8170: 	$r->print('</select></td>'.&end_data_table_row()."\n");
1.31      albertel 8171: 	$i++;
                   8172:     }
1.594     raeburn  8173:     $r->print(&end_data_table());
1.31      albertel 8174:     $i--;
                   8175:     return $i;
                   8176: }
1.56      matthew  8177: 
1.144     matthew  8178: ######################################################
                   8179: ######################################################
                   8180: 
1.56      matthew  8181: =pod
1.31      albertel 8182: 
1.648     raeburn  8183: =item * &csv_samples_select_table($r,$records,$d)
1.41      ng       8184: 
                   8185: Prints a table of sample values from the upload and can make associate samples to internal names.
                   8186: 
                   8187: $r is an Apache Request ref,
                   8188: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
                   8189: $d is an array of 2 element arrays (internal name, displayed name)
                   8190: 
                   8191: =cut
                   8192: 
1.144     matthew  8193: ######################################################
                   8194: ######################################################
1.31      albertel 8195: sub csv_samples_select_table {
                   8196:     my ($r,$records,$d) = @_;
                   8197:     my $i=0;
1.144     matthew  8198:     #
1.662     bisitz   8199:     my $max_samples = 5;
                   8200:     my $samples = &get_samples($records,$max_samples);
1.594     raeburn  8201:     $r->print(&start_data_table().
                   8202:               &start_data_table_header_row().'<th>'.
                   8203:               &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
                   8204:               &end_data_table_header_row());
1.301     albertel 8205: 
                   8206:     foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594     raeburn  8207: 	$r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32      matthew  8208: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.301     albertel 8209: 	foreach my $option (@$d) {
                   8210: 	    my ($value,$display,$defaultcol)=@{ $option };
1.174     matthew  8211: 	    $r->print('<option value="'.$value.'"'.
1.253     albertel 8212:                       ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174     matthew  8213:                       $display.'</option>');
1.31      albertel 8214: 	}
                   8215: 	$r->print('</select></td><td>');
1.662     bisitz   8216: 	foreach my $line (0..($max_samples-1)) {
1.301     albertel 8217: 	    if (defined($samples->[$line]{$key})) { 
                   8218: 		$r->print($samples->[$line]{$key}."<br />\n"); 
                   8219: 	    }
                   8220: 	}
1.594     raeburn  8221: 	$r->print('</td>'.&end_data_table_row());
1.31      albertel 8222: 	$i++;
                   8223:     }
1.594     raeburn  8224:     $r->print(&end_data_table());
1.31      albertel 8225:     $i--;
                   8226:     return($i);
1.115     matthew  8227: }
                   8228: 
1.144     matthew  8229: ######################################################
                   8230: ######################################################
                   8231: 
1.115     matthew  8232: =pod
                   8233: 
1.648     raeburn  8234: =item * &clean_excel_name($name)
1.115     matthew  8235: 
                   8236: Returns a replacement for $name which does not contain any illegal characters.
                   8237: 
                   8238: =cut
                   8239: 
1.144     matthew  8240: ######################################################
                   8241: ######################################################
1.115     matthew  8242: sub clean_excel_name {
                   8243:     my ($name) = @_;
                   8244:     $name =~ s/[:\*\?\/\\]//g;
                   8245:     if (length($name) > 31) {
                   8246:         $name = substr($name,0,31);
                   8247:     }
                   8248:     return $name;
1.25      albertel 8249: }
1.84      albertel 8250: 
1.85      albertel 8251: =pod
                   8252: 
1.648     raeburn  8253: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85      albertel 8254: 
                   8255: Returns either 1 or undef
                   8256: 
                   8257: 1 if the part is to be hidden, undef if it is to be shown
                   8258: 
                   8259: Arguments are:
                   8260: 
                   8261: $id the id of the part to be checked
                   8262: $symb, optional the symb of the resource to check
                   8263: $udom, optional the domain of the user to check for
                   8264: $uname, optional the username of the user to check for
                   8265: 
                   8266: =cut
1.84      albertel 8267: 
                   8268: sub check_if_partid_hidden {
                   8269:     my ($id,$symb,$udom,$uname) = @_;
1.133     albertel 8270:     my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84      albertel 8271: 					 $symb,$udom,$uname);
1.141     albertel 8272:     my $truth=1;
                   8273:     #if the string starts with !, then the list is the list to show not hide
                   8274:     if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84      albertel 8275:     my @hiddenlist=split(/,/,$hiddenparts);
                   8276:     foreach my $checkid (@hiddenlist) {
1.141     albertel 8277: 	if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84      albertel 8278:     }
1.141     albertel 8279:     return !$truth;
1.84      albertel 8280: }
1.127     matthew  8281: 
1.138     matthew  8282: 
                   8283: ############################################################
                   8284: ############################################################
                   8285: 
                   8286: =pod
                   8287: 
1.157     matthew  8288: =back 
                   8289: 
1.138     matthew  8290: =head1 cgi-bin script and graphing routines
                   8291: 
1.157     matthew  8292: =over 4
                   8293: 
1.648     raeburn  8294: =item * &get_cgi_id()
1.138     matthew  8295: 
                   8296: Inputs: none
                   8297: 
                   8298: Returns an id which can be used to pass environment variables
                   8299: to various cgi-bin scripts.  These environment variables will
                   8300: be removed from the users environment after a given time by
                   8301: the routine &Apache::lonnet::transfer_profile_to_env.
                   8302: 
                   8303: =cut
                   8304: 
                   8305: ############################################################
                   8306: ############################################################
1.152     albertel 8307: my $uniq=0;
1.136     matthew  8308: sub get_cgi_id {
1.154     albertel 8309:     $uniq=($uniq+1)%100000;
1.280     albertel 8310:     return (time.'_'.$$.'_'.$uniq);
1.136     matthew  8311: }
                   8312: 
1.127     matthew  8313: ############################################################
                   8314: ############################################################
                   8315: 
                   8316: =pod
                   8317: 
1.648     raeburn  8318: =item * &DrawBarGraph()
1.127     matthew  8319: 
1.138     matthew  8320: Facilitates the plotting of data in a (stacked) bar graph.
                   8321: Puts plot definition data into the users environment in order for 
                   8322: graph.png to plot it.  Returns an <img> tag for the plot.
                   8323: The bars on the plot are labeled '1','2',...,'n'.
                   8324: 
                   8325: Inputs:
                   8326: 
                   8327: =over 4
                   8328: 
                   8329: =item $Title: string, the title of the plot
                   8330: 
                   8331: =item $xlabel: string, text describing the X-axis of the plot
                   8332: 
                   8333: =item $ylabel: string, text describing the Y-axis of the plot
                   8334: 
                   8335: =item $Max: scalar, the maximum Y value to use in the plot
                   8336: If $Max is < any data point, the graph will not be rendered.
                   8337: 
1.140     matthew  8338: =item $colors: array ref holding the colors to be used for the data sets when
1.138     matthew  8339: they are plotted.  If undefined, default values will be used.
                   8340: 
1.178     matthew  8341: =item $labels: array ref holding the labels to use on the x-axis for the bars.
                   8342: 
1.138     matthew  8343: =item @Values: An array of array references.  Each array reference holds data
                   8344: to be plotted in a stacked bar chart.
                   8345: 
1.239     matthew  8346: =item If the final element of @Values is a hash reference the key/value
                   8347: pairs will be added to the graph definition.
                   8348: 
1.138     matthew  8349: =back
                   8350: 
                   8351: Returns:
                   8352: 
                   8353: An <img> tag which references graph.png and the appropriate identifying
                   8354: information for the plot.
                   8355: 
1.127     matthew  8356: =cut
                   8357: 
                   8358: ############################################################
                   8359: ############################################################
1.134     matthew  8360: sub DrawBarGraph {
1.178     matthew  8361:     my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134     matthew  8362:     #
                   8363:     if (! defined($colors)) {
                   8364:         $colors = ['#33ff00', 
                   8365:                   '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
                   8366:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                   8367:                   ]; 
                   8368:     }
1.228     matthew  8369:     my $extra_settings = {};
                   8370:     if (ref($Values[-1]) eq 'HASH') {
                   8371:         $extra_settings = pop(@Values);
                   8372:     }
1.127     matthew  8373:     #
1.136     matthew  8374:     my $identifier = &get_cgi_id();
                   8375:     my $id = 'cgi.'.$identifier;        
1.129     matthew  8376:     if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127     matthew  8377:         return '';
                   8378:     }
1.225     matthew  8379:     #
                   8380:     my @Labels;
                   8381:     if (defined($labels)) {
                   8382:         @Labels = @$labels;
                   8383:     } else {
                   8384:         for (my $i=0;$i<@{$Values[0]};$i++) {
                   8385:             push (@Labels,$i+1);
                   8386:         }
                   8387:     }
                   8388:     #
1.129     matthew  8389:     my $NumBars = scalar(@{$Values[0]});
1.225     matthew  8390:     if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129     matthew  8391:     my %ValuesHash;
                   8392:     my $NumSets=1;
                   8393:     foreach my $array (@Values) {
                   8394:         next if (! ref($array));
1.136     matthew  8395:         $ValuesHash{$id.'.data.'.$NumSets++} = 
1.132     matthew  8396:             join(',',@$array);
1.129     matthew  8397:     }
1.127     matthew  8398:     #
1.136     matthew  8399:     my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225     matthew  8400:     if ($NumBars < 3) {
                   8401:         $width = 120+$NumBars*32;
1.220     matthew  8402:         $xskip = 1;
1.225     matthew  8403:         $bar_width = 30;
                   8404:     } elsif ($NumBars < 5) {
                   8405:         $width = 120+$NumBars*20;
                   8406:         $xskip = 1;
                   8407:         $bar_width = 20;
1.220     matthew  8408:     } elsif ($NumBars < 10) {
1.136     matthew  8409:         $width = 120+$NumBars*15;
                   8410:         $xskip = 1;
                   8411:         $bar_width = 15;
                   8412:     } elsif ($NumBars <= 25) {
                   8413:         $width = 120+$NumBars*11;
                   8414:         $xskip = 5;
                   8415:         $bar_width = 8;
                   8416:     } elsif ($NumBars <= 50) {
                   8417:         $width = 120+$NumBars*8;
                   8418:         $xskip = 5;
                   8419:         $bar_width = 4;
                   8420:     } else {
                   8421:         $width = 120+$NumBars*8;
                   8422:         $xskip = 5;
                   8423:         $bar_width = 4;
                   8424:     }
                   8425:     #
1.137     matthew  8426:     $Max = 1 if ($Max < 1);
                   8427:     if ( int($Max) < $Max ) {
                   8428:         $Max++;
                   8429:         $Max = int($Max);
                   8430:     }
1.127     matthew  8431:     $Title  = '' if (! defined($Title));
                   8432:     $xlabel = '' if (! defined($xlabel));
                   8433:     $ylabel = '' if (! defined($ylabel));
1.369     www      8434:     $ValuesHash{$id.'.title'}    = &escape($Title);
                   8435:     $ValuesHash{$id.'.xlabel'}   = &escape($xlabel);
                   8436:     $ValuesHash{$id.'.ylabel'}   = &escape($ylabel);
1.137     matthew  8437:     $ValuesHash{$id.'.y_max_value'} = $Max;
1.136     matthew  8438:     $ValuesHash{$id.'.NumBars'}  = $NumBars;
                   8439:     $ValuesHash{$id.'.NumSets'}  = $NumSets;
                   8440:     $ValuesHash{$id.'.PlotType'} = 'bar';
                   8441:     $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   8442:     $ValuesHash{$id.'.height'}   = $height;
                   8443:     $ValuesHash{$id.'.width'}    = $width;
                   8444:     $ValuesHash{$id.'.xskip'}    = $xskip;
                   8445:     $ValuesHash{$id.'.bar_width'} = $bar_width;
                   8446:     $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127     matthew  8447:     #
1.228     matthew  8448:     # Deal with other parameters
                   8449:     while (my ($key,$value) = each(%$extra_settings)) {
                   8450:         $ValuesHash{$id.'.'.$key} = $value;
                   8451:     }
                   8452:     #
1.646     raeburn  8453:     &Apache::lonnet::appenv(\%ValuesHash);
1.137     matthew  8454:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   8455: }
                   8456: 
                   8457: ############################################################
                   8458: ############################################################
                   8459: 
                   8460: =pod
                   8461: 
1.648     raeburn  8462: =item * &DrawXYGraph()
1.137     matthew  8463: 
1.138     matthew  8464: Facilitates the plotting of data in an XY graph.
                   8465: Puts plot definition data into the users environment in order for 
                   8466: graph.png to plot it.  Returns an <img> tag for the plot.
                   8467: 
                   8468: Inputs:
                   8469: 
                   8470: =over 4
                   8471: 
                   8472: =item $Title: string, the title of the plot
                   8473: 
                   8474: =item $xlabel: string, text describing the X-axis of the plot
                   8475: 
                   8476: =item $ylabel: string, text describing the Y-axis of the plot
                   8477: 
                   8478: =item $Max: scalar, the maximum Y value to use in the plot
                   8479: If $Max is < any data point, the graph will not be rendered.
                   8480: 
                   8481: =item $colors: Array ref containing the hex color codes for the data to be 
                   8482: plotted in.  If undefined, default values will be used.
                   8483: 
                   8484: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   8485: 
                   8486: =item $Ydata: Array ref containing Array refs.  
1.185     www      8487: Each of the contained arrays will be plotted as a separate curve.
1.138     matthew  8488: 
                   8489: =item %Values: hash indicating or overriding any default values which are 
                   8490: passed to graph.png.  
                   8491: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   8492: 
                   8493: =back
                   8494: 
                   8495: Returns:
                   8496: 
                   8497: An <img> tag which references graph.png and the appropriate identifying
                   8498: information for the plot.
                   8499: 
1.137     matthew  8500: =cut
                   8501: 
                   8502: ############################################################
                   8503: ############################################################
                   8504: sub DrawXYGraph {
                   8505:     my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
                   8506:     #
                   8507:     # Create the identifier for the graph
                   8508:     my $identifier = &get_cgi_id();
                   8509:     my $id = 'cgi.'.$identifier;
                   8510:     #
                   8511:     $Title  = '' if (! defined($Title));
                   8512:     $xlabel = '' if (! defined($xlabel));
                   8513:     $ylabel = '' if (! defined($ylabel));
                   8514:     my %ValuesHash = 
                   8515:         (
1.369     www      8516:          $id.'.title'  => &escape($Title),
                   8517:          $id.'.xlabel' => &escape($xlabel),
                   8518:          $id.'.ylabel' => &escape($ylabel),
1.137     matthew  8519:          $id.'.y_max_value'=> $Max,
                   8520:          $id.'.labels'     => join(',',@$Xlabels),
                   8521:          $id.'.PlotType'   => 'XY',
                   8522:          );
                   8523:     #
                   8524:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   8525:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   8526:     }
                   8527:     #
                   8528:     if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
                   8529:         return '';
                   8530:     }
                   8531:     my $NumSets=1;
1.138     matthew  8532:     foreach my $array (@{$Ydata}){
1.137     matthew  8533:         next if (! ref($array));
                   8534:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
                   8535:     }
1.138     matthew  8536:     $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137     matthew  8537:     #
                   8538:     # Deal with other parameters
                   8539:     while (my ($key,$value) = each(%Values)) {
                   8540:         $ValuesHash{$id.'.'.$key} = $value;
1.127     matthew  8541:     }
                   8542:     #
1.646     raeburn  8543:     &Apache::lonnet::appenv(\%ValuesHash);
1.136     matthew  8544:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   8545: }
                   8546: 
                   8547: ############################################################
                   8548: ############################################################
                   8549: 
                   8550: =pod
                   8551: 
1.648     raeburn  8552: =item * &DrawXYYGraph()
1.138     matthew  8553: 
                   8554: Facilitates the plotting of data in an XY graph with two Y axes.
                   8555: Puts plot definition data into the users environment in order for 
                   8556: graph.png to plot it.  Returns an <img> tag for the plot.
                   8557: 
                   8558: Inputs:
                   8559: 
                   8560: =over 4
                   8561: 
                   8562: =item $Title: string, the title of the plot
                   8563: 
                   8564: =item $xlabel: string, text describing the X-axis of the plot
                   8565: 
                   8566: =item $ylabel: string, text describing the Y-axis of the plot
                   8567: 
                   8568: =item $colors: Array ref containing the hex color codes for the data to be 
                   8569: plotted in.  If undefined, default values will be used.
                   8570: 
                   8571: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   8572: 
                   8573: =item $Ydata1: The first data set
                   8574: 
                   8575: =item $Min1: The minimum value of the left Y-axis
                   8576: 
                   8577: =item $Max1: The maximum value of the left Y-axis
                   8578: 
                   8579: =item $Ydata2: The second data set
                   8580: 
                   8581: =item $Min2: The minimum value of the right Y-axis
                   8582: 
                   8583: =item $Max2: The maximum value of the left Y-axis
                   8584: 
                   8585: =item %Values: hash indicating or overriding any default values which are 
                   8586: passed to graph.png.  
                   8587: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   8588: 
                   8589: =back
                   8590: 
                   8591: Returns:
                   8592: 
                   8593: An <img> tag which references graph.png and the appropriate identifying
                   8594: information for the plot.
1.136     matthew  8595: 
                   8596: =cut
                   8597: 
                   8598: ############################################################
                   8599: ############################################################
1.137     matthew  8600: sub DrawXYYGraph {
                   8601:     my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
                   8602:                                         $Ydata2,$Min2,$Max2,%Values)=@_;
1.136     matthew  8603:     #
                   8604:     # Create the identifier for the graph
                   8605:     my $identifier = &get_cgi_id();
                   8606:     my $id = 'cgi.'.$identifier;
                   8607:     #
                   8608:     $Title  = '' if (! defined($Title));
                   8609:     $xlabel = '' if (! defined($xlabel));
                   8610:     $ylabel = '' if (! defined($ylabel));
                   8611:     my %ValuesHash = 
                   8612:         (
1.369     www      8613:          $id.'.title'  => &escape($Title),
                   8614:          $id.'.xlabel' => &escape($xlabel),
                   8615:          $id.'.ylabel' => &escape($ylabel),
1.136     matthew  8616:          $id.'.labels' => join(',',@$Xlabels),
                   8617:          $id.'.PlotType' => 'XY',
                   8618:          $id.'.NumSets' => 2,
1.137     matthew  8619:          $id.'.two_axes' => 1,
                   8620:          $id.'.y1_max_value' => $Max1,
                   8621:          $id.'.y1_min_value' => $Min1,
                   8622:          $id.'.y2_max_value' => $Max2,
                   8623:          $id.'.y2_min_value' => $Min2,
1.136     matthew  8624:          );
                   8625:     #
1.137     matthew  8626:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   8627:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   8628:     }
                   8629:     #
                   8630:     if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
                   8631:         ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136     matthew  8632:         return '';
                   8633:     }
                   8634:     my $NumSets=1;
1.137     matthew  8635:     foreach my $array ($Ydata1,$Ydata2){
1.136     matthew  8636:         next if (! ref($array));
                   8637:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137     matthew  8638:     }
                   8639:     #
                   8640:     # Deal with other parameters
                   8641:     while (my ($key,$value) = each(%Values)) {
                   8642:         $ValuesHash{$id.'.'.$key} = $value;
1.136     matthew  8643:     }
                   8644:     #
1.646     raeburn  8645:     &Apache::lonnet::appenv(\%ValuesHash);
1.130     albertel 8646:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139     matthew  8647: }
                   8648: 
                   8649: ############################################################
                   8650: ############################################################
                   8651: 
                   8652: =pod
                   8653: 
1.157     matthew  8654: =back 
                   8655: 
1.139     matthew  8656: =head1 Statistics helper routines?  
                   8657: 
                   8658: Bad place for them but what the hell.
                   8659: 
1.157     matthew  8660: =over 4
                   8661: 
1.648     raeburn  8662: =item * &chartlink()
1.139     matthew  8663: 
                   8664: Returns a link to the chart for a specific student.  
                   8665: 
                   8666: Inputs:
                   8667: 
                   8668: =over 4
                   8669: 
                   8670: =item $linktext: The text of the link
                   8671: 
                   8672: =item $sname: The students username
                   8673: 
                   8674: =item $sdomain: The students domain
                   8675: 
                   8676: =back
                   8677: 
1.157     matthew  8678: =back
                   8679: 
1.139     matthew  8680: =cut
                   8681: 
                   8682: ############################################################
                   8683: ############################################################
                   8684: sub chartlink {
                   8685:     my ($linktext, $sname, $sdomain) = @_;
                   8686:     my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369     www      8687:         '&amp;SelectedStudent='.&escape($sname.':'.$sdomain).
1.219     albertel 8688:         '&amp;chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139     matthew  8689:        '">'.$linktext.'</a>';
1.153     matthew  8690: }
                   8691: 
                   8692: #######################################################
                   8693: #######################################################
                   8694: 
                   8695: =pod
                   8696: 
                   8697: =head1 Course Environment Routines
1.157     matthew  8698: 
                   8699: =over 4
1.153     matthew  8700: 
1.648     raeburn  8701: =item * &restore_course_settings()
1.153     matthew  8702: 
1.648     raeburn  8703: =item * &store_course_settings()
1.153     matthew  8704: 
                   8705: Restores/Store indicated form parameters from the course environment.
                   8706: Will not overwrite existing values of the form parameters.
                   8707: 
                   8708: Inputs: 
                   8709: a scalar describing the data (e.g. 'chart', 'problem_analysis')
                   8710: 
                   8711: a hash ref describing the data to be stored.  For example:
                   8712:    
                   8713: %Save_Parameters = ('Status' => 'scalar',
                   8714:     'chartoutputmode' => 'scalar',
                   8715:     'chartoutputdata' => 'scalar',
                   8716:     'Section' => 'array',
1.373     raeburn  8717:     'Group' => 'array',
1.153     matthew  8718:     'StudentData' => 'array',
                   8719:     'Maps' => 'array');
                   8720: 
                   8721: Returns: both routines return nothing
                   8722: 
1.631     raeburn  8723: =back
                   8724: 
1.153     matthew  8725: =cut
                   8726: 
                   8727: #######################################################
                   8728: #######################################################
                   8729: sub store_course_settings {
1.496     albertel 8730:     return &store_settings($env{'request.course.id'},@_);
                   8731: }
                   8732: 
                   8733: sub store_settings {
1.153     matthew  8734:     # save to the environment
                   8735:     # appenv the same items, just to be safe
1.300     albertel 8736:     my $udom  = $env{'user.domain'};
                   8737:     my $uname = $env{'user.name'};
1.496     albertel 8738:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  8739:     my %SaveHash;
                   8740:     my %AppHash;
                   8741:     while (my ($setting,$type) = each(%$Settings)) {
1.496     albertel 8742:         my $basename = join('.','internal',$context,$prefix,$setting);
1.300     albertel 8743:         my $envname = 'environment.'.$basename;
1.258     albertel 8744:         if (exists($env{'form.'.$setting})) {
1.153     matthew  8745:             # Save this value away
                   8746:             if ($type eq 'scalar' &&
1.258     albertel 8747:                 (! exists($env{$envname}) || 
                   8748:                  $env{$envname} ne $env{'form.'.$setting})) {
                   8749:                 $SaveHash{$basename} = $env{'form.'.$setting};
                   8750:                 $AppHash{$envname}   = $env{'form.'.$setting};
1.153     matthew  8751:             } elsif ($type eq 'array') {
                   8752:                 my $stored_form;
1.258     albertel 8753:                 if (ref($env{'form.'.$setting})) {
1.153     matthew  8754:                     $stored_form = join(',',
                   8755:                                         map {
1.369     www      8756:                                             &escape($_);
1.258     albertel 8757:                                         } sort(@{$env{'form.'.$setting}}));
1.153     matthew  8758:                 } else {
                   8759:                     $stored_form = 
1.369     www      8760:                         &escape($env{'form.'.$setting});
1.153     matthew  8761:                 }
                   8762:                 # Determine if the array contents are the same.
1.258     albertel 8763:                 if ($stored_form ne $env{$envname}) {
1.153     matthew  8764:                     $SaveHash{$basename} = $stored_form;
                   8765:                     $AppHash{$envname}   = $stored_form;
                   8766:                 }
                   8767:             }
                   8768:         }
                   8769:     }
                   8770:     my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300     albertel 8771:                                           $udom,$uname);
1.153     matthew  8772:     if ($put_result !~ /^(ok|delayed)/) {
                   8773:         &Apache::lonnet::logthis('unable to save form parameters, '.
                   8774:                                  'got error:'.$put_result);
                   8775:     }
                   8776:     # Make sure these settings stick around in this session, too
1.646     raeburn  8777:     &Apache::lonnet::appenv(\%AppHash);
1.153     matthew  8778:     return;
                   8779: }
                   8780: 
                   8781: sub restore_course_settings {
1.499     albertel 8782:     return &restore_settings($env{'request.course.id'},@_);
1.496     albertel 8783: }
                   8784: 
                   8785: sub restore_settings {
                   8786:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  8787:     while (my ($setting,$type) = each(%$Settings)) {
1.258     albertel 8788:         next if (exists($env{'form.'.$setting}));
1.496     albertel 8789:         my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153     matthew  8790:             '.'.$setting;
1.258     albertel 8791:         if (exists($env{$envname})) {
1.153     matthew  8792:             if ($type eq 'scalar') {
1.258     albertel 8793:                 $env{'form.'.$setting} = $env{$envname};
1.153     matthew  8794:             } elsif ($type eq 'array') {
1.258     albertel 8795:                 $env{'form.'.$setting} = [ 
1.153     matthew  8796:                                            map { 
1.369     www      8797:                                                &unescape($_); 
1.258     albertel 8798:                                            } split(',',$env{$envname})
1.153     matthew  8799:                                            ];
                   8800:             }
                   8801:         }
                   8802:     }
1.127     matthew  8803: }
                   8804: 
1.618     raeburn  8805: #######################################################
                   8806: #######################################################
                   8807: 
                   8808: =pod
                   8809: 
                   8810: =head1 Domain E-mail Routines  
                   8811: 
                   8812: =over 4
                   8813: 
1.648     raeburn  8814: =item * &build_recipient_list()
1.618     raeburn  8815: 
1.692.4.14  raeburn  8816: Build recipient lists for five types of e-mail:
1.692.4.2  raeburn  8817: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.692.4.14  raeburn  8818: (d) Help requests, (e) Course requests needing approval,  generated by
                   8819: lonerrorhandler.pm, CHECKRPMS, loncron, lonsupportreq.pm and
                   8820: loncoursequeueadmin.pm respectively.
1.618     raeburn  8821: 
                   8822: Inputs:
1.619     raeburn  8823: defmail (scalar - email address of default recipient), 
1.618     raeburn  8824: mailing type (scalar - errormail, packagesmail, or helpdeskmail), 
1.619     raeburn  8825: defdom (domain for which to retrieve configuration settings),
                   8826: origmail (scalar - email address of recipient from loncapa.conf, 
                   8827: i.e., predates configuration by DC via domainprefs.pm 
1.618     raeburn  8828: 
1.655     raeburn  8829: Returns: comma separated list of addresses to which to send e-mail.
                   8830: 
                   8831: =back
1.618     raeburn  8832: 
                   8833: =cut
                   8834: 
                   8835: ############################################################
                   8836: ############################################################
                   8837: sub build_recipient_list {
1.619     raeburn  8838:     my ($defmail,$mailing,$defdom,$origmail) = @_;
1.618     raeburn  8839:     my @recipients;
                   8840:     my $otheremails;
                   8841:     my %domconfig =
                   8842:          &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
                   8843:     if (ref($domconfig{'contacts'}) eq 'HASH') {
1.692.4.2  raeburn  8844:         if (exists($domconfig{'contacts'}{$mailing})) {
                   8845:             if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
                   8846:                 my @contacts = ('adminemail','supportemail');
                   8847:                 foreach my $item (@contacts) {
                   8848:                     if ($domconfig{'contacts'}{$mailing}{$item}) {
                   8849:                         my $addr = $domconfig{'contacts'}{$item};
                   8850:                         if (!grep(/^\Q$addr\E$/,@recipients)) {
                   8851:                             push(@recipients,$addr);
                   8852:                         }
1.619     raeburn  8853:                     }
1.692.4.2  raeburn  8854:                     $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
1.618     raeburn  8855:                 }
                   8856:             }
1.692.4.2  raeburn  8857:         } elsif ($origmail ne '') {
                   8858:             push(@recipients,$origmail);
1.618     raeburn  8859:         }
1.619     raeburn  8860:     } elsif ($origmail ne '') {
                   8861:         push(@recipients,$origmail);
1.618     raeburn  8862:     }
1.688     raeburn  8863:     if (defined($defmail)) {
                   8864:         if ($defmail ne '') {
                   8865:             push(@recipients,$defmail);
                   8866:         }
1.618     raeburn  8867:     }
                   8868:     if ($otheremails) {
1.619     raeburn  8869:         my @others;
                   8870:         if ($otheremails =~ /,/) {
                   8871:             @others = split(/,/,$otheremails);
1.618     raeburn  8872:         } else {
1.619     raeburn  8873:             push(@others,$otheremails);
                   8874:         }
                   8875:         foreach my $addr (@others) {
                   8876:             if (!grep(/^\Q$addr\E$/,@recipients)) {
                   8877:                 push(@recipients,$addr);
                   8878:             }
1.618     raeburn  8879:         }
                   8880:     }
1.619     raeburn  8881:     my $recipientlist = join(',',@recipients); 
1.618     raeburn  8882:     return $recipientlist;
                   8883: }
                   8884: 
1.127     matthew  8885: ############################################################
                   8886: ############################################################
1.154     albertel 8887: 
1.655     raeburn  8888: =pod
                   8889: 
                   8890: =head1 Course Catalog Routines
                   8891: 
                   8892: =over 4
                   8893: 
                   8894: =item * &gather_categories()
                   8895: 
                   8896: Converts category definitions - keys of categories hash stored in  
                   8897: coursecategories in configuration.db on the primary library server in a 
                   8898: domain - to an array.  Also generates javascript and idx hash used to 
                   8899: generate Domain Coordinator interface for editing Course Categories.
                   8900: 
                   8901: Inputs:
1.663     raeburn  8902: 
1.655     raeburn  8903: categories (reference to hash of category definitions).
1.663     raeburn  8904: 
1.655     raeburn  8905: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   8906:       categories and subcategories).
1.663     raeburn  8907: 
1.655     raeburn  8908: idx (reference to hash of counters used in Domain Coordinator interface for 
                   8909:       editing Course Categories).
1.663     raeburn  8910: 
1.655     raeburn  8911: jsarray (reference to array of categories used to create Javascript arrays for
                   8912:          Domain Coordinator interface for editing Course Categories).
                   8913: 
                   8914: Returns: nothing
                   8915: 
                   8916: Side effects: populates cats, idx and jsarray. 
                   8917: 
                   8918: =cut
                   8919: 
                   8920: sub gather_categories {
                   8921:     my ($categories,$cats,$idx,$jsarray) = @_;
                   8922:     my %counters;
                   8923:     my $num = 0;
                   8924:     foreach my $item (keys(%{$categories})) {
                   8925:         my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
                   8926:         if ($container eq '' && $depth == 0) {
                   8927:             $cats->[$depth][$categories->{$item}] = $cat;
                   8928:         } else {
                   8929:             $cats->[$depth]{$container}[$categories->{$item}] = $cat;
                   8930:         }
                   8931:         my ($escitem,$tail) = split(/:/,$item,2);
                   8932:         if ($counters{$tail} eq '') {
                   8933:             $counters{$tail} = $num;
                   8934:             $num ++;
                   8935:         }
                   8936:         if (ref($idx) eq 'HASH') {
                   8937:             $idx->{$item} = $counters{$tail};
                   8938:         }
                   8939:         if (ref($jsarray) eq 'ARRAY') {
                   8940:             push(@{$jsarray->[$counters{$tail}]},$item);
                   8941:         }
                   8942:     }
                   8943:     return;
                   8944: }
                   8945: 
                   8946: =pod
                   8947: 
                   8948: =item * &extract_categories()
                   8949: 
                   8950: Used to generate breadcrumb trails for course categories.
                   8951: 
                   8952: Inputs:
1.663     raeburn  8953: 
1.655     raeburn  8954: categories (reference to hash of category definitions).
1.663     raeburn  8955: 
1.655     raeburn  8956: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   8957:       categories and subcategories).
1.663     raeburn  8958: 
1.655     raeburn  8959: trails (reference to array of breacrumb trails for each category).
1.663     raeburn  8960: 
1.655     raeburn  8961: allitems (reference to hash - key is category key 
                   8962:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  8963: 
1.655     raeburn  8964: idx (reference to hash of counters used in Domain Coordinator interface for
                   8965:       editing Course Categories).
1.663     raeburn  8966: 
1.655     raeburn  8967: jsarray (reference to array of categories used to create Javascript arrays for
                   8968:          Domain Coordinator interface for editing Course Categories).
                   8969: 
1.665     raeburn  8970: subcats (reference to hash of arrays containing all subcategories within each 
                   8971:          category, -recursive)
                   8972: 
1.655     raeburn  8973: Returns: nothing
                   8974: 
                   8975: Side effects: populates trails and allitems hash references.
                   8976: 
                   8977: =cut
                   8978: 
                   8979: sub extract_categories {
1.665     raeburn  8980:     my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_;
1.655     raeburn  8981:     if (ref($categories) eq 'HASH') {
                   8982:         &gather_categories($categories,$cats,$idx,$jsarray);
                   8983:         if (ref($cats->[0]) eq 'ARRAY') {
                   8984:             for (my $i=0; $i<@{$cats->[0]}; $i++) {
                   8985:                 my $name = $cats->[0][$i];
                   8986:                 my $item = &escape($name).'::0';
                   8987:                 my $trailstr;
                   8988:                 if ($name eq 'instcode') {
                   8989:                     $trailstr = &mt('Official courses (with institutional codes)');
                   8990:                 } else {
                   8991:                     $trailstr = $name;
                   8992:                 }
                   8993:                 if ($allitems->{$item} eq '') {
                   8994:                     push(@{$trails},$trailstr);
                   8995:                     $allitems->{$item} = scalar(@{$trails})-1;
                   8996:                 }
                   8997:                 my @parents = ($name);
                   8998:                 if (ref($cats->[1]{$name}) eq 'ARRAY') {
                   8999:                     for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
                   9000:                         my $category = $cats->[1]{$name}[$j];
1.665     raeburn  9001:                         if (ref($subcats) eq 'HASH') {
                   9002:                             push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
                   9003:                         }
                   9004:                         &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats);
                   9005:                     }
                   9006:                 } else {
                   9007:                     if (ref($subcats) eq 'HASH') {
                   9008:                         $subcats->{$item} = [];
1.655     raeburn  9009:                     }
                   9010:                 }
                   9011:             }
                   9012:         }
                   9013:     }
                   9014:     return;
                   9015: }
                   9016: 
                   9017: =pod
                   9018: 
                   9019: =item *&recurse_categories()
                   9020: 
                   9021: Recursively used to generate breadcrumb trails for course categories.
                   9022: 
                   9023: Inputs:
1.663     raeburn  9024: 
1.655     raeburn  9025: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   9026:       categories and subcategories).
1.663     raeburn  9027: 
1.655     raeburn  9028: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663     raeburn  9029: 
                   9030: category (current course category, for which breadcrumb trail is being generated).
                   9031: 
                   9032: trails (reference to array of breadcrumb trails for each category).
                   9033: 
1.655     raeburn  9034: allitems (reference to hash - key is category key
                   9035:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  9036: 
1.655     raeburn  9037: parents (array containing containers directories for current category, 
                   9038:          back to top level). 
                   9039: 
                   9040: Returns: nothing
                   9041: 
                   9042: Side effects: populates trails and allitems hash references
                   9043: 
                   9044: =cut
                   9045: 
                   9046: sub recurse_categories {
1.665     raeburn  9047:     my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_;
1.655     raeburn  9048:     my $shallower = $depth - 1;
                   9049:     if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
                   9050:         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
                   9051:             my $name = $cats->[$depth]{$category}[$k];
                   9052:             my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   9053:             my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   9054:             if ($allitems->{$item} eq '') {
                   9055:                 push(@{$trails},$trailstr);
                   9056:                 $allitems->{$item} = scalar(@{$trails})-1;
                   9057:             }
                   9058:             my $deeper = $depth+1;
                   9059:             push(@{$parents},$category);
1.665     raeburn  9060:             if (ref($subcats) eq 'HASH') {
                   9061:                 my $subcat = &escape($name).':'.$category.':'.$depth;
                   9062:                 for (my $j=@{$parents}; $j>=0; $j--) {
                   9063:                     my $higher;
                   9064:                     if ($j > 0) {
                   9065:                         $higher = &escape($parents->[$j]).':'.
                   9066:                                   &escape($parents->[$j-1]).':'.$j;
                   9067:                     } else {
                   9068:                         $higher = &escape($parents->[$j]).'::'.$j;
                   9069:                     }
                   9070:                     push(@{$subcats->{$higher}},$subcat);
                   9071:                 }
                   9072:             }
                   9073:             &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
                   9074:                                 $subcats);
1.655     raeburn  9075:             pop(@{$parents});
                   9076:         }
                   9077:     } else {
                   9078:         my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   9079:         my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   9080:         if ($allitems->{$item} eq '') {
                   9081:             push(@{$trails},$trailstr);
                   9082:             $allitems->{$item} = scalar(@{$trails})-1;
                   9083:         }
                   9084:     }
                   9085:     return;
                   9086: }
                   9087: 
1.663     raeburn  9088: =pod
                   9089: 
                   9090: =item *&assign_categories_table()
                   9091: 
                   9092: Create a datatable for display of hierarchical categories in a domain,
                   9093: with checkboxes to allow a course to be categorized. 
                   9094: 
                   9095: Inputs:
                   9096: 
                   9097: cathash - reference to hash of categories defined for the domain (from
                   9098:           configuration.db)
                   9099: 
                   9100: currcat - scalar with an & separated list of categories assigned to a course. 
                   9101: 
                   9102: Returns: $output (markup to be displayed) 
                   9103: 
                   9104: =cut
                   9105: 
                   9106: sub assign_categories_table {
                   9107:     my ($cathash,$currcat) = @_;
                   9108:     my $output;
                   9109:     if (ref($cathash) eq 'HASH') {
                   9110:         my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth);
                   9111:         &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray);
                   9112:         $maxdepth = scalar(@cats);
                   9113:         if (@cats > 0) {
                   9114:             my $itemcount = 0;
                   9115:             if (ref($cats[0]) eq 'ARRAY') {
                   9116:                 $output = &Apache::loncommon::start_data_table();
                   9117:                 my @currcategories;
                   9118:                 if ($currcat ne '') {
                   9119:                     @currcategories = split('&',$currcat);
                   9120:                 }
                   9121:                 for (my $i=0; $i<@{$cats[0]}; $i++) {
                   9122:                     my $parent = $cats[0][$i];
                   9123:                     my $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   9124:                     next if ($parent eq 'instcode');
                   9125:                     my $item = &escape($parent).'::0';
                   9126:                     my $checked = '';
                   9127:                     if (@currcategories > 0) {
                   9128:                         if (grep(/^\Q$item\E$/,@currcategories)) {
                   9129:                             $checked = ' checked="checked" ';
                   9130:                         }
                   9131:                     }
1.675     raeburn  9132:                     $output .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
                   9133:                                '<input type="checkbox" name="usecategory" value="'.
                   9134:                                $item.'"'.$checked.' />'.$parent.'</span>'.
                   9135:                                '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663     raeburn  9136:                     my $depth = 1;
                   9137:                     push(@path,$parent);
                   9138:                     $output .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories);
                   9139:                     pop(@path);
                   9140:                     $output .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
                   9141:                     $itemcount ++;
                   9142:                 }
                   9143:                 $output .= &Apache::loncommon::end_data_table();
                   9144:             }
                   9145:         }
                   9146:     }
                   9147:     return $output;
                   9148: }
                   9149: 
                   9150: =pod
                   9151: 
                   9152: =item *&assign_category_rows()
                   9153: 
                   9154: Create a datatable row for display of nested categories in a domain,
                   9155: with checkboxes to allow a course to be categorized,called recursively.
                   9156: 
                   9157: Inputs:
                   9158: 
                   9159: itemcount - track row number for alternating colors
                   9160: 
                   9161: cats - reference to array of arrays/hashes which encapsulates hierarchy of
                   9162:       categories and subcategories.
                   9163: 
                   9164: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
                   9165: 
                   9166: parent - parent of current category item
                   9167: 
                   9168: path - Array containing all categories back up through the hierarchy from the
                   9169:        current category to the top level.
                   9170: 
                   9171: currcategories - reference to array of current categories assigned to the course
                   9172: 
                   9173: Returns: $output (markup to be displayed).
                   9174: 
                   9175: =cut
                   9176: 
                   9177: sub assign_category_rows {
                   9178:     my ($itemcount,$cats,$depth,$parent,$path,$currcategories) = @_;
                   9179:     my ($text,$name,$item,$chgstr);
                   9180:     if (ref($cats) eq 'ARRAY') {
                   9181:         my $maxdepth = scalar(@{$cats});
                   9182:         if (ref($cats->[$depth]) eq 'HASH') {
                   9183:             if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
                   9184:                 my $numchildren = @{$cats->[$depth]{$parent}};
                   9185:                 my $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   9186:                 $text .= '<td><table class="LC_datatable">';
                   9187:                 for (my $j=0; $j<$numchildren; $j++) {
                   9188:                     $name = $cats->[$depth]{$parent}[$j];
                   9189:                     $item = &escape($name).':'.&escape($parent).':'.$depth;
                   9190:                     my $deeper = $depth+1;
                   9191:                     my $checked = '';
                   9192:                     if (ref($currcategories) eq 'ARRAY') {
                   9193:                         if (@{$currcategories} > 0) {
                   9194:                             if (grep(/^\Q$item\E$/,@{$currcategories})) {
                   9195:                                 $checked = ' checked="checked" ';
                   9196:                             }
                   9197:                         }
                   9198:                     }
1.664     raeburn  9199:                     $text .= '<tr><td><span class="LC_nobreak"><label>'.
                   9200:                              '<input type="checkbox" name="usecategory" value="'.
1.675     raeburn  9201:                              $item.'"'.$checked.' />'.$name.'</label></span>'.
                   9202:                              '<input type="hidden" name="catname" value="'.$name.'" />'.
                   9203:                              '</td><td>';
1.663     raeburn  9204:                     if (ref($path) eq 'ARRAY') {
                   9205:                         push(@{$path},$name);
                   9206:                         $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories);
                   9207:                         pop(@{$path});
                   9208:                     }
                   9209:                     $text .= '</td></tr>';
                   9210:                 }
                   9211:                 $text .= '</table></td>';
                   9212:             }
                   9213:         }
                   9214:     }
                   9215:     return $text;
                   9216: }
                   9217: 
1.655     raeburn  9218: ############################################################
                   9219: ############################################################
                   9220: 
                   9221: 
1.443     albertel 9222: sub commit_customrole {
1.664     raeburn  9223:     my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
1.630     raeburn  9224:     my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443     albertel 9225:                          ($start?', '.&mt('starting').' '.localtime($start):'').
                   9226:                          ($end?', ending '.localtime($end):'').': <b>'.
                   9227:               &Apache::lonnet::assigncustomrole(
1.664     raeburn  9228:                  $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
1.443     albertel 9229:                  '</b><br />';
                   9230:     return $output;
                   9231: }
                   9232: 
                   9233: sub commit_standardrole {
1.541     raeburn  9234:     my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
                   9235:     my ($output,$logmsg,$linefeed);
                   9236:     if ($context eq 'auto') {
                   9237:         $linefeed = "\n";
                   9238:     } else {
                   9239:         $linefeed = "<br />\n";
                   9240:     }  
1.443     albertel 9241:     if ($three eq 'st') {
1.541     raeburn  9242:         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
                   9243:                                          $one,$two,$sec,$context);
                   9244:         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
1.626     raeburn  9245:             ($result eq 'unknown_course') || ($result eq 'refused')) {
                   9246:             $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
1.443     albertel 9247:         } else {
1.541     raeburn  9248:             $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443     albertel 9249:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  9250:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
                   9251:             if ($context eq 'auto') {
                   9252:                 $output .= $result.$linefeed.&mt('Add to classlist').': ok';
                   9253:             } else {
                   9254:                $output .= '<b>'.$result.'</b>'.$linefeed.
                   9255:                &mt('Add to classlist').': <b>ok</b>';
                   9256:             }
                   9257:             $output .= $linefeed;
1.443     albertel 9258:         }
                   9259:     } else {
                   9260:         $output = &mt('Assigning').' '.$three.' in '.$url.
                   9261:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  9262:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652     raeburn  9263:         my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541     raeburn  9264:         if ($context eq 'auto') {
                   9265:             $output .= $result.$linefeed;
                   9266:         } else {
                   9267:             $output .= '<b>'.$result.'</b>'.$linefeed;
                   9268:         }
1.443     albertel 9269:     }
                   9270:     return $output;
                   9271: }
                   9272: 
                   9273: sub commit_studentrole {
1.541     raeburn  9274:     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
1.626     raeburn  9275:     my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541     raeburn  9276:     if ($context eq 'auto') {
                   9277:         $linefeed = "\n";
                   9278:     } else {
                   9279:         $linefeed = '<br />'."\n";
                   9280:     }
1.443     albertel 9281:     if (defined($one) && defined($two)) {
                   9282:         my $cid=$one.'_'.$two;
                   9283:         my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
                   9284:         my $secchange = 0;
                   9285:         my $expire_role_result;
                   9286:         my $modify_section_result;
1.628     raeburn  9287:         if ($oldsec ne '-1') { 
                   9288:             if ($oldsec ne $sec) {
1.443     albertel 9289:                 $secchange = 1;
1.628     raeburn  9290:                 my $now = time;
1.443     albertel 9291:                 my $uurl='/'.$cid;
                   9292:                 $uurl=~s/\_/\//g;
                   9293:                 if ($oldsec) {
                   9294:                     $uurl.='/'.$oldsec;
                   9295:                 }
1.626     raeburn  9296:                 $oldsecurl = $uurl;
1.628     raeburn  9297:                 $expire_role_result = 
1.652     raeburn  9298:                     &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628     raeburn  9299:                 if ($env{'request.course.sec'} ne '') { 
                   9300:                     if ($expire_role_result eq 'refused') {
                   9301:                         my @roles = ('st');
                   9302:                         my @statuses = ('previous');
                   9303:                         my @roledoms = ($one);
                   9304:                         my $withsec = 1;
                   9305:                         my %roleshash = 
                   9306:                             &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
                   9307:                                               \@statuses,\@roles,\@roledoms,$withsec);
                   9308:                         if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
                   9309:                             my ($oldstart,$oldend) = 
                   9310:                                 split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
                   9311:                             if ($oldend > 0 && $oldend <= $now) {
                   9312:                                 $expire_role_result = 'ok';
                   9313:                             }
                   9314:                         }
                   9315:                     }
                   9316:                 }
1.443     albertel 9317:                 $result = $expire_role_result;
                   9318:             }
                   9319:         }
                   9320:         if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.652     raeburn  9321:             $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid,'',$context);
1.443     albertel 9322:             if ($modify_section_result =~ /^ok/) {
                   9323:                 if ($secchange == 1) {
1.628     raeburn  9324:                     if ($sec eq '') {
                   9325:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
                   9326:                     } else {
                   9327:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
                   9328:                     }
1.443     albertel 9329:                 } elsif ($oldsec eq '-1') {
1.628     raeburn  9330:                     if ($sec eq '') {
                   9331:                         $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
                   9332:                     } else {
                   9333:                         $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   9334:                     }
1.443     albertel 9335:                 } else {
1.628     raeburn  9336:                     if ($sec eq '') {
                   9337:                         $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
                   9338:                     } else {
                   9339:                         $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   9340:                     }
1.443     albertel 9341:                 }
                   9342:             } else {
1.628     raeburn  9343:                 if ($secchange) {       
                   9344:                     $$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;
                   9345:                 } else {
                   9346:                     $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
                   9347:                 }
1.443     albertel 9348:             }
                   9349:             $result = $modify_section_result;
                   9350:         } elsif ($secchange == 1) {
1.628     raeburn  9351:             if ($oldsec eq '') {
                   9352:                 $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_3] -error: ',$uname,$cid).' '.$expire_role_result.$linefeed;
                   9353:             } else {
                   9354:                 $$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;
                   9355:             }
1.626     raeburn  9356:             if ($expire_role_result eq 'refused') {
                   9357:                 my $newsecurl = '/'.$cid;
                   9358:                 $newsecurl =~ s/\_/\//g;
                   9359:                 if ($sec ne '') {
                   9360:                     $newsecurl.='/'.$sec;
                   9361:                 }
                   9362:                 if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
                   9363:                     if ($sec eq '') {
                   9364:                         $$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;
                   9365:                     } else {
                   9366:                         $$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;
                   9367:                     }
                   9368:                 }
                   9369:             }
1.443     albertel 9370:         }
                   9371:     } else {
1.626     raeburn  9372:         $$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 9373:         $result = "error: incomplete course id\n";
                   9374:     }
                   9375:     return $result;
                   9376: }
                   9377: 
                   9378: ############################################################
                   9379: ############################################################
                   9380: 
1.566     albertel 9381: sub check_clone {
1.578     raeburn  9382:     my ($args,$linefeed) = @_;
1.566     albertel 9383:     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
                   9384:     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
                   9385:     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
                   9386:     my $clonemsg;
                   9387:     my $can_clone = 0;
1.692.4.22! raeburn  9388:     my $lctype = lc($args->{'type'});
        !          9389:     if ($lctype ne 'community') {
        !          9390:         $lctype = 'course';
        !          9391:     }
1.566     albertel 9392:     if ($clonehome eq 'no_host') {
1.692.4.22! raeburn  9393:         if ($args->{'type'} eq 'Community') {
        !          9394:             $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
        !          9395:         } else {
        !          9396:             $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'});
        !          9397:         }
1.566     albertel 9398:     } else {
                   9399: 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.692.4.22! raeburn  9400:         if ($args->{'type'} eq 'Community') {
        !          9401:             if ($clonedesc{'type'} ne 'Community') {
        !          9402:                  $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
        !          9403:                 return ($can_clone, $clonemsg, $cloneid, $clonehome);
        !          9404:             }
        !          9405:         }
        !          9406:         if (($env{'request.role.domain'} eq $args->{'clonedomain'}) &&
1.692.4.12  raeburn  9407:             (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.692.4.22! raeburn  9408:             $can_clone = 1;
        !          9409:         } else {
        !          9410:             my %clonehash = &Apache::lonnet::get('environment',['cloners'],
        !          9411:                                                  $args->{'clonedomain'},$args->{'clonecourse'});
        !          9412:             my @cloners = split(/,/,$clonehash{'cloners'});
1.578     raeburn  9413:             if (grep(/^\*$/,@cloners)) {
                   9414:                 $can_clone = 1;
                   9415:             } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
                   9416:                 $can_clone = 1;
                   9417:             } else {
1.692.4.22! raeburn  9418:                 my $ccrole = 'cc';
        !          9419:                 if ($args->{'type'} eq 'Community') {
        !          9420:                     $ccrole = 'co';
        !          9421:                 }
        !          9422:                 my %roleshash =
        !          9423:                     &Apache::lonnet::get_my_roles($args->{'ccuname'},
        !          9424:                                          $args->{'ccdomain'},
        !          9425:                                          'userroles',['active'],[$ccrole],
        !          9426:                                          [$args->{'clonedomain'}]);
        !          9427:                 if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
        !          9428:                     $can_clone = 1;
        !          9429:                 } else {
        !          9430:                     if ($args->{'type'} eq 'Community') {
        !          9431:                         $clonemsg = &mt('No new community created.').$linefeed.&mt('The new community could not be cloned from the existing community because the new community owner ([_1]) does not have cloning rights in the existing community ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
        !          9432:                     } else {
        !          9433:                         $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'});
        !          9434:                     }
        !          9435:                 }
        !          9436:             }
1.578     raeburn  9437:         }
1.566     albertel 9438:     }
                   9439:     return ($can_clone, $clonemsg, $cloneid, $clonehome);
                   9440: }
                   9441: 
1.444     albertel 9442: sub construct_course {
1.692.4.14  raeburn  9443:     my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,$cnum,$category) = @_;
1.444     albertel 9444:     my $outcome;
1.541     raeburn  9445:     my $linefeed =  '<br />'."\n";
                   9446:     if ($context eq 'auto') {
                   9447:         $linefeed = "\n";
                   9448:     }
1.566     albertel 9449: 
                   9450: #
                   9451: # Are we cloning?
                   9452: #
                   9453:     my ($can_clone, $clonemsg, $cloneid, $clonehome);
                   9454:     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578     raeburn  9455: 	($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566     albertel 9456: 	if ($context ne 'auto') {
1.578     raeburn  9457:             if ($clonemsg ne '') {
                   9458: 	        $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
                   9459:             }
1.566     albertel 9460: 	}
                   9461: 	$outcome .= $clonemsg.$linefeed;
                   9462: 
                   9463:         if (!$can_clone) {
                   9464: 	    return (0,$outcome);
                   9465: 	}
                   9466:     }
                   9467: 
1.444     albertel 9468: #
                   9469: # Open course
                   9470: #
                   9471:     my $crstype = lc($args->{'crstype'});
                   9472:     my %cenv=();
                   9473:     $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
                   9474:                                              $args->{'cdescr'},
                   9475:                                              $args->{'curl'},
                   9476:                                              $args->{'course_home'},
                   9477:                                              $args->{'nonstandard'},
                   9478:                                              $args->{'crscode'},
                   9479:                                              $args->{'ccuname'}.':'.
                   9480:                                              $args->{'ccdomain'},
1.692.4.12  raeburn  9481:                                              $args->{'crstype'},
1.692.4.14  raeburn  9482:                                              $cnum,$context,$category);
1.692.4.12  raeburn  9483: 
1.444     albertel 9484: 
                   9485:     # Note: The testing routines depend on this being output; see 
                   9486:     # Utils::Course. This needs to at least be output as a comment
                   9487:     # if anyone ever decides to not show this, and Utils::Course::new
                   9488:     # will need to be suitably modified.
1.541     raeburn  9489:     $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
1.444     albertel 9490: #
                   9491: # Check if created correctly
                   9492: #
1.479     albertel 9493:     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444     albertel 9494:     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.541     raeburn  9495:     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566     albertel 9496: 
1.444     albertel 9497: #
1.566     albertel 9498: # Do the cloning
                   9499: #   
                   9500:     if ($can_clone && $cloneid) {
                   9501: 	$clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
                   9502: 	if ($context ne 'auto') {
                   9503: 	    $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
                   9504: 	}
                   9505: 	$outcome .= $clonemsg.$linefeed;
                   9506: 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444     albertel 9507: # Copy all files
1.637     www      9508: 	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
1.444     albertel 9509: # Restore URL
1.566     albertel 9510: 	$cenv{'url'}=$oldcenv{'url'};
1.444     albertel 9511: # Restore title
1.566     albertel 9512: 	$cenv{'description'}=$oldcenv{'description'};
1.444     albertel 9513: # Mark as cloned
1.566     albertel 9514: 	$cenv{'clonedfrom'}=$cloneid;
1.638     www      9515: # Need to clone grading mode
                   9516:         my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
                   9517:         $cenv{'grading'}=$newenv{'grading'};
                   9518: # Do not clone these environment entries
                   9519:         &Apache::lonnet::del('environment',
                   9520:                   ['default_enrollment_start_date',
                   9521:                    'default_enrollment_end_date',
                   9522:                    'question.email',
                   9523:                    'policy.email',
                   9524:                    'comment.email',
                   9525:                    'pch.users.denied',
1.692.4.2  raeburn  9526:                    'plc.users.denied',
                   9527:                    'hidefromcat',
                   9528:                    'categories'],
1.638     www      9529:                    $$crsudom,$$crsunum);
1.444     albertel 9530:     }
1.566     albertel 9531: 
1.444     albertel 9532: #
                   9533: # Set environment (will override cloned, if existing)
                   9534: #
                   9535:     my @sections = ();
                   9536:     my @xlists = ();
                   9537:     if ($args->{'crstype'}) {
                   9538:         $cenv{'type'}=$args->{'crstype'};
                   9539:     }
                   9540:     if ($args->{'crsid'}) {
                   9541:         $cenv{'courseid'}=$args->{'crsid'};
                   9542:     }
                   9543:     if ($args->{'crscode'}) {
                   9544:         $cenv{'internal.coursecode'}=$args->{'crscode'};
                   9545:     }
                   9546:     if ($args->{'crsquota'} ne '') {
                   9547:         $cenv{'internal.coursequota'}=$args->{'crsquota'};
                   9548:     } else {
                   9549:         $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
                   9550:     }
                   9551:     if ($args->{'ccuname'}) {
                   9552:         $cenv{'internal.courseowner'} = $args->{'ccuname'}.
                   9553:                                         ':'.$args->{'ccdomain'};
                   9554:     } else {
                   9555:         $cenv{'internal.courseowner'} = $args->{'curruser'};
                   9556:     }
                   9557:     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
                   9558:     if ($args->{'crssections'}) {
                   9559:         $cenv{'internal.sectionnums'} = '';
                   9560:         if ($args->{'crssections'} =~ m/,/) {
                   9561:             @sections = split/,/,$args->{'crssections'};
                   9562:         } else {
                   9563:             $sections[0] = $args->{'crssections'};
                   9564:         }
                   9565:         if (@sections > 0) {
                   9566:             foreach my $item (@sections) {
                   9567:                 my ($sec,$gp) = split/:/,$item;
                   9568:                 my $class = $args->{'crscode'}.$sec;
                   9569:                 my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
                   9570:                 $cenv{'internal.sectionnums'} .= $item.',';
                   9571:                 unless ($addcheck eq 'ok') {
                   9572:                     push @badclasses, $class;
                   9573:                 }
                   9574:             }
                   9575:             $cenv{'internal.sectionnums'} =~ s/,$//;
                   9576:         }
                   9577:     }
                   9578: # do not hide course coordinator from staff listing, 
                   9579: # even if privileged
                   9580:     $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   9581: # add crosslistings
                   9582:     if ($args->{'crsxlist'}) {
                   9583:         $cenv{'internal.crosslistings'}='';
                   9584:         if ($args->{'crsxlist'} =~ m/,/) {
                   9585:             @xlists = split/,/,$args->{'crsxlist'};
                   9586:         } else {
                   9587:             $xlists[0] = $args->{'crsxlist'};
                   9588:         }
                   9589:         if (@xlists > 0) {
                   9590:             foreach my $item (@xlists) {
                   9591:                 my ($xl,$gp) = split/:/,$item;
                   9592:                 my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
                   9593:                 $cenv{'internal.crosslistings'} .= $item.',';
                   9594:                 unless ($addcheck eq 'ok') {
                   9595:                     push @badclasses, $xl;
                   9596:                 }
                   9597:             }
                   9598:             $cenv{'internal.crosslistings'} =~ s/,$//;
                   9599:         }
                   9600:     }
                   9601:     if ($args->{'autoadds'}) {
                   9602:         $cenv{'internal.autoadds'}=$args->{'autoadds'};
                   9603:     }
                   9604:     if ($args->{'autodrops'}) {
                   9605:         $cenv{'internal.autodrops'}=$args->{'autodrops'};
                   9606:     }
                   9607: # check for notification of enrollment changes
                   9608:     my @notified = ();
                   9609:     if ($args->{'notify_owner'}) {
                   9610:         if ($args->{'ccuname'} ne '') {
                   9611:             push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
                   9612:         }
                   9613:     }
                   9614:     if ($args->{'notify_dc'}) {
                   9615:         if ($uname ne '') { 
1.630     raeburn  9616:             push(@notified,$uname.':'.$udom);
1.444     albertel 9617:         }
                   9618:     }
                   9619:     if (@notified > 0) {
                   9620:         my $notifylist;
                   9621:         if (@notified > 1) {
                   9622:             $notifylist = join(',',@notified);
                   9623:         } else {
                   9624:             $notifylist = $notified[0];
                   9625:         }
                   9626:         $cenv{'internal.notifylist'} = $notifylist;
                   9627:     }
                   9628:     if (@badclasses > 0) {
                   9629:         my %lt=&Apache::lonlocal::texthash(
                   9630:                 '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',
                   9631:                 'dnhr' => 'does not have rights to access enrollment in these classes',
                   9632:                 'adby' => 'as determined by the policies of your institution on access to official classlists'
                   9633:         );
1.541     raeburn  9634:         my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
                   9635:                            ' ('.$lt{'adby'}.')';
                   9636:         if ($context eq 'auto') {
                   9637:             $outcome .= $badclass_msg.$linefeed;
1.566     albertel 9638:             $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.541     raeburn  9639:             foreach my $item (@badclasses) {
                   9640:                 if ($context eq 'auto') {
                   9641:                     $outcome .= " - $item\n";
                   9642:                 } else {
                   9643:                     $outcome .= "<li>$item</li>\n";
                   9644:                 }
                   9645:             }
                   9646:             if ($context eq 'auto') {
                   9647:                 $outcome .= $linefeed;
                   9648:             } else {
1.566     albertel 9649:                 $outcome .= "</ul><br /><br /></div>\n";
1.541     raeburn  9650:             }
                   9651:         } 
1.444     albertel 9652:     }
                   9653:     if ($args->{'no_end_date'}) {
                   9654:         $args->{'endaccess'} = 0;
                   9655:     }
                   9656:     $cenv{'internal.autostart'}=$args->{'enrollstart'};
                   9657:     $cenv{'internal.autoend'}=$args->{'enrollend'};
                   9658:     $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
                   9659:     $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
                   9660:     if ($args->{'showphotos'}) {
                   9661:       $cenv{'internal.showphotos'}=$args->{'showphotos'};
                   9662:     }
                   9663:     $cenv{'internal.authtype'} = $args->{'authtype'};
                   9664:     $cenv{'internal.autharg'} = $args->{'autharg'}; 
                   9665:     if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
                   9666:         if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'}  eq '') {
1.541     raeburn  9667:             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'); 
                   9668:             if ($context eq 'auto') {
                   9669:                 $outcome .= $krb_msg;
                   9670:             } else {
1.566     albertel 9671:                 $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541     raeburn  9672:             }
                   9673:             $outcome .= $linefeed;
1.444     albertel 9674:         }
                   9675:     }
                   9676:     if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
                   9677:        if ($args->{'setpolicy'}) {
                   9678:            $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   9679:        }
                   9680:        if ($args->{'setcontent'}) {
                   9681:            $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   9682:        }
                   9683:     }
                   9684:     if ($args->{'reshome'}) {
                   9685: 	$cenv{'reshome'}=$args->{'reshome'}.'/';
                   9686: 	$cenv{'reshome'}=~s/\/+$/\//;
                   9687:     }
                   9688: #
                   9689: # course has keyed access
                   9690: #
                   9691:     if ($args->{'setkeys'}) {
                   9692:        $cenv{'keyaccess'}='yes';
                   9693:     }
                   9694: # if specified, key authority is not course, but user
                   9695: # only active if keyaccess is yes
                   9696:     if ($args->{'keyauth'}) {
1.487     albertel 9697: 	my ($user,$domain) = split(':',$args->{'keyauth'});
                   9698: 	$user = &LONCAPA::clean_username($user);
                   9699: 	$domain = &LONCAPA::clean_username($domain);
1.488     foxr     9700: 	if ($user ne '' && $domain ne '') {
1.487     albertel 9701: 	    $cenv{'keyauth'}=$user.':'.$domain;
1.444     albertel 9702: 	}
                   9703:     }
                   9704: 
                   9705:     if ($args->{'disresdis'}) {
                   9706:         $cenv{'pch.roles.denied'}='st';
                   9707:     }
                   9708:     if ($args->{'disablechat'}) {
                   9709:         $cenv{'plc.roles.denied'}='st';
                   9710:     }
                   9711: 
                   9712:     # Record we've not yet viewed the Course Initialization Helper for this 
                   9713:     # course
                   9714:     $cenv{'course.helper.not.run'} = 1;
                   9715:     #
                   9716:     # Use new Randomseed
                   9717:     #
                   9718:     $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
                   9719:     $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
                   9720:     #
                   9721:     # The encryption code and receipt prefix for this course
                   9722:     #
                   9723:     $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
                   9724:     $cenv{'internal.encpref'}=100+int(9*rand(99));
                   9725:     #
                   9726:     # By default, use standard grading
                   9727:     if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
                   9728: 
1.541     raeburn  9729:     $outcome .= $linefeed.&mt('Setting environment').': '.                 
                   9730:           &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 9731: #
                   9732: # Open all assignments
                   9733: #
                   9734:     if ($args->{'openall'}) {
                   9735:        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
                   9736:        my %storecontent = ($storeunder         => time,
                   9737:                            $storeunder.'.type' => 'date_start');
                   9738:        
                   9739:        $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541     raeburn  9740:                  ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 9741:    }
                   9742: #
                   9743: # Set first page
                   9744: #
                   9745:     unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
                   9746: 	    || ($cloneid)) {
1.445     albertel 9747: 	use LONCAPA::map;
1.444     albertel 9748: 	$outcome .= &mt('Setting first resource').': ';
1.445     albertel 9749: 
                   9750: 	my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
                   9751:         my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
                   9752: 
1.444     albertel 9753:         $outcome .= ($fatal?$errtext:'read ok').' - ';
                   9754:         my $title; my $url;
                   9755:         if ($args->{'firstres'} eq 'syl') {
1.690     bisitz   9756: 	    $title=&mt('Syllabus');
1.444     albertel 9757:             $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
                   9758:         } else {
1.690     bisitz   9759:             $title=&mt('Navigate Contents');
1.444     albertel 9760:             $url='/adm/navmaps';
                   9761:         }
1.445     albertel 9762: 
                   9763:         $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
                   9764: 	(my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
                   9765: 
                   9766: 	if ($errtext) { $fatal=2; }
1.541     raeburn  9767:         $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444     albertel 9768:     }
1.566     albertel 9769: 
                   9770:     return (1,$outcome);
1.444     albertel 9771: }
                   9772: 
                   9773: ############################################################
                   9774: ############################################################
                   9775: 
1.378     raeburn  9776: sub course_type {
                   9777:     my ($cid) = @_;
                   9778:     if (!defined($cid)) {
                   9779:         $cid = $env{'request.course.id'};
                   9780:     }
1.404     albertel 9781:     if (defined($env{'course.'.$cid.'.type'})) {
                   9782:         return $env{'course.'.$cid.'.type'};
1.378     raeburn  9783:     } else {
                   9784:         return 'Course';
1.377     raeburn  9785:     }
                   9786: }
1.156     albertel 9787: 
1.406     raeburn  9788: sub group_term {
                   9789:     my $crstype = &course_type();
                   9790:     my %names = (
1.692.4.6  raeburn  9791:                   'Course'    => 'group',
                   9792:                   'Community' => 'group',
1.406     raeburn  9793:                 );
                   9794:     return $names{$crstype};
                   9795: }
                   9796: 
1.692.4.20  raeburn  9797: sub course_types {
                   9798:     my @types = ('official','unofficial','community');
                   9799:     my %typename = (
                   9800:                          official   => 'Official course',
                   9801:                          unofficial => 'Unofficial course',
                   9802:                          community  => 'Community',
                   9803:                    );
                   9804:     return (\@types,\%typename);
                   9805: }
                   9806: 
1.156     albertel 9807: sub icon {
                   9808:     my ($file)=@_;
1.505     albertel 9809:     my $curfext = lc((split(/\./,$file))[-1]);
1.168     albertel 9810:     my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156     albertel 9811:     my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168     albertel 9812:     if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
                   9813: 	if (-e  $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                   9814: 	          $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   9815: 	            $curfext.".gif") {
                   9816: 	    $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   9817: 		$curfext.".gif";
                   9818: 	}
                   9819:     }
1.249     albertel 9820:     return &lonhttpdurl($iconname);
1.154     albertel 9821: } 
1.84      albertel 9822: 
1.575     albertel 9823: sub lonhttpdurl {
1.692     www      9824: #
                   9825: # Had been used for "small fry" static images on separate port 8080.
                   9826: # Modify here if lightweight http functionality desired again.
                   9827: # Currently eliminated due to increasing firewall issues.
                   9828: #
1.575     albertel 9829:     my ($url)=@_;
1.692     www      9830:     return $url;
1.215     albertel 9831: }
                   9832: 
1.213     albertel 9833: sub connection_aborted {
                   9834:     my ($r)=@_;
                   9835:     $r->print(" ");$r->rflush();
                   9836:     my $c = $r->connection;
                   9837:     return $c->aborted();
                   9838: }
                   9839: 
1.221     foxr     9840: #    Escapes strings that may have embedded 's that will be put into
1.222     foxr     9841: #    strings as 'strings'.
                   9842: sub escape_single {
1.221     foxr     9843:     my ($input) = @_;
1.223     albertel 9844:     $input =~ s/\\/\\\\/g;	# Escape the \'s..(must be first)>
1.221     foxr     9845:     $input =~ s/\'/\\\'/g;	# Esacpe the 's....
                   9846:     return $input;
                   9847: }
1.223     albertel 9848: 
1.222     foxr     9849: #  Same as escape_single, but escape's "'s  This 
                   9850: #  can be used for  "strings"
                   9851: sub escape_double {
                   9852:     my ($input) = @_;
                   9853:     $input =~ s/\\/\\\\/g;	# Escape the /'s..(must be first)>
                   9854:     $input =~ s/\"/\\\"/g;	# Esacpe the "s....
                   9855:     return $input;
                   9856: }
1.223     albertel 9857:  
1.222     foxr     9858: #   Escapes the last element of a full URL.
                   9859: sub escape_url {
                   9860:     my ($url)   = @_;
1.238     raeburn  9861:     my @urlslices = split(/\//, $url,-1);
1.369     www      9862:     my $lastitem = &escape(pop(@urlslices));
1.223     albertel 9863:     return join('/',@urlslices).'/'.$lastitem;
1.222     foxr     9864: }
1.462     albertel 9865: 
1.692.4.2  raeburn  9866: sub compare_arrays {
                   9867:     my ($arrayref1,$arrayref2) = @_;
                   9868:     my (@difference,%count);
                   9869:     @difference = ();
                   9870:     %count = ();
                   9871:     if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
                   9872:         foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
                   9873:         foreach my $element (keys(%count)) {
                   9874:             if ($count{$element} == 1) {
                   9875:                 push(@difference,$element);
                   9876:             }
                   9877:         }
                   9878:     }
                   9879:     return @difference;
                   9880: }
                   9881: 
1.462     albertel 9882: # -------------------------------------------------------- Initliaze user login
                   9883: sub init_user_environment {
1.463     albertel 9884:     my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462     albertel 9885:     my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
                   9886: 
                   9887:     my $public=($username eq 'public' && $domain eq 'public');
                   9888: 
                   9889: # See if old ID present, if so, remove
                   9890: 
                   9891:     my ($filename,$cookie,$userroles);
                   9892:     my $now=time;
                   9893: 
                   9894:     if ($public) {
                   9895: 	my $max_public=100;
                   9896: 	my $oldest;
                   9897: 	my $oldest_time=0;
                   9898: 	for(my $next=1;$next<=$max_public;$next++) {
                   9899: 	    if (-e $lonids."/publicuser_$next.id") {
                   9900: 		my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
                   9901: 		if ($mtime<$oldest_time || !$oldest_time) {
                   9902: 		    $oldest_time=$mtime;
                   9903: 		    $oldest=$next;
                   9904: 		}
                   9905: 	    } else {
                   9906: 		$cookie="publicuser_$next";
                   9907: 		last;
                   9908: 	    }
                   9909: 	}
                   9910: 	if (!$cookie) { $cookie="publicuser_$oldest"; }
                   9911:     } else {
1.463     albertel 9912: 	# if this isn't a robot, kill any existing non-robot sessions
                   9913: 	if (!$args->{'robot'}) {
                   9914: 	    opendir(DIR,$lonids);
                   9915: 	    while ($filename=readdir(DIR)) {
                   9916: 		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
                   9917: 		    unlink($lonids.'/'.$filename);
                   9918: 		}
1.462     albertel 9919: 	    }
1.463     albertel 9920: 	    closedir(DIR);
1.462     albertel 9921: 	}
                   9922: # Give them a new cookie
1.463     albertel 9923: 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684     www      9924: 		                   : $now.$$.int(rand(10000)));
1.463     albertel 9925: 	$cookie="$username\_$id\_$domain\_$authhost";
1.462     albertel 9926:     
                   9927: # Initialize roles
                   9928: 
                   9929: 	$userroles=&Apache::lonnet::rolesinit($domain,$username,$authhost);
                   9930:     }
                   9931: # ------------------------------------ Check browser type and MathML capability
                   9932: 
                   9933:     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
                   9934:         $clientunicode,$clientos) = &decode_user_agent($r);
                   9935: 
                   9936: # -------------------------------------- Any accessibility options to remember?
                   9937:     if (($form->{'interface'}) && ($form->{'remember'} eq 'true')) {
                   9938: 	foreach my $option ('imagesuppress','appletsuppress',
                   9939: 			    'embedsuppress','fontenhance','blackwhite') {
                   9940: 	    if ($form->{$option} eq 'true') {
                   9941: 		&Apache::lonnet::put('environment',{$option => 'on'},
                   9942: 				     $domain,$username);
                   9943: 	    } else {
                   9944: 		&Apache::lonnet::del('environment',[$option],
                   9945: 				     $domain,$username);
                   9946: 	    }
                   9947: 	}
                   9948:     }
                   9949: # ------------------------------------------------------------- Get environment
                   9950: 
                   9951:     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
                   9952:     my ($tmp) = keys(%userenv);
                   9953:     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   9954: 	# default remote control to off
                   9955: 	if ($userenv{'remote'} ne 'on') { $userenv{'remote'} = 'off'; }
                   9956:     } else {
                   9957: 	undef(%userenv);
                   9958:     }
                   9959:     if (($userenv{'interface'}) && (!$form->{'interface'})) {
                   9960: 	$form->{'interface'}=$userenv{'interface'};
                   9961:     }
                   9962:     $env{'environment.remote'}=$userenv{'remote'};
                   9963:     if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
                   9964: 
                   9965: # --------------- Do not trust query string to be put directly into environment
                   9966:     foreach my $option ('imagesuppress','appletsuppress',
                   9967: 			'embedsuppress','fontenhance','blackwhite',
                   9968: 			'interface','localpath','localres') {
                   9969: 	$form->{$option}=~s/[\n\r\=]//gs;
                   9970:     }
                   9971: # --------------------------------------------------------- Write first profile
                   9972: 
                   9973:     {
                   9974: 	my %initial_env = 
                   9975: 	    ("user.name"          => $username,
                   9976: 	     "user.domain"        => $domain,
                   9977: 	     "user.home"          => $authhost,
                   9978: 	     "browser.type"       => $clientbrowser,
                   9979: 	     "browser.version"    => $clientversion,
                   9980: 	     "browser.mathml"     => $clientmathml,
                   9981: 	     "browser.unicode"    => $clientunicode,
                   9982: 	     "browser.os"         => $clientos,
                   9983: 	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
                   9984: 	     "request.course.fn"  => '',
                   9985: 	     "request.course.uri" => '',
                   9986: 	     "request.course.sec" => '',
                   9987: 	     "request.role"       => 'cm',
                   9988: 	     "request.role.adv"   => $env{'user.adv'},
                   9989: 	     "request.host"       => $ENV{'REMOTE_ADDR'},);
                   9990: 
                   9991:         if ($form->{'localpath'}) {
                   9992: 	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
                   9993: 	    $initial_env{"browser.localres"}   = $form->{'localres'};
                   9994:         }
                   9995: 	
                   9996: 	if ($public) {
                   9997: 	    $initial_env{"environment.remote"} = "off";
                   9998: 	}
                   9999: 	if ($form->{'interface'}) {
                   10000: 	    $form->{'interface'}=~s/\W//gs;
                   10001: 	    $initial_env{"browser.interface"} = $form->{'interface'};
                   10002: 	    $env{'browser.interface'}=$form->{'interface'};
                   10003: 	    foreach my $option ('imagesuppress','appletsuppress',
                   10004: 				'embedsuppress','fontenhance','blackwhite') {
                   10005: 		if (($form->{$option} eq 'true') ||
                   10006: 		    ($userenv{$option} eq 'on')) {
                   10007: 		    $initial_env{"browser.$option"} = "on";
                   10008: 		}
                   10009: 	    }
                   10010: 	}
                   10011: 
1.692.4.2  raeburn  10012:         foreach my $tool ('aboutme','blog','portfolio') {
                   10013:             $userenv{'availabletools.'.$tool} =
                   10014:                 &Apache::lonnet::usertools_access($username,$domain,$tool,'reload');
                   10015:         }
                   10016: 
1.692.4.6  raeburn  10017:         foreach my $crstype ('official','unofficial','community') {
1.692.4.2  raeburn  10018:             $userenv{'canrequest.'.$crstype} =
                   10019:                 &Apache::lonnet::usertools_access($username,$domain,$crstype,
                   10020:                                                   'reload','requestcourses');
                   10021:         }
                   10022: 
1.462     albertel 10023: 	$env{'user.environment'} = "$lonids/$cookie.id";
                   10024: 	
                   10025: 	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
                   10026: 		 &GDBM_WRCREAT(),0640)) {
                   10027: 	    &_add_to_env(\%disk_env,\%initial_env);
                   10028: 	    &_add_to_env(\%disk_env,\%userenv,'environment.');
                   10029: 	    &_add_to_env(\%disk_env,$userroles);
1.463     albertel 10030: 	    if (ref($args->{'extra_env'})) {
                   10031: 		&_add_to_env(\%disk_env,$args->{'extra_env'});
                   10032: 	    }
1.462     albertel 10033: 	    untie(%disk_env);
                   10034: 	} else {
                   10035: 	    &Apache::lonnet::logthis("<font color=\"blue\">WARNING: ".
                   10036: 			   'Could not create environment storage in lonauth: '.$!.'</font>');
                   10037: 	    return 'error: '.$!;
                   10038: 	}
                   10039:     }
                   10040:     $env{'request.role'}='cm';
                   10041:     $env{'request.role.adv'}=$env{'user.adv'};
                   10042:     $env{'browser.type'}=$clientbrowser;
                   10043: 
                   10044:     return $cookie;
                   10045: 
                   10046: }
                   10047: 
                   10048: sub _add_to_env {
                   10049:     my ($idf,$env_data,$prefix) = @_;
1.676     raeburn  10050:     if (ref($env_data) eq 'HASH') {
                   10051:         while (my ($key,$value) = each(%$env_data)) {
                   10052: 	    $idf->{$prefix.$key} = $value;
                   10053: 	    $env{$prefix.$key}   = $value;
                   10054:         }
1.462     albertel 10055:     }
                   10056: }
                   10057: 
1.685     tempelho 10058: # --- Get the symbolic name of a problem and the url
                   10059: sub get_symb {
                   10060:     my ($request,$silent) = @_;
1.692.4.2  raeburn  10061:     (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685     tempelho 10062:     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
                   10063:     if ($symb eq '') {
                   10064:         if (!$silent) {
                   10065:             $request->print("Unable to handle ambiguous references:$url:.");
                   10066:             return ();
                   10067:         }
                   10068:     }
                   10069:     &Apache::lonenc::check_decrypt(\$symb);
                   10070:     return ($symb);
                   10071: }
                   10072: 
                   10073: # --------------------------------------------------------------Get annotation
                   10074: 
                   10075: sub get_annotation {
                   10076:     my ($symb,$enc) = @_;
                   10077: 
                   10078:     my $key = $symb;
                   10079:     if (!$enc) {
                   10080:         $key =
                   10081:             &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
                   10082:     }
                   10083:     my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
                   10084:     return $annotation{$key};
                   10085: }
                   10086: 
                   10087: sub clean_symb {
1.692.4.2  raeburn  10088:     my ($symb,$delete_enc) = @_;
1.685     tempelho 10089: 
                   10090:     &Apache::lonenc::check_decrypt(\$symb);
                   10091:     my $enc = $env{'request.enc'};
1.692.4.2  raeburn  10092:     if ($delete_enc) {
                   10093:         delete($env{'request.enc'});
                   10094:     }
1.685     tempelho 10095: 
                   10096:     return ($symb,$enc);
                   10097: }
1.462     albertel 10098: 
1.41      ng       10099: =pod
                   10100: 
                   10101: =back
                   10102: 
1.112     bowersj2 10103: =cut
1.41      ng       10104: 
1.112     bowersj2 10105: 1;
                   10106: __END__;
1.41      ng       10107: 

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