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

1.10      albertel    1: # The LearningOnline Network with CAPA
1.1       albertel    2: # a pile of common routines
1.10      albertel    3: #
1.905   ! raeburn     4: # $Id: loncommon.pm,v 1.904 2009/10/28 08:47:56 droeschl 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.705     tempelho  274:              "<span style='color:yellow;'>INFO: Read file types</span>");
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.776     bisitz    409: <script type="text/javascript" language="Javascript">
1.824     bisitz    410: // <![CDATA[
1.74      www       411:     var stdeditbrowser;
1.793     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.793     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.824     bisitz    433: // ]]>
1.74      www       434: </script>
                    435: ENDSTDBRW
                    436: }
1.42      matthew   437: 
1.74      www       438: sub selectstudent_link {
1.793     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.793     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.793     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";
1.776     bisitz    465: <script type="text/javascript" language="JavaScript">
1.824     bisitz    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: }
                    478: 
1.824     bisitz    479: // ]]>
1.653     raeburn   480: </script>
                    481: ENDAUTHORBRW
                    482: }
                    483: 
1.91      www       484: sub coursebrowser_javascript {
1.905   ! raeburn   485:     my ($domainfilter,$sec_element,$formname,$role_element)=@_;
1.886     raeburn   486:     my $crs_or_grp_alert = &mt('Please select the type of LON-CAPA entity - Course or Community - for which you wish to add/modify a user role.');
1.876     raeburn   487:     my $id_functions = &javascript_index_functions();
                    488:     my $output = '
1.776     bisitz    489: <script type="text/javascript" language="JavaScript">
1.824     bisitz    490: // <![CDATA[
1.468     raeburn   491:     var stdeditbrowser;'."\n";
1.876     raeburn   492: 
                    493:     $output .= <<"ENDSTDBRW";
1.377     raeburn   494:     function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,crstype) {
1.91      www       495:         var url = '/adm/pickcourse?';
1.895     raeburn   496:         var formid = getFormIdByName(formname);
1.876     raeburn   497:         var domainfilter = getDomainFromSelectbox(formname,udom);
1.128     albertel  498:         if (domainfilter != null) {
                    499:            if (domainfilter != '') {
                    500:                url += 'domainfilter='+domainfilter+'&';
                    501: 	   }
                    502:         }
1.91      www       503:         url += 'form=' + formname + '&cnumelement='+uname+
1.187     albertel  504: 	                            '&cdomelement='+udom+
                    505:                                     '&cnameelement='+desc;
1.468     raeburn   506:         if (extra_element !=null && extra_element != '') {
1.594     raeburn   507:             if (formname == 'rolechoice' || formname == 'studentform') {
1.468     raeburn   508:                 url += '&roleelement='+extra_element;
                    509:                 if (domainfilter == null || domainfilter == '') {
                    510:                     url += '&domainfilter='+extra_element;
                    511:                 }
1.234     raeburn   512:             }
1.468     raeburn   513:             else {
                    514:                 if (formname == 'portform') {
                    515:                     url += '&setroles='+extra_element;
1.800     raeburn   516:                 } else {
                    517:                     if (formname == 'rules') {
                    518:                         url += '&fixeddom='+extra_element; 
                    519:                     }
1.468     raeburn   520:                 }
                    521:             }     
1.230     raeburn   522:         }
1.872     raeburn   523:         if (formname == 'ccrs') {
                    524:             var ownername = document.forms[formid].ccuname.value;
                    525:             var ownerdom =  document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
                    526:             url += '&cloner='+ownername+':'+ownerdom;
                    527:         }
1.293     raeburn   528:         if (multflag !=null && multflag != '') {
                    529:             url += '&multiple='+multflag;
                    530:         }
1.865     raeburn   531:         if (crstype == 'Course/Community') {
1.377     raeburn   532:             if (formname == 'cu') {
                    533:                 crstype = document.cu.crstype.options[document.cu.crstype.selectedIndex].value; 
                    534:                 if (crstype == "") {
                    535:                     alert("$crs_or_grp_alert");
                    536:                     return;
                    537:                 }
                    538:             }
                    539:         }
                    540:         if (crstype !=null && crstype != '') {
                    541:             url += '&type='+crstype;
                    542:         }
1.102     www       543:         var title = 'Course_Browser';
1.91      www       544:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    545:         options += ',width=700,height=600';
                    546:         stdeditbrowser = open(url,title,options,'1');
                    547:         stdeditbrowser.focus();
                    548:     }
1.876     raeburn   549: $id_functions
                    550: ENDSTDBRW
1.905   ! raeburn   551:     if (($sec_element ne '') || ($role_element ne '')) {
        !           552:         $output .= &setsec_javascript($sec_element,$formname,$role_element);
1.876     raeburn   553:     }
                    554:     $output .= '
                    555: // ]]>
                    556: </script>';
                    557:     return $output;
                    558: }
                    559: 
                    560: sub javascript_index_functions {
                    561:     return <<"ENDJS";
                    562: 
                    563: function getFormIdByName(formname) {
                    564:     for (var i=0;i<document.forms.length;i++) {
                    565:         if (document.forms[i].name == formname) {
                    566:             return i;
                    567:         }
                    568:     }
                    569:     return -1;
                    570: }
                    571: 
                    572: function getIndexByName(formid,item) {
                    573:     for (var i=0;i<document.forms[formid].elements.length;i++) {
                    574:         if (document.forms[formid].elements[i].name == item) {
                    575:             return i;
                    576:         }
                    577:     }
                    578:     return -1;
                    579: }
1.468     raeburn   580: 
1.876     raeburn   581: function getDomainFromSelectbox(formname,udom) {
                    582:     var userdom;
                    583:     var formid = getFormIdByName(formname);
                    584:     if (formid > -1) {
                    585:         var domid = getIndexByName(formid,udom);
                    586:         if (domid > -1) {
                    587:             if (document.forms[formid].elements[domid].type == 'select-one') {
                    588:                 userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
                    589:             }
                    590:             if (document.forms[formid].elements[domid].type == 'hidden') {
                    591:                 userdom=document.forms[formid].elements[domid].value;
1.468     raeburn   592:             }
                    593:         }
                    594:     }
1.876     raeburn   595:     return userdom;
                    596: }
                    597: 
                    598: ENDJS
1.468     raeburn   599: 
1.876     raeburn   600: }
                    601: 
                    602: sub userbrowser_javascript {
                    603:     my $id_functions = &javascript_index_functions();
                    604:     return <<"ENDUSERBRW";
                    605: 
1.888     raeburn   606: function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
1.876     raeburn   607:     var url = '/adm/pickuser?';
                    608:     var userdom = getDomainFromSelectbox(formname,udom);
                    609:     if (userdom != null) {
                    610:        if (userdom != '') {
                    611:            url += 'srchdom='+userdom+'&';
                    612:        }
                    613:     }
                    614:     url += 'form=' + formname + '&unameelement='+uname+
                    615:                                 '&udomelement='+udom+
                    616:                                 '&ulastelement='+ulast+
                    617:                                 '&ufirstelement='+ufirst+
                    618:                                 '&uemailelement='+uemail+
1.881     raeburn   619:                                 '&hideudomelement='+hideudom+
                    620:                                 '&coursedom='+crsdom;
1.888     raeburn   621:     if ((caller != null) && (caller != undefined)) {
                    622:         url += '&caller='+caller;
                    623:     }
1.876     raeburn   624:     var title = 'User_Browser';
                    625:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    626:     options += ',width=700,height=600';
                    627:     var stdeditbrowser = open(url,title,options,'1');
                    628:     stdeditbrowser.focus();
                    629: }
                    630: 
1.888     raeburn   631: function fix_domain (formname,udom,origdom,uname) {
1.876     raeburn   632:     var formid = getFormIdByName(formname);
                    633:     if (formid > -1) {
1.888     raeburn   634:         var unameid = getIndexByName(formid,uname);
1.876     raeburn   635:         var domid = getIndexByName(formid,udom);
                    636:         var hidedomid = getIndexByName(formid,origdom);
                    637:         if (hidedomid > -1) {
                    638:             var fixeddom = document.forms[formid].elements[hidedomid].value;
1.888     raeburn   639:             var unameval = document.forms[formid].elements[unameid].value;
                    640:             if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
                    641:                 if (domid > -1) {
                    642:                     var slct = document.forms[formid].elements[domid];
                    643:                     if (slct.type == 'select-one') {
                    644:                         var i;
                    645:                         for (i=0;i<slct.length;i++) {
                    646:                             if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
                    647:                         }
                    648:                     }
                    649:                     if (slct.type == 'hidden') {
                    650:                         slct.value = fixeddom;
1.876     raeburn   651:                     }
                    652:                 }
1.468     raeburn   653:             }
                    654:         }
                    655:     }
1.876     raeburn   656:     return;
                    657: }
                    658: 
                    659: $id_functions
                    660: ENDUSERBRW
1.468     raeburn   661: }
                    662: 
                    663: sub setsec_javascript {
1.905   ! raeburn   664:     my ($sec_element,$formname,$role_element) = @_;
        !           665:     my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
        !           666:         $communityrolestr);
        !           667:     if ($role_element ne '') {
        !           668:         my @allroles = ('st','ta','ep','in','ad');
        !           669:         foreach my $crstype ('Course','Community') {
        !           670:             if ($crstype eq 'Community') {
        !           671:                 foreach my $role (@allroles) {
        !           672:                     push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
        !           673:                 }
        !           674:                 push(@communityrolenames,&Apache::lonnet::plaintext('co'));
        !           675:             } else {
        !           676:                 foreach my $role (@allroles) {
        !           677:                     push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
        !           678:                 }
        !           679:                 push(@courserolenames,&Apache::lonnet::plaintext('cc'));
        !           680:             }
        !           681:         }
        !           682:         $rolestr = '"'.join('","',@allroles).'"';
        !           683:         $courserolestr = '"'.join('","',@courserolenames).'"';
        !           684:         $communityrolestr = '"'.join('","',@communityrolenames).'"';
        !           685:     }
1.468     raeburn   686:     my $setsections = qq|
                    687: function setSect(sectionlist) {
1.629     raeburn   688:     var sectionsArray = new Array();
                    689:     if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
                    690:         sectionsArray = sectionlist.split(",");
                    691:     }
1.468     raeburn   692:     var numSections = sectionsArray.length;
                    693:     document.$formname.$sec_element.length = 0;
                    694:     if (numSections == 0) {
                    695:         document.$formname.$sec_element.multiple=false;
                    696:         document.$formname.$sec_element.size=1;
                    697:         document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
                    698:     } else {
                    699:         if (numSections == 1) {
                    700:             document.$formname.$sec_element.multiple=false;
                    701:             document.$formname.$sec_element.size=1;
                    702:             document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
                    703:             document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
                    704:             document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
                    705:         } else {
                    706:             for (var i=0; i<numSections; i++) {
                    707:                 document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
                    708:             }
                    709:             document.$formname.$sec_element.multiple=true
                    710:             if (numSections < 3) {
                    711:                 document.$formname.$sec_element.size=numSections;
                    712:             } else {
                    713:                 document.$formname.$sec_element.size=3;
                    714:             }
                    715:             document.$formname.$sec_element.options[0].selected = false
                    716:         }
                    717:     }
1.91      www       718: }
1.905   ! raeburn   719: 
        !           720: function setRole(crstype) {
1.468     raeburn   721: |;
1.905   ! raeburn   722:     if ($role_element eq '') {
        !           723:         $setsections .= '    return;
        !           724: }
        !           725: ';
        !           726:     } else {
        !           727:         $setsections .= qq|
        !           728:     var elementLength = document.$formname.$role_element.length;
        !           729:     var allroles = Array($rolestr);
        !           730:     var courserolenames = Array($courserolestr);
        !           731:     var communityrolenames = Array($communityrolestr);
        !           732:     if (elementLength != undefined) {
        !           733:         if (document.$formname.$role_element.options[5].value == 'cc') {
        !           734:             if (crstype == 'Course') {
        !           735:                 return;
        !           736:             } else {
        !           737:                 allroles[5] = 'co';
        !           738:                 for (var i=0; i<6; i++) {
        !           739:                     document.$formname.$role_element.options[i].value = allroles[i];
        !           740:                     document.$formname.$role_element.options[i].text = communityrolenames[i];
        !           741:                 }
        !           742:             }
        !           743:         } else {
        !           744:             if (crstype == 'Community') {
        !           745:                 return;
        !           746:             } else {
        !           747:                 allroles[5] = 'cc';
        !           748:                 for (var i=0; i<6; i++) {
        !           749:                     document.$formname.$role_element.options[i].value = allroles[i];
        !           750:                     document.$formname.$role_element.options[i].text = courserolenames[i];
        !           751:                 }
        !           752:             }
        !           753:         }
        !           754:     }
        !           755:     return;
        !           756: }
        !           757: |;
        !           758:     }
1.468     raeburn   759:     return $setsections;
                    760: }
                    761: 
1.91      www       762: sub selectcourse_link {
1.377     raeburn   763:    my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype)=@_;
1.871     raeburn   764:    my $linktext = &mt('Select Course');
                    765:    if ($selecttype eq 'Community') {
                    766:        $linktext = &mt('Select Community'); 
                    767:    }
1.787     bisitz    768:    return '<span class="LC_nobreak">'
                    769:          ."<a href='"
                    770:          .'javascript:opencrsbrowser("'.$form.'","'.$unameele
                    771:          .'","'.$udomele.'","'.$desc.'","'.$extra_element
                    772:          .'","'.$multflag.'","'.$selecttype.'");'
1.871     raeburn   773:          ."'>".$linktext.'</a>'
1.787     bisitz    774:          .'</span>';
1.74      www       775: }
1.42      matthew   776: 
1.653     raeburn   777: sub selectauthor_link {
                    778:    my ($form,$udom)=@_;
                    779:    return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
                    780:           &mt('Select Author').'</a>';
                    781: }
                    782: 
1.876     raeburn   783: sub selectuser_link {
1.881     raeburn   784:     my ($form,$unameelem,$domelem,$lastelem,$firstelem,$emailelem,$hdomelem,
1.888     raeburn   785:         $coursedom,$linktext,$caller) = @_;
1.876     raeburn   786:     return '<a href="javascript:openuserbrowser('."'$form','$unameelem','$domelem',".
1.888     raeburn   787:            "'$lastelem','$firstelem','$emailelem','$hdomelem','$coursedom','$caller'".
1.881     raeburn   788:            ');">'.$linktext.'</a>';
1.876     raeburn   789: }
                    790: 
1.273     raeburn   791: sub check_uncheck_jscript {
                    792:     my $jscript = <<"ENDSCRT";
                    793: function checkAll(field) {
                    794:     if (field.length > 0) {
                    795:         for (i = 0; i < field.length; i++) {
                    796:             field[i].checked = true ;
                    797:         }
                    798:     } else {
                    799:         field.checked = true
                    800:     }
                    801: }
                    802:  
                    803: function uncheckAll(field) {
                    804:     if (field.length > 0) {
                    805:         for (i = 0; i < field.length; i++) {
                    806:             field[i].checked = false ;
1.543     albertel  807:         }
                    808:     } else {
1.273     raeburn   809:         field.checked = false ;
                    810:     }
                    811: }
                    812: ENDSCRT
                    813:     return $jscript;
                    814: }
                    815: 
1.656     www       816: sub select_timezone {
1.659     raeburn   817:    my ($name,$selected,$onchange,$includeempty)=@_;
                    818:    my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
                    819:    if ($includeempty) {
                    820:        $output .= '<option value=""';
                    821:        if (($selected eq '') || ($selected eq 'local')) {
                    822:            $output .= ' selected="selected" ';
                    823:        }
                    824:        $output .= '> </option>';
                    825:    }
1.657     raeburn   826:    my @timezones = DateTime::TimeZone->all_names;
                    827:    foreach my $tzone (@timezones) {
                    828:        $output.= '<option value="'.$tzone.'"';
                    829:        if ($tzone eq $selected) {
                    830:            $output.=' selected="selected"';
                    831:        }
                    832:        $output.=">$tzone</option>\n";
1.656     www       833:    }
                    834:    $output.="</select>";
                    835:    return $output;
                    836: }
1.273     raeburn   837: 
1.687     raeburn   838: sub select_datelocale {
                    839:     my ($name,$selected,$onchange,$includeempty)=@_;
                    840:     my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
                    841:     if ($includeempty) {
                    842:         $output .= '<option value=""';
                    843:         if ($selected eq '') {
                    844:             $output .= ' selected="selected" ';
                    845:         }
                    846:         $output .= '> </option>';
                    847:     }
                    848:     my (@possibles,%locale_names);
                    849:     my @locales = DateTime::Locale::Catalog::Locales;
                    850:     foreach my $locale (@locales) {
                    851:         if (ref($locale) eq 'HASH') {
                    852:             my $id = $locale->{'id'};
                    853:             if ($id ne '') {
                    854:                 my $en_terr = $locale->{'en_territory'};
                    855:                 my $native_terr = $locale->{'native_territory'};
1.695     raeburn   856:                 my @languages = &Apache::lonlocal::preferred_languages();
1.687     raeburn   857:                 if (grep(/^en$/,@languages) || !@languages) {
                    858:                     if ($en_terr ne '') {
                    859:                         $locale_names{$id} = '('.$en_terr.')';
                    860:                     } elsif ($native_terr ne '') {
                    861:                         $locale_names{$id} = $native_terr;
                    862:                     }
                    863:                 } else {
                    864:                     if ($native_terr ne '') {
                    865:                         $locale_names{$id} = $native_terr.' ';
                    866:                     } elsif ($en_terr ne '') {
                    867:                         $locale_names{$id} = '('.$en_terr.')';
                    868:                     }
                    869:                 }
                    870:                 push (@possibles,$id);
                    871:             }
                    872:         }
                    873:     }
                    874:     foreach my $item (sort(@possibles)) {
                    875:         $output.= '<option value="'.$item.'"';
                    876:         if ($item eq $selected) {
                    877:             $output.=' selected="selected"';
                    878:         }
                    879:         $output.=">$item";
                    880:         if ($locale_names{$item} ne '') {
                    881:             $output.="  $locale_names{$item}</option>\n";
                    882:         }
                    883:         $output.="</option>\n";
                    884:     }
                    885:     $output.="</select>";
                    886:     return $output;
                    887: }
                    888: 
1.792     raeburn   889: sub select_language {
                    890:     my ($name,$selected,$includeempty) = @_;
                    891:     my %langchoices;
                    892:     if ($includeempty) {
                    893:         %langchoices = ('' => 'No language preference');
                    894:     }
                    895:     foreach my $id (&languageids()) {
                    896:         my $code = &supportedlanguagecode($id);
                    897:         if ($code) {
                    898:             $langchoices{$code} = &plainlanguagedescription($id);
                    899:         }
                    900:     }
                    901:     return &select_form($selected,$name,%langchoices);
                    902: }
                    903: 
1.42      matthew   904: =pod
1.36      matthew   905: 
1.648     raeburn   906: =item * &linked_select_forms(...)
1.36      matthew   907: 
                    908: linked_select_forms returns a string containing a <script></script> block
                    909: and html for two <select> menus.  The select menus will be linked in that
                    910: changing the value of the first menu will result in new values being placed
                    911: in the second menu.  The values in the select menu will appear in alphabetical
1.609     raeburn   912: order unless a defined order is provided.
1.36      matthew   913: 
                    914: linked_select_forms takes the following ordered inputs:
                    915: 
                    916: =over 4
                    917: 
1.112     bowersj2  918: =item * $formname, the name of the <form> tag
1.36      matthew   919: 
1.112     bowersj2  920: =item * $middletext, the text which appears between the <select> tags
1.36      matthew   921: 
1.112     bowersj2  922: =item * $firstdefault, the default value for the first menu
1.36      matthew   923: 
1.112     bowersj2  924: =item * $firstselectname, the name of the first <select> tag
1.36      matthew   925: 
1.112     bowersj2  926: =item * $secondselectname, the name of the second <select> tag
1.36      matthew   927: 
1.112     bowersj2  928: =item * $hashref, a reference to a hash containing the data for the menus.
1.36      matthew   929: 
1.609     raeburn   930: =item * $menuorder, the order of values in the first menu
                    931: 
1.41      ng        932: =back 
                    933: 
1.36      matthew   934: Below is an example of such a hash.  Only the 'text', 'default', and 
                    935: 'select2' keys must appear as stated.  keys(%menu) are the possible 
                    936: values for the first select menu.  The text that coincides with the 
1.41      ng        937: first menu value is given in $menu{$choice1}->{'text'}.  The values 
1.36      matthew   938: and text for the second menu are given in the hash pointed to by 
                    939: $menu{$choice1}->{'select2'}.  
                    940: 
1.112     bowersj2  941:  my %menu = ( A1 => { text =>"Choice A1" ,
                    942:                        default => "B3",
                    943:                        select2 => { 
                    944:                            B1 => "Choice B1",
                    945:                            B2 => "Choice B2",
                    946:                            B3 => "Choice B3",
                    947:                            B4 => "Choice B4"
1.609     raeburn   948:                            },
                    949:                        order => ['B4','B3','B1','B2'],
1.112     bowersj2  950:                    },
                    951:                A2 => { text =>"Choice A2" ,
                    952:                        default => "C2",
                    953:                        select2 => { 
                    954:                            C1 => "Choice C1",
                    955:                            C2 => "Choice C2",
                    956:                            C3 => "Choice C3"
1.609     raeburn   957:                            },
                    958:                        order => ['C2','C1','C3'],
1.112     bowersj2  959:                    },
                    960:                A3 => { text =>"Choice A3" ,
                    961:                        default => "D6",
                    962:                        select2 => { 
                    963:                            D1 => "Choice D1",
                    964:                            D2 => "Choice D2",
                    965:                            D3 => "Choice D3",
                    966:                            D4 => "Choice D4",
                    967:                            D5 => "Choice D5",
                    968:                            D6 => "Choice D6",
                    969:                            D7 => "Choice D7"
1.609     raeburn   970:                            },
                    971:                        order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112     bowersj2  972:                    }
                    973:                );
1.36      matthew   974: 
                    975: =cut
                    976: 
                    977: sub linked_select_forms {
                    978:     my ($formname,
                    979:         $middletext,
                    980:         $firstdefault,
                    981:         $firstselectname,
                    982:         $secondselectname, 
1.609     raeburn   983:         $hashref,
                    984:         $menuorder,
1.36      matthew   985:         ) = @_;
                    986:     my $second = "document.$formname.$secondselectname";
                    987:     my $first = "document.$formname.$firstselectname";
                    988:     # output the javascript to do the changing
                    989:     my $result = '';
1.776     bisitz    990:     $result.='<script type="text/javascript" language="JavaScript">'."\n";
1.824     bisitz    991:     $result.="// <![CDATA[\n";
1.36      matthew   992:     $result.="var select2data = new Object();\n";
                    993:     $" = '","';
                    994:     my $debug = '';
                    995:     foreach my $s1 (sort(keys(%$hashref))) {
                    996:         $result.="select2data.d_$s1 = new Object();\n";        
                    997:         $result.="select2data.d_$s1.def = new String('".
                    998:             $hashref->{$s1}->{'default'}."');\n";
1.609     raeburn   999:         $result.="select2data.d_$s1.values = new Array(";
1.36      matthew  1000:         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609     raeburn  1001:         if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
                   1002:             @s2values = @{$hashref->{$s1}->{'order'}};
                   1003:         }
1.36      matthew  1004:         $result.="\"@s2values\");\n";
                   1005:         $result.="select2data.d_$s1.texts = new Array(";        
                   1006:         my @s2texts;
                   1007:         foreach my $value (@s2values) {
                   1008:             push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
                   1009:         }
                   1010:         $result.="\"@s2texts\");\n";
                   1011:     }
                   1012:     $"=' ';
                   1013:     $result.= <<"END";
                   1014: 
                   1015: function select1_changed() {
                   1016:     // Determine new choice
                   1017:     var newvalue = "d_" + $first.value;
                   1018:     // update select2
                   1019:     var values     = select2data[newvalue].values;
                   1020:     var texts      = select2data[newvalue].texts;
                   1021:     var select2def = select2data[newvalue].def;
                   1022:     var i;
                   1023:     // out with the old
                   1024:     for (i = 0; i < $second.options.length; i++) {
                   1025:         $second.options[i] = null;
                   1026:     }
                   1027:     // in with the nuclear
                   1028:     for (i=0;i<values.length; i++) {
                   1029:         $second.options[i] = new Option(values[i]);
1.143     matthew  1030:         $second.options[i].value = values[i];
1.36      matthew  1031:         $second.options[i].text = texts[i];
                   1032:         if (values[i] == select2def) {
                   1033:             $second.options[i].selected = true;
                   1034:         }
                   1035:     }
                   1036: }
1.824     bisitz   1037: // ]]>
1.36      matthew  1038: </script>
                   1039: END
                   1040:     # output the initial values for the selection lists
                   1041:     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed()\">\n";
1.609     raeburn  1042:     my @order = sort(keys(%{$hashref}));
                   1043:     if (ref($menuorder) eq 'ARRAY') {
                   1044:         @order = @{$menuorder};
                   1045:     }
                   1046:     foreach my $value (@order) {
1.36      matthew  1047:         $result.="    <option value=\"$value\" ";
1.253     albertel 1048:         $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119     www      1049:         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36      matthew  1050:     }
                   1051:     $result .= "</select>\n";
                   1052:     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
                   1053:     $result .= $middletext;
                   1054:     $result .= "<select size=\"1\" name=\"$secondselectname\">\n";
                   1055:     my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609     raeburn  1056:     
                   1057:     my @secondorder = sort(keys(%select2));
                   1058:     if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
                   1059:         @secondorder = @{$hashref->{$firstdefault}->{'order'}};
                   1060:     }
                   1061:     foreach my $value (@secondorder) {
1.36      matthew  1062:         $result.="    <option value=\"$value\" ";        
1.253     albertel 1063:         $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119     www      1064:         $result.=">".&mt($select2{$value})."</option>\n";
1.36      matthew  1065:     }
                   1066:     $result .= "</select>\n";
                   1067:     #    return $debug;
                   1068:     return $result;
                   1069: }   #  end of sub linked_select_forms {
                   1070: 
1.45      matthew  1071: =pod
1.44      bowersj2 1072: 
1.648     raeburn  1073: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height)
1.44      bowersj2 1074: 
1.112     bowersj2 1075: Returns a string corresponding to an HTML link to the given help
                   1076: $topic, where $topic corresponds to the name of a .tex file in
                   1077: /home/httpd/html/adm/help/tex, with underscores replaced by
                   1078: spaces. 
                   1079: 
                   1080: $text will optionally be linked to the same topic, allowing you to
                   1081: link text in addition to the graphic. If you do not want to link
                   1082: text, but wish to specify one of the later parameters, pass an
                   1083: empty string. 
                   1084: 
                   1085: $stayOnPage is a value that will be interpreted as a boolean. If true,
                   1086: the link will not open a new window. If false, the link will open
                   1087: a new window using Javascript. (Default is false.) 
                   1088: 
                   1089: $width and $height are optional numerical parameters that will
                   1090: override the width and height of the popped up window, which may
                   1091: be useful for certain help topics with big pictures included. 
1.44      bowersj2 1092: 
                   1093: =cut
                   1094: 
                   1095: sub help_open_topic {
1.48      bowersj2 1096:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
                   1097:     $text = "" if (not defined $text);
1.44      bowersj2 1098:     $stayOnPage = 0 if (not defined $stayOnPage);
                   1099:     $width = 350 if (not defined $width);
                   1100:     $height = 400 if (not defined $height);
                   1101:     my $filename = $topic;
                   1102:     $filename =~ s/ /_/g;
                   1103: 
1.48      bowersj2 1104:     my $template = "";
                   1105:     my $link;
1.572     banghart 1106:     
1.159     www      1107:     $topic=~s/\W/\_/g;
1.44      bowersj2 1108: 
1.572     banghart 1109:     if (!$stayOnPage) {
1.72      bowersj2 1110: 	$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 1111:     } else {
1.48      bowersj2 1112: 	$link = "/adm/help/${filename}.hlp";
                   1113:     }
                   1114: 
                   1115:     # Add the text
1.755     neumanie 1116:     if ($text ne "") {	
1.763     bisitz   1117: 	$template.='<span class="LC_help_open_topic">'
                   1118:                   .'<a target="_top" href="'.$link.'">'
                   1119:                   .$text.'</a>';
1.48      bowersj2 1120:     }
                   1121: 
1.763     bisitz   1122:     # (Always) Add the graphic
1.179     matthew  1123:     my $title = &mt('Online Help');
1.667     raeburn  1124:     my $helpicon=&lonhttpdurl("/adm/help/help.png");
1.763     bisitz   1125:     $template.=' <a target="_top" href="'.$link.'" title="'.$title.'">'
                   1126:               .'<img src="'.$helpicon.'" border="0"'
                   1127:               .' alt="'.&mt('Help: [_1]',$topic).'"'
1.783     amueller 1128:               .' title="'.$title.'"' 
1.763     bisitz   1129:               .' /></a>';
                   1130:     if ($text ne "") {	
                   1131:         $template.='</span>';
                   1132:     }
1.44      bowersj2 1133:     return $template;
                   1134: 
1.106     bowersj2 1135: }
                   1136: 
                   1137: # This is a quicky function for Latex cheatsheet editing, since it 
                   1138: # appears in at least four places
                   1139: sub helpLatexCheatsheet {
1.732     raeburn  1140:     my ($topic,$text,$not_author) = @_;
                   1141:     my $out;
1.106     bowersj2 1142:     my $addOther = '';
1.732     raeburn  1143:     if ($topic) {
1.763     bisitz   1144: 	$addOther = '<span>'.&Apache::loncommon::help_open_topic($topic,&mt($text),
                   1145: 							       undef, undef, 600).
                   1146: 								   '</span> ';
                   1147:     }
                   1148:     $out = '<span>' # Start cheatsheet
                   1149: 	  .$addOther
                   1150:           .'<span>'
                   1151: 	  .&Apache::loncommon::help_open_topic('Greek_Symbols',&mt('Greek Symbols'),
                   1152: 					       undef,undef,600)
                   1153: 	  .'</span> <span>'
                   1154: 	  .&Apache::loncommon::help_open_topic('Other_Symbols',&mt('Other Symbols'),
                   1155: 					       undef,undef,600)
                   1156: 	  .'</span>';
1.732     raeburn  1157:     unless ($not_author) {
1.763     bisitz   1158:         $out .= ' <span>'
                   1159: 	       .&Apache::loncommon::help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),
                   1160: 	                                            undef,undef,600)
                   1161: 	       .'</span>';
1.732     raeburn  1162:     }
1.763     bisitz   1163:     $out .= '</span>'; # End cheatsheet
1.732     raeburn  1164:     return $out;
1.172     www      1165: }
                   1166: 
1.430     albertel 1167: sub general_help {
                   1168:     my $helptopic='Student_Intro';
                   1169:     if ($env{'request.role'}=~/^(ca|au)/) {
                   1170: 	$helptopic='Authoring_Intro';
                   1171:     } elsif ($env{'request.role'}=~/^cc/) {
                   1172: 	$helptopic='Course_Coordination_Intro';
1.672     raeburn  1173:     } elsif ($env{'request.role'}=~/^dc/) {
                   1174:         $helptopic='Domain_Coordination_Intro';
1.430     albertel 1175:     }
                   1176:     return $helptopic;
                   1177: }
                   1178: 
                   1179: sub update_help_link {
                   1180:     my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
                   1181:     my $origurl = $ENV{'REQUEST_URI'};
                   1182:     $origurl=~s|^/~|/priv/|;
                   1183:     my $timestamp = time;
                   1184:     foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
                   1185:         $$datum = &escape($$datum);
                   1186:     }
                   1187: 
                   1188:     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";
                   1189:     my $output .= <<"ENDOUTPUT";
                   1190: <script type="text/javascript">
1.824     bisitz   1191: // <![CDATA[
1.430     albertel 1192: banner_link = '$banner_link';
1.824     bisitz   1193: // ]]>
1.430     albertel 1194: </script>
                   1195: ENDOUTPUT
                   1196:     return $output;
                   1197: }
                   1198: 
                   1199: # now just updates the help link and generates a blue icon
1.193     raeburn  1200: sub help_open_menu {
1.430     albertel 1201:     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text) 
1.552     banghart 1202: 	= @_;    
1.430     albertel 1203:     $stayOnPage = 0 if (not defined $stayOnPage);
1.572     banghart 1204:     # only use pop-up help (stayOnPage == 0)
1.552     banghart 1205:     # if environment.remote is on (using remote control UI)
1.798     tempelho 1206:     if ($env{'environment.remote'} eq 'off' ) {
1.552     banghart 1207:         $stayOnPage=1;
1.430     albertel 1208:     }
                   1209:     my $output;
                   1210:     if ($component_help) {
                   1211: 	if (!$text) {
                   1212: 	    $output=&help_open_topic($component_help,undef,$stayOnPage,
                   1213: 				       $width,$height);
                   1214: 	} else {
                   1215: 	    my $help_text;
                   1216: 	    $help_text=&unescape($topic);
                   1217: 	    $output='<table><tr><td>'.
                   1218: 		&help_open_topic($component_help,$help_text,$stayOnPage,
                   1219: 				 $width,$height).'</td></tr></table>';
                   1220: 	}
                   1221:     }
                   1222:     my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
                   1223:     return $output.$banner_link;
                   1224: }
                   1225: 
                   1226: sub top_nav_help {
                   1227:     my ($text) = @_;
1.436     albertel 1228:     $text = &mt($text);
1.572     banghart 1229:     my $stay_on_page = 
1.798     tempelho 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 = 
1.798     tempelho 1247: 	($env{'environment.remote'} eq 'off' );
1.436     albertel 1248: 
                   1249:     my $width = 620;
                   1250:     my $height = 600;
1.430     albertel 1251:     my $helptopic=&general_help();
                   1252:     my $details_link = '/adm/help/'.$helptopic.'.hlp';
1.261     albertel 1253:     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331     albertel 1254:     my $start_page =
                   1255:         &Apache::loncommon::start_page('Help Menu', undef,
                   1256: 				       {'frameset'    => 1,
                   1257: 					'js_ready'    => 1,
                   1258: 					'add_entries' => {
                   1259: 					    'border' => '0',
1.579     raeburn  1260: 					    'rows'   => "110,*",},});
1.331     albertel 1261:     my $end_page =
                   1262:         &Apache::loncommon::end_page({'frameset' => 1,
                   1263: 				      'js_ready' => 1,});
                   1264: 
1.436     albertel 1265:     my $template .= <<"ENDTEMPLATE";
                   1266: <script type="text/javascript">
1.877     bisitz   1267: // <![CDATA[
1.253     albertel 1268: // <!-- BEGIN LON-CAPA Internal
1.430     albertel 1269: var banner_link = '';
1.243     raeburn  1270: function helpMenu(target) {
                   1271:     var caller = this;
                   1272:     if (target == 'open') {
                   1273:         var newWindow = null;
                   1274:         try {
1.262     albertel 1275:             newWindow =  window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243     raeburn  1276:         }
                   1277:         catch(error) {
                   1278:             writeHelp(caller);
                   1279:             return;
                   1280:         }
                   1281:         if (newWindow) {
                   1282:             caller = newWindow;
                   1283:         }
1.193     raeburn  1284:     }
1.243     raeburn  1285:     writeHelp(caller);
                   1286:     return;
                   1287: }
                   1288: function writeHelp(caller) {
1.430     albertel 1289:     caller.document.writeln('$start_page<frame name="bannerframe"  src="'+banner_link+'" /><frame name="bodyframe" src="$details_link" /> $end_page')
1.243     raeburn  1290:     caller.document.close()
                   1291:     caller.focus()
1.193     raeburn  1292: }
1.877     bisitz   1293: // END LON-CAPA Internal -->
1.253     albertel 1294: // ]]>
1.436     albertel 1295: </script>
1.193     raeburn  1296: ENDTEMPLATE
                   1297:     return $template;
                   1298: }
                   1299: 
1.172     www      1300: sub help_open_bug {
                   1301:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1302:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1303:     unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
                   1304:     $text = "" if (not defined $text);
                   1305:     $stayOnPage = 0 if (not defined $stayOnPage);
1.798     tempelho 1306:     if ($env{'environment.remote'} eq 'off' ) {
1.172     www      1307: 	$stayOnPage=1;
                   1308:     }
1.184     albertel 1309:     $width = 600 if (not defined $width);
                   1310:     $height = 600 if (not defined $height);
1.172     www      1311: 
                   1312:     $topic=~s/\W+/\+/g;
                   1313:     my $link='';
                   1314:     my $template='';
1.379     albertel 1315:     my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&amp;bug_file_loc='.
                   1316: 	&escape($ENV{'REQUEST_URI'}).'&amp;component='.$topic;
1.172     www      1317:     if (!$stayOnPage)
                   1318:     {
                   1319: 	$link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1320:     }
                   1321:     else
                   1322:     {
                   1323: 	$link = $url;
                   1324:     }
                   1325:     # Add the text
                   1326:     if ($text ne "")
                   1327:     {
                   1328: 	$template .= 
                   1329:   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705     tempelho 1330:   "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
1.172     www      1331:     }
                   1332: 
                   1333:     # Add the graphic
1.179     matthew  1334:     my $title = &mt('Report a Bug');
1.215     albertel 1335:     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172     www      1336:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1337:  <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172     www      1338: ENDTEMPLATE
                   1339:     if ($text ne '') { $template.='</td></tr></table>' };
                   1340:     return $template;
                   1341: 
                   1342: }
                   1343: 
                   1344: sub help_open_faq {
                   1345:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1346:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1347:     unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
                   1348:     $text = "" if (not defined $text);
                   1349:     $stayOnPage = 0 if (not defined $stayOnPage);
1.798     tempelho 1350:     if ($env{'environment.remote'} eq 'off' ) {
1.172     www      1351: 	$stayOnPage=1;
                   1352:     }
                   1353:     $width = 350 if (not defined $width);
                   1354:     $height = 400 if (not defined $height);
                   1355: 
                   1356:     $topic=~s/\W+/\+/g;
                   1357:     my $link='';
                   1358:     my $template='';
                   1359:     my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
                   1360:     if (!$stayOnPage)
                   1361:     {
                   1362: 	$link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1363:     }
                   1364:     else
                   1365:     {
                   1366: 	$link = $url;
                   1367:     }
                   1368: 
                   1369:     # Add the text
                   1370:     if ($text ne "")
                   1371:     {
                   1372: 	$template .= 
1.173     www      1373:   "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705     tempelho 1374:   "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>";
1.172     www      1375:     }
                   1376: 
                   1377:     # Add the graphic
1.179     matthew  1378:     my $title = &mt('View the FAQ');
1.215     albertel 1379:     my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172     www      1380:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1381:  <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172     www      1382: ENDTEMPLATE
                   1383:     if ($text ne '') { $template.='</td></tr></table>' };
                   1384:     return $template;
                   1385: 
1.44      bowersj2 1386: }
1.37      matthew  1387: 
1.180     matthew  1388: ###############################################################
                   1389: ###############################################################
                   1390: 
1.45      matthew  1391: =pod
                   1392: 
1.648     raeburn  1393: =item * &change_content_javascript():
1.256     matthew  1394: 
                   1395: This and the next function allow you to create small sections of an
                   1396: otherwise static HTML page that you can update on the fly with
                   1397: Javascript, even in Netscape 4.
                   1398: 
                   1399: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
                   1400: must be written to the HTML page once. It will prove the Javascript
                   1401: function "change(name, content)". Calling the change function with the
                   1402: name of the section 
                   1403: you want to update, matching the name passed to C<changable_area>, and
                   1404: the new content you want to put in there, will put the content into
                   1405: that area.
                   1406: 
                   1407: B<Note>: Netscape 4 only reserves enough space for the changable area
                   1408: to contain room for the original contents. You need to "make space"
                   1409: for whatever changes you wish to make, and be B<sure> to check your
                   1410: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
                   1411: it's adequate for updating a one-line status display, but little more.
                   1412: This script will set the space to 100% width, so you only need to
                   1413: worry about height in Netscape 4.
                   1414: 
                   1415: Modern browsers are much less limiting, and if you can commit to the
                   1416: user not using Netscape 4, this feature may be used freely with
                   1417: pretty much any HTML.
                   1418: 
                   1419: =cut
                   1420: 
                   1421: sub change_content_javascript {
                   1422:     # If we're on Netscape 4, we need to use Layer-based code
1.258     albertel 1423:     if ($env{'browser.type'} eq 'netscape' &&
                   1424: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1425: 	return (<<NETSCAPE4);
                   1426: 	function change(name, content) {
                   1427: 	    doc = document.layers[name+"___escape"].layers[0].document;
                   1428: 	    doc.open();
                   1429: 	    doc.write(content);
                   1430: 	    doc.close();
                   1431: 	}
                   1432: NETSCAPE4
                   1433:     } else {
                   1434: 	# Otherwise, we need to use semi-standards-compliant code
                   1435: 	# (technically, "innerHTML" isn't standard but the equivalent
                   1436: 	# is really scary, and every useful browser supports it
                   1437: 	return (<<DOMBASED);
                   1438: 	function change(name, content) {
                   1439: 	    element = document.getElementById(name);
                   1440: 	    element.innerHTML = content;
                   1441: 	}
                   1442: DOMBASED
                   1443:     }
                   1444: }
                   1445: 
                   1446: =pod
                   1447: 
1.648     raeburn  1448: =item * &changable_area($name,$origContent):
1.256     matthew  1449: 
                   1450: This provides a "changable area" that can be modified on the fly via
                   1451: the Javascript code provided in C<change_content_javascript>. $name is
                   1452: the name you will use to reference the area later; do not repeat the
                   1453: same name on a given HTML page more then once. $origContent is what
                   1454: the area will originally contain, which can be left blank.
                   1455: 
                   1456: =cut
                   1457: 
                   1458: sub changable_area {
                   1459:     my ($name, $origContent) = @_;
                   1460: 
1.258     albertel 1461:     if ($env{'browser.type'} eq 'netscape' &&
                   1462: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1463: 	# If this is netscape 4, we need to use the Layer tag
                   1464: 	return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
                   1465:     } else {
                   1466: 	return "<span id='$name'>$origContent</span>";
                   1467:     }
                   1468: }
                   1469: 
                   1470: =pod
                   1471: 
1.648     raeburn  1472: =item * &viewport_geometry_js 
1.590     raeburn  1473: 
                   1474: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
                   1475: 
                   1476: =cut
                   1477: 
                   1478: 
                   1479: sub viewport_geometry_js { 
                   1480:     return <<"GEOMETRY";
                   1481: var Geometry = {};
                   1482: function init_geometry() {
                   1483:     if (Geometry.init) { return };
                   1484:     Geometry.init=1;
                   1485:     if (window.innerHeight) {
                   1486:         Geometry.getViewportHeight   = function() { return window.innerHeight; };
                   1487:         Geometry.getViewportWidth   = function() { return window.innerWidth; };
                   1488:         Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
                   1489:         Geometry.getVerticalScroll   = function() { return window.pageYOffset; };
                   1490:     }
                   1491:     else if (document.documentElement && document.documentElement.clientHeight) {
                   1492:         Geometry.getViewportHeight =
                   1493:             function() { return document.documentElement.clientHeight; };
                   1494:         Geometry.getViewportWidth =
                   1495:             function() { return document.documentElement.clientWidth; };
                   1496: 
                   1497:         Geometry.getHorizontalScroll =
                   1498:             function() { return document.documentElement.scrollLeft; };
                   1499:         Geometry.getVerticalScroll =
                   1500:             function() { return document.documentElement.scrollTop; };
                   1501:     }
                   1502:     else if (document.body.clientHeight) {
                   1503:         Geometry.getViewportHeight =
                   1504:             function() { return document.body.clientHeight; };
                   1505:         Geometry.getViewportWidth =
                   1506:             function() { return document.body.clientWidth; };
                   1507:         Geometry.getHorizontalScroll =
                   1508:             function() { return document.body.scrollLeft; };
                   1509:         Geometry.getVerticalScroll =
                   1510:             function() { return document.body.scrollTop; };
                   1511:     }
                   1512: }
                   1513: 
                   1514: GEOMETRY
                   1515: }
                   1516: 
                   1517: =pod
                   1518: 
1.648     raeburn  1519: =item * &viewport_size_js()
1.590     raeburn  1520: 
                   1521: 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. 
                   1522: 
                   1523: =cut
                   1524: 
                   1525: sub viewport_size_js {
                   1526:     my $geometry = &viewport_geometry_js();
                   1527:     return <<"DIMS";
                   1528: 
                   1529: $geometry
                   1530: 
                   1531: function getViewportDims(width,height) {
                   1532:     init_geometry();
                   1533:     width.value = Geometry.getViewportWidth();
                   1534:     height.value = Geometry.getViewportHeight();
                   1535:     return;
                   1536: }
                   1537: 
                   1538: DIMS
                   1539: }
                   1540: 
                   1541: =pod
                   1542: 
1.648     raeburn  1543: =item * &resize_textarea_js()
1.565     albertel 1544: 
                   1545: emits the needed javascript to resize a textarea to be as big as possible
                   1546: 
                   1547: creates a function resize_textrea that takes two IDs first should be
                   1548: the id of the element to resize, second should be the id of a div that
                   1549: surrounds everything that comes after the textarea, this routine needs
                   1550: to be attached to the <body> for the onload and onresize events.
                   1551: 
1.648     raeburn  1552: =back
1.565     albertel 1553: 
                   1554: =cut
                   1555: 
                   1556: sub resize_textarea_js {
1.590     raeburn  1557:     my $geometry = &viewport_geometry_js();
1.565     albertel 1558:     return <<"RESIZE";
                   1559:     <script type="text/javascript">
1.824     bisitz   1560: // <![CDATA[
1.590     raeburn  1561: $geometry
1.565     albertel 1562: 
1.588     albertel 1563: function getX(element) {
                   1564:     var x = 0;
                   1565:     while (element) {
                   1566: 	x += element.offsetLeft;
                   1567: 	element = element.offsetParent;
                   1568:     }
                   1569:     return x;
                   1570: }
                   1571: function getY(element) {
                   1572:     var y = 0;
                   1573:     while (element) {
                   1574: 	y += element.offsetTop;
                   1575: 	element = element.offsetParent;
                   1576:     }
                   1577:     return y;
                   1578: }
                   1579: 
                   1580: 
1.565     albertel 1581: function resize_textarea(textarea_id,bottom_id) {
                   1582:     init_geometry();
                   1583:     var textarea        = document.getElementById(textarea_id);
                   1584:     //alert(textarea);
                   1585: 
1.588     albertel 1586:     var textarea_top    = getY(textarea);
1.565     albertel 1587:     var textarea_height = textarea.offsetHeight;
                   1588:     var bottom          = document.getElementById(bottom_id);
1.588     albertel 1589:     var bottom_top      = getY(bottom);
1.565     albertel 1590:     var bottom_height   = bottom.offsetHeight;
                   1591:     var window_height   = Geometry.getViewportHeight();
1.588     albertel 1592:     var fudge           = 23;
1.565     albertel 1593:     var new_height      = window_height-fudge-textarea_top-bottom_height;
                   1594:     if (new_height < 300) {
                   1595: 	new_height = 300;
                   1596:     }
                   1597:     textarea.style.height=new_height+'px';
                   1598: }
1.824     bisitz   1599: // ]]>
1.565     albertel 1600: </script>
                   1601: RESIZE
                   1602: 
                   1603: }
                   1604: 
                   1605: =pod
                   1606: 
1.256     matthew  1607: =head1 Excel and CSV file utility routines
                   1608: 
                   1609: =over 4
                   1610: 
                   1611: =cut
                   1612: 
                   1613: ###############################################################
                   1614: ###############################################################
                   1615: 
                   1616: =pod
                   1617: 
1.648     raeburn  1618: =item * &csv_translate($text) 
1.37      matthew  1619: 
1.185     www      1620: Translate $text to allow it to be output as a 'comma separated values' 
1.37      matthew  1621: format.
                   1622: 
                   1623: =cut
                   1624: 
1.180     matthew  1625: ###############################################################
                   1626: ###############################################################
1.37      matthew  1627: sub csv_translate {
                   1628:     my $text = shift;
                   1629:     $text =~ s/\"/\"\"/g;
1.209     albertel 1630:     $text =~ s/\n/ /g;
1.37      matthew  1631:     return $text;
                   1632: }
1.180     matthew  1633: 
                   1634: ###############################################################
                   1635: ###############################################################
                   1636: 
                   1637: =pod
                   1638: 
1.648     raeburn  1639: =item * &define_excel_formats()
1.180     matthew  1640: 
                   1641: Define some commonly used Excel cell formats.
                   1642: 
                   1643: Currently supported formats:
                   1644: 
                   1645: =over 4
                   1646: 
                   1647: =item header
                   1648: 
                   1649: =item bold
                   1650: 
                   1651: =item h1
                   1652: 
                   1653: =item h2
                   1654: 
                   1655: =item h3
                   1656: 
1.256     matthew  1657: =item h4
                   1658: 
                   1659: =item i
                   1660: 
1.180     matthew  1661: =item date
                   1662: 
                   1663: =back
                   1664: 
                   1665: Inputs: $workbook
                   1666: 
                   1667: Returns: $format, a hash reference.
                   1668: 
                   1669: =cut
                   1670: 
                   1671: ###############################################################
                   1672: ###############################################################
                   1673: sub define_excel_formats {
                   1674:     my ($workbook) = @_;
                   1675:     my $format;
                   1676:     $format->{'header'} = $workbook->add_format(bold      => 1, 
                   1677:                                                 bottom    => 1,
                   1678:                                                 align     => 'center');
                   1679:     $format->{'bold'} = $workbook->add_format(bold=>1);
                   1680:     $format->{'h1'}   = $workbook->add_format(bold=>1, size=>18);
                   1681:     $format->{'h2'}   = $workbook->add_format(bold=>1, size=>16);
                   1682:     $format->{'h3'}   = $workbook->add_format(bold=>1, size=>14);
1.255     matthew  1683:     $format->{'h4'}   = $workbook->add_format(bold=>1, size=>12);
1.246     matthew  1684:     $format->{'i'}    = $workbook->add_format(italic=>1);
1.180     matthew  1685:     $format->{'date'} = $workbook->add_format(num_format=>
1.207     matthew  1686:                                             'mm/dd/yyyy hh:mm:ss');
1.180     matthew  1687:     return $format;
                   1688: }
                   1689: 
                   1690: ###############################################################
                   1691: ###############################################################
1.113     bowersj2 1692: 
                   1693: =pod
                   1694: 
1.648     raeburn  1695: =item * &create_workbook()
1.255     matthew  1696: 
                   1697: Create an Excel worksheet.  If it fails, output message on the
                   1698: request object and return undefs.
                   1699: 
                   1700: Inputs: Apache request object
                   1701: 
                   1702: Returns (undef) on failure, 
                   1703:     Excel worksheet object, scalar with filename, and formats 
                   1704:     from &Apache::loncommon::define_excel_formats on success
                   1705: 
                   1706: =cut
                   1707: 
                   1708: ###############################################################
                   1709: ###############################################################
                   1710: sub create_workbook {
                   1711:     my ($r) = @_;
                   1712:         #
                   1713:     # Create the excel spreadsheet
                   1714:     my $filename = '/prtspool/'.
1.258     albertel 1715:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255     matthew  1716:         time.'_'.rand(1000000000).'.xls';
                   1717:     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
                   1718:     if (! defined($workbook)) {
                   1719:         $r->log_error("Error creating excel spreadsheet $filename: $!");
                   1720:         $r->print('<p>'.&mt("Unable to create new Excel file.  ".
                   1721:                             "This error has been logged.  ".
                   1722:                             "Please alert your LON-CAPA administrator").
                   1723:                   '</p>');
                   1724:         return (undef);
                   1725:     }
                   1726:     #
                   1727:     $workbook->set_tempdir('/home/httpd/perl/tmp');
                   1728:     #
                   1729:     my $format = &Apache::loncommon::define_excel_formats($workbook);
                   1730:     return ($workbook,$filename,$format);
                   1731: }
                   1732: 
                   1733: ###############################################################
                   1734: ###############################################################
                   1735: 
                   1736: =pod
                   1737: 
1.648     raeburn  1738: =item * &create_text_file()
1.113     bowersj2 1739: 
1.542     raeburn  1740: Create a file to write to and eventually make available to the user.
1.256     matthew  1741: If file creation fails, outputs an error message on the request object and 
                   1742: return undefs.
1.113     bowersj2 1743: 
1.256     matthew  1744: Inputs: Apache request object, and file suffix
1.113     bowersj2 1745: 
1.256     matthew  1746: Returns (undef) on failure, 
                   1747:     Filehandle and filename on success.
1.113     bowersj2 1748: 
                   1749: =cut
                   1750: 
1.256     matthew  1751: ###############################################################
                   1752: ###############################################################
                   1753: sub create_text_file {
                   1754:     my ($r,$suffix) = @_;
                   1755:     if (! defined($suffix)) { $suffix = 'txt'; };
                   1756:     my $fh;
                   1757:     my $filename = '/prtspool/'.
1.258     albertel 1758:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256     matthew  1759:         time.'_'.rand(1000000000).'.'.$suffix;
                   1760:     $fh = Apache::File->new('>/home/httpd'.$filename);
                   1761:     if (! defined($fh)) {
                   1762:         $r->log_error("Couldn't open $filename for output $!");
1.683     bisitz   1763:         $r->print(&mt('Problems occurred in creating the output file. '
                   1764:                      .'This error has been logged. '
                   1765:                      .'Please alert your LON-CAPA administrator.'));
1.113     bowersj2 1766:     }
1.256     matthew  1767:     return ($fh,$filename)
1.113     bowersj2 1768: }
                   1769: 
                   1770: 
1.256     matthew  1771: =pod 
1.113     bowersj2 1772: 
                   1773: =back
                   1774: 
                   1775: =cut
1.37      matthew  1776: 
                   1777: ###############################################################
1.33      matthew  1778: ##        Home server <option> list generating code          ##
                   1779: ###############################################################
1.35      matthew  1780: 
1.169     www      1781: # ------------------------------------------
                   1782: 
                   1783: sub domain_select {
                   1784:     my ($name,$value,$multiple)=@_;
                   1785:     my %domains=map { 
1.514     albertel 1786: 	$_ => $_.' '. &Apache::lonnet::domain($_,'description') 
1.512     albertel 1787:     } &Apache::lonnet::all_domains();
1.169     www      1788:     if ($multiple) {
                   1789: 	$domains{''}=&mt('Any domain');
1.550     albertel 1790: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287     albertel 1791: 	return &multiple_select_form($name,$value,4,\%domains);
1.169     www      1792:     } else {
1.550     albertel 1793: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.169     www      1794: 	return &select_form($name,$value,%domains);
                   1795:     }
                   1796: }
                   1797: 
1.282     albertel 1798: #-------------------------------------------
                   1799: 
                   1800: =pod
                   1801: 
1.519     raeburn  1802: =head1 Routines for form select boxes
                   1803: 
                   1804: =over 4
                   1805: 
1.648     raeburn  1806: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282     albertel 1807: 
                   1808: Returns a string containing a <select> element int multiple mode
                   1809: 
                   1810: 
                   1811: Args:
                   1812:   $name - name of the <select> element
1.506     raeburn  1813:   $value - scalar or array ref of values that should already be selected
1.282     albertel 1814:   $size - number of rows long the select element is
1.283     albertel 1815:   $hash - the elements should be 'option' => 'shown text'
1.282     albertel 1816:           (shown text should already have been &mt())
1.506     raeburn  1817:   $order - (optional) array ref of the order to show the elements in
1.283     albertel 1818: 
1.282     albertel 1819: =cut
                   1820: 
                   1821: #-------------------------------------------
1.169     www      1822: sub multiple_select_form {
1.284     albertel 1823:     my ($name,$value,$size,$hash,$order)=@_;
1.169     www      1824:     my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
                   1825:     my $output='';
1.191     matthew  1826:     if (! defined($size)) {
                   1827:         $size = 4;
1.283     albertel 1828:         if (scalar(keys(%$hash))<4) {
                   1829:             $size = scalar(keys(%$hash));
1.191     matthew  1830:         }
                   1831:     }
1.734     bisitz   1832:     $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple">';
1.501     banghart 1833:     my @order;
1.506     raeburn  1834:     if (ref($order) eq 'ARRAY')  {
                   1835:         @order = @{$order};
                   1836:     } else {
                   1837:         @order = sort(keys(%$hash));
1.501     banghart 1838:     }
                   1839:     if (exists($$hash{'select_form_order'})) {
                   1840:         @order = @{$$hash{'select_form_order'}};
                   1841:     }
                   1842:         
1.284     albertel 1843:     foreach my $key (@order) {
1.356     albertel 1844:         $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284     albertel 1845:         $output.='selected="selected" ' if ($selected{$key});
                   1846:         $output.='>'.$hash->{$key}."</option>\n";
1.169     www      1847:     }
                   1848:     $output.="</select>\n";
                   1849:     return $output;
                   1850: }
                   1851: 
1.88      www      1852: #-------------------------------------------
                   1853: 
                   1854: =pod
                   1855: 
1.648     raeburn  1856: =item * &select_form($defdom,$name,%hash)
1.88      www      1857: 
                   1858: Returns a string containing a <select name='$name' size='1'> form to 
                   1859: allow a user to select options from a hash option_name => displayed text.  
                   1860: See lonrights.pm for an example invocation and use.
                   1861: 
                   1862: =cut
                   1863: 
                   1864: #-------------------------------------------
                   1865: sub select_form {
                   1866:     my ($def,$name,%hash) = @_;
                   1867:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
1.128     albertel 1868:     my @keys;
                   1869:     if (exists($hash{'select_form_order'})) {
                   1870: 	@keys=@{$hash{'select_form_order'}};
                   1871:     } else {
                   1872: 	@keys=sort(keys(%hash));
                   1873:     }
1.356     albertel 1874:     foreach my $key (@keys) {
                   1875:         $selectform.=
                   1876: 	    '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
                   1877:             ($key eq $def ? 'selected="selected" ' : '').
                   1878:                 ">".&mt($hash{$key})."</option>\n";
1.88      www      1879:     }
                   1880:     $selectform.="</select>";
                   1881:     return $selectform;
                   1882: }
                   1883: 
1.475     www      1884: # For display filters
                   1885: 
                   1886: sub display_filter {
                   1887:     if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477     www      1888:     if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.714     bisitz   1889:     return '<span class="LC_nobreak"><label>'.&mt('Records [_1]',
1.475     www      1890: 			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
                   1891: 							   (&mt('all'),10,20,50,100,1000,10000))).
1.714     bisitz   1892: 	   '</label></span> <span class="LC_nobreak">'.
1.475     www      1893:            &mt('Filter [_1]',
1.477     www      1894: 	   &select_form($env{'form.displayfilter'},
                   1895: 			'displayfilter',
                   1896: 			('currentfolder' => 'Current folder/page',
                   1897: 			 'containing' => 'Containing phrase',
                   1898: 			 'none' => 'None'))).
1.714     bisitz   1899: 			 '<input type="text" name="containingphrase" size="30" value="'.&HTML::Entities::encode($env{'form.containingphrase'}).'" /></span>';
1.475     www      1900: }
                   1901: 
1.167     www      1902: sub gradeleveldescription {
                   1903:     my $gradelevel=shift;
                   1904:     my %gradelevels=(0 => 'Not specified',
                   1905: 		     1 => 'Grade 1',
                   1906: 		     2 => 'Grade 2',
                   1907: 		     3 => 'Grade 3',
                   1908: 		     4 => 'Grade 4',
                   1909: 		     5 => 'Grade 5',
                   1910: 		     6 => 'Grade 6',
                   1911: 		     7 => 'Grade 7',
                   1912: 		     8 => 'Grade 8',
                   1913: 		     9 => 'Grade 9',
                   1914: 		     10 => 'Grade 10',
                   1915: 		     11 => 'Grade 11',
                   1916: 		     12 => 'Grade 12',
                   1917: 		     13 => 'Grade 13',
                   1918: 		     14 => '100 Level',
                   1919: 		     15 => '200 Level',
                   1920: 		     16 => '300 Level',
                   1921: 		     17 => '400 Level',
                   1922: 		     18 => 'Graduate Level');
                   1923:     return &mt($gradelevels{$gradelevel});
                   1924: }
                   1925: 
1.163     www      1926: sub select_level_form {
                   1927:     my ($deflevel,$name)=@_;
                   1928:     unless ($deflevel) { $deflevel=0; }
1.167     www      1929:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
                   1930:     for (my $i=0; $i<=18; $i++) {
                   1931:         $selectform.="<option value=\"$i\" ".
1.253     albertel 1932:             ($i==$deflevel ? 'selected="selected" ' : '').
1.167     www      1933:                 ">".&gradeleveldescription($i)."</option>\n";
                   1934:     }
                   1935:     $selectform.="</select>";
                   1936:     return $selectform;
1.163     www      1937: }
1.167     www      1938: 
1.35      matthew  1939: #-------------------------------------------
                   1940: 
1.45      matthew  1941: =pod
                   1942: 
1.873     raeburn  1943: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange)
1.35      matthew  1944: 
                   1945: Returns a string containing a <select name='$name' size='1'> form to 
                   1946: allow a user to select the domain to preform an operation in.  
                   1947: See loncreateuser.pm for an example invocation and use.
                   1948: 
1.90      www      1949: If the $includeempty flag is set, it also includes an empty choice ("no domain
                   1950: selected");
                   1951: 
1.743     raeburn  1952: If the $showdomdesc flag is set, the domain name is followed by the domain description.
                   1953: 
1.872     raeburn  1954: The optional $onchange argumnet 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  1955: 
1.35      matthew  1956: =cut
                   1957: 
                   1958: #-------------------------------------------
1.34      matthew  1959: sub select_dom_form {
1.872     raeburn  1960:     my ($defdom,$name,$includeempty,$showdomdesc,$onchange) = @_;
                   1961:     if ($onchange) {
1.874     raeburn  1962:         $onchange = ' onchange="'.$onchange.'"';
1.743     raeburn  1963:     }
1.550     albertel 1964:     my @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
1.90      www      1965:     if ($includeempty) { @domains=('',@domains); }
1.743     raeburn  1966:     my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange>\n";
1.356     albertel 1967:     foreach my $dom (@domains) {
                   1968:         $selectdomain.="<option value=\"$dom\" ".
1.563     raeburn  1969:             ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
                   1970:         if ($showdomdesc) {
                   1971:             if ($dom ne '') {
                   1972:                 my $domdesc = &Apache::lonnet::domain($dom,'description');
                   1973:                 if ($domdesc ne '') {
                   1974:                     $selectdomain .= ' ('.$domdesc.')';
                   1975:                 }
                   1976:             } 
                   1977:         }
                   1978:         $selectdomain .= "</option>\n";
1.34      matthew  1979:     }
                   1980:     $selectdomain.="</select>";
                   1981:     return $selectdomain;
                   1982: }
                   1983: 
1.35      matthew  1984: #-------------------------------------------
                   1985: 
1.45      matthew  1986: =pod
                   1987: 
1.648     raeburn  1988: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35      matthew  1989: 
1.586     raeburn  1990: input: 4 arguments (two required, two optional) - 
                   1991:     $domain - domain of new user
                   1992:     $name - name of form element
                   1993:     $default - Value of 'default' causes a default item to be first 
                   1994:                             option, and selected by default. 
                   1995:     $hide - Value of 'hide' causes hiding of the name of the server, 
                   1996:                             if 1 server found, or default, if 0 found.
1.594     raeburn  1997: output: returns 2 items: 
1.586     raeburn  1998: (a) form element which contains either:
                   1999:    (i) <select name="$name">
                   2000:         <option value="$hostid1">$hostid $servers{$hostid}</option>
                   2001:         <option value="$hostid2">$hostid $servers{$hostid}</option>       
                   2002:        </select>
                   2003:        form item if there are multiple library servers in $domain, or
                   2004:    (ii) an <input type="hidden" name="$name" value="$hostid" /> form item 
                   2005:        if there is only one library server in $domain.
                   2006: 
                   2007: (b) number of library servers found.
                   2008: 
                   2009: See loncreateuser.pm for example of use.
1.35      matthew  2010: 
                   2011: =cut
                   2012: 
                   2013: #-------------------------------------------
1.586     raeburn  2014: sub home_server_form_item {
                   2015:     my ($domain,$name,$default,$hide) = @_;
1.513     albertel 2016:     my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586     raeburn  2017:     my $result;
                   2018:     my $numlib = keys(%servers);
                   2019:     if ($numlib > 1) {
                   2020:         $result .= '<select name="'.$name.'" />'."\n";
                   2021:         if ($default) {
1.804     bisitz   2022:             $result .= '<option value="default" selected="selected">'.&mt('default').
1.586     raeburn  2023:                        '</option>'."\n";
                   2024:         }
                   2025:         foreach my $hostid (sort(keys(%servers))) {
                   2026:             $result.= '<option value="'.$hostid.'">'.
                   2027: 	              $hostid.' '.$servers{$hostid}."</option>\n";
                   2028:         }
                   2029:         $result .= '</select>'."\n";
                   2030:     } elsif ($numlib == 1) {
                   2031:         my $hostid;
                   2032:         foreach my $item (keys(%servers)) {
                   2033:             $hostid = $item;
                   2034:         }
                   2035:         $result .= '<input type="hidden" name="'.$name.'" value="'.
                   2036:                    $hostid.'" />';
                   2037:                    if (!$hide) {
                   2038:                        $result .= $hostid.' '.$servers{$hostid};
                   2039:                    }
                   2040:                    $result .= "\n";
                   2041:     } elsif ($default) {
                   2042:         $result .= '<input type="hidden" name="'.$name.
                   2043:                    '" value="default" />';
                   2044:                    if (!$hide) {
                   2045:                        $result .= &mt('default');
                   2046:                    }
                   2047:                    $result .= "\n";
1.33      matthew  2048:     }
1.586     raeburn  2049:     return ($result,$numlib);
1.33      matthew  2050: }
1.112     bowersj2 2051: 
                   2052: =pod
                   2053: 
1.534     albertel 2054: =back 
                   2055: 
1.112     bowersj2 2056: =cut
1.87      matthew  2057: 
                   2058: ###############################################################
1.112     bowersj2 2059: ##                  Decoding User Agent                      ##
1.87      matthew  2060: ###############################################################
                   2061: 
                   2062: =pod
                   2063: 
1.112     bowersj2 2064: =head1 Decoding the User Agent
                   2065: 
                   2066: =over 4
                   2067: 
                   2068: =item * &decode_user_agent()
1.87      matthew  2069: 
                   2070: Inputs: $r
                   2071: 
                   2072: Outputs:
                   2073: 
                   2074: =over 4
                   2075: 
1.112     bowersj2 2076: =item * $httpbrowser
1.87      matthew  2077: 
1.112     bowersj2 2078: =item * $clientbrowser
1.87      matthew  2079: 
1.112     bowersj2 2080: =item * $clientversion
1.87      matthew  2081: 
1.112     bowersj2 2082: =item * $clientmathml
1.87      matthew  2083: 
1.112     bowersj2 2084: =item * $clientunicode
1.87      matthew  2085: 
1.112     bowersj2 2086: =item * $clientos
1.87      matthew  2087: 
                   2088: =back
                   2089: 
1.157     matthew  2090: =back 
                   2091: 
1.87      matthew  2092: =cut
                   2093: 
                   2094: ###############################################################
                   2095: ###############################################################
                   2096: sub decode_user_agent {
1.247     albertel 2097:     my ($r)=@_;
1.87      matthew  2098:     my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
                   2099:     my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
                   2100:     my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247     albertel 2101:     if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87      matthew  2102:     my $clientbrowser='unknown';
                   2103:     my $clientversion='0';
                   2104:     my $clientmathml='';
                   2105:     my $clientunicode='0';
                   2106:     for (my $i=0;$i<=$#browsertype;$i++) {
                   2107:         my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\:/,$browsertype[$i]);
                   2108: 	if (($httpbrowser=~/$match/i)  && ($httpbrowser!~/$notmatch/i)) {
                   2109: 	    $clientbrowser=$bname;
                   2110:             $httpbrowser=~/$vreg/i;
                   2111: 	    $clientversion=$1;
                   2112:             $clientmathml=($clientversion>=$minv);
                   2113:             $clientunicode=($clientversion>=$univ);
                   2114: 	}
                   2115:     }
                   2116:     my $clientos='unknown';
                   2117:     if (($httpbrowser=~/linux/i) ||
                   2118:         ($httpbrowser=~/unix/i) ||
                   2119:         ($httpbrowser=~/ux/i) ||
                   2120:         ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
                   2121:     if (($httpbrowser=~/vax/i) ||
                   2122:         ($httpbrowser=~/vms/i)) { $clientos='vms'; }
                   2123:     if ($httpbrowser=~/next/i) { $clientos='next'; }
                   2124:     if (($httpbrowser=~/mac/i) ||
                   2125:         ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
                   2126:     if ($httpbrowser=~/win/i) { $clientos='win'; }
                   2127:     if ($httpbrowser=~/embed/i) { $clientos='pda'; }
                   2128:     return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
                   2129:             $clientunicode,$clientos,);
                   2130: }
                   2131: 
1.32      matthew  2132: ###############################################################
                   2133: ##    Authentication changing form generation subroutines    ##
                   2134: ###############################################################
                   2135: ##
                   2136: ## All of the authform_xxxxxxx subroutines take their inputs in a
                   2137: ## hash, and have reasonable default values.
                   2138: ##
                   2139: ##    formname = the name given in the <form> tag.
1.35      matthew  2140: #-------------------------------------------
                   2141: 
1.45      matthew  2142: =pod
                   2143: 
1.112     bowersj2 2144: =head1 Authentication Routines
                   2145: 
                   2146: =over 4
                   2147: 
1.648     raeburn  2148: =item * &authform_xxxxxx()
1.35      matthew  2149: 
                   2150: The authform_xxxxxx subroutines provide javascript and html forms which 
                   2151: handle some of the conveniences required for authentication forms.  
                   2152: This is not an optimal method, but it works.  
                   2153: 
                   2154: =over 4
                   2155: 
1.112     bowersj2 2156: =item * authform_header
1.35      matthew  2157: 
1.112     bowersj2 2158: =item * authform_authorwarning
1.35      matthew  2159: 
1.112     bowersj2 2160: =item * authform_nochange
1.35      matthew  2161: 
1.112     bowersj2 2162: =item * authform_kerberos
1.35      matthew  2163: 
1.112     bowersj2 2164: =item * authform_internal
1.35      matthew  2165: 
1.112     bowersj2 2166: =item * authform_filesystem
1.35      matthew  2167: 
                   2168: =back
                   2169: 
1.648     raeburn  2170: See loncreateuser.pm for invocation and use examples.
1.157     matthew  2171: 
1.35      matthew  2172: =cut
                   2173: 
                   2174: #-------------------------------------------
1.32      matthew  2175: sub authform_header{  
                   2176:     my %in = (
                   2177:         formname => 'cu',
1.80      albertel 2178:         kerb_def_dom => '',
1.32      matthew  2179:         @_,
                   2180:     );
                   2181:     $in{'formname'} = 'document.' . $in{'formname'};
                   2182:     my $result='';
1.80      albertel 2183: 
                   2184: #---------------------------------------------- Code for upper case translation
                   2185:     my $Javascript_toUpperCase;
                   2186:     unless ($in{kerb_def_dom}) {
                   2187:         $Javascript_toUpperCase =<<"END";
                   2188:         switch (choice) {
                   2189:            case 'krb': currentform.elements[choicearg].value =
                   2190:                currentform.elements[choicearg].value.toUpperCase();
                   2191:                break;
                   2192:            default:
                   2193:         }
                   2194: END
                   2195:     } else {
                   2196:         $Javascript_toUpperCase = "";
                   2197:     }
                   2198: 
1.165     raeburn  2199:     my $radioval = "'nochange'";
1.591     raeburn  2200:     if (defined($in{'curr_authtype'})) {
                   2201:         if ($in{'curr_authtype'} ne '') {
                   2202:             $radioval = "'".$in{'curr_authtype'}."arg'";
                   2203:         }
1.174     matthew  2204:     }
1.165     raeburn  2205:     my $argfield = 'null';
1.591     raeburn  2206:     if (defined($in{'mode'})) {
1.165     raeburn  2207:         if ($in{'mode'} eq 'modifycourse')  {
1.591     raeburn  2208:             if (defined($in{'curr_autharg'})) {
                   2209:                 if ($in{'curr_autharg'} ne '') {
1.165     raeburn  2210:                     $argfield = "'$in{'curr_autharg'}'";
                   2211:                 }
                   2212:             }
                   2213:         }
                   2214:     }
                   2215: 
1.32      matthew  2216:     $result.=<<"END";
                   2217: var current = new Object();
1.165     raeburn  2218: current.radiovalue = $radioval;
                   2219: current.argfield = $argfield;
1.32      matthew  2220: 
                   2221: function changed_radio(choice,currentform) {
                   2222:     var choicearg = choice + 'arg';
                   2223:     // If a radio button in changed, we need to change the argfield
                   2224:     if (current.radiovalue != choice) {
                   2225:         current.radiovalue = choice;
                   2226:         if (current.argfield != null) {
                   2227:             currentform.elements[current.argfield].value = '';
                   2228:         }
                   2229:         if (choice == 'nochange') {
                   2230:             current.argfield = null;
                   2231:         } else {
                   2232:             current.argfield = choicearg;
                   2233:             switch(choice) {
                   2234:                 case 'krb': 
                   2235:                     currentform.elements[current.argfield].value = 
                   2236:                         "$in{'kerb_def_dom'}";
                   2237:                 break;
                   2238:               default:
                   2239:                 break;
                   2240:             }
                   2241:         }
                   2242:     }
                   2243:     return;
                   2244: }
1.22      www      2245: 
1.32      matthew  2246: function changed_text(choice,currentform) {
                   2247:     var choicearg = choice + 'arg';
                   2248:     if (currentform.elements[choicearg].value !='') {
1.80      albertel 2249:         $Javascript_toUpperCase
1.32      matthew  2250:         // clear old field
                   2251:         if ((current.argfield != choicearg) && (current.argfield != null)) {
                   2252:             currentform.elements[current.argfield].value = '';
                   2253:         }
                   2254:         current.argfield = choicearg;
                   2255:     }
                   2256:     set_auth_radio_buttons(choice,currentform);
                   2257:     return;
1.20      www      2258: }
1.32      matthew  2259: 
                   2260: function set_auth_radio_buttons(newvalue,currentform) {
                   2261:     var i=0;
                   2262:     while (i < currentform.login.length) {
                   2263:         if (currentform.login[i].value == newvalue) { break; }
                   2264:         i++;
                   2265:     }
                   2266:     if (i == currentform.login.length) {
                   2267:         return;
                   2268:     }
                   2269:     current.radiovalue = newvalue;
                   2270:     currentform.login[i].checked = true;
                   2271:     return;
                   2272: }
                   2273: END
                   2274:     return $result;
                   2275: }
                   2276: 
                   2277: sub authform_authorwarning{
                   2278:     my $result='';
1.144     matthew  2279:     $result='<i>'.
                   2280:         &mt('As a general rule, only authors or co-authors should be '.
                   2281:             'filesystem authenticated '.
                   2282:             '(which allows access to the server filesystem).')."</i>\n";
1.32      matthew  2283:     return $result;
                   2284: }
                   2285: 
                   2286: sub authform_nochange{  
                   2287:     my %in = (
                   2288:               formname => 'document.cu',
                   2289:               kerb_def_dom => 'MSU.EDU',
                   2290:               @_,
                   2291:           );
1.586     raeburn  2292:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'}); 
                   2293:     my $result;
                   2294:     if (keys(%can_assign) == 0) {
                   2295:         $result = &mt('Under you current role you are not permitted to change login settings for this user');  
                   2296:     } else {
                   2297:         $result = '<label>'.&mt('[_1] Do not change login data',
                   2298:                   '<input type="radio" name="login" value="nochange" '.
                   2299:                   'checked="checked" onclick="'.
1.281     albertel 2300:             "javascript:changed_radio('nochange',$in{'formname'});".'" />').
                   2301: 	    '</label>';
1.586     raeburn  2302:     }
1.32      matthew  2303:     return $result;
                   2304: }
                   2305: 
1.591     raeburn  2306: sub authform_kerberos {
1.32      matthew  2307:     my %in = (
                   2308:               formname => 'document.cu',
                   2309:               kerb_def_dom => 'MSU.EDU',
1.80      albertel 2310:               kerb_def_auth => 'krb4',
1.32      matthew  2311:               @_,
                   2312:               );
1.586     raeburn  2313:     my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
                   2314:         $autharg,$jscall);
                   2315:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.80      albertel 2316:     if ($in{'kerb_def_auth'} eq 'krb5') {
1.772     bisitz   2317:        $check5 = ' checked="checked"';
1.80      albertel 2318:     } else {
1.772     bisitz   2319:        $check4 = ' checked="checked"';
1.80      albertel 2320:     }
1.165     raeburn  2321:     $krbarg = $in{'kerb_def_dom'};
1.591     raeburn  2322:     if (defined($in{'curr_authtype'})) {
                   2323:         if ($in{'curr_authtype'} eq 'krb') {
1.772     bisitz   2324:             $krbcheck = ' checked="checked"';
1.623     raeburn  2325:             if (defined($in{'mode'})) {
                   2326:                 if ($in{'mode'} eq 'modifyuser') {
                   2327:                     $krbcheck = '';
                   2328:                 }
                   2329:             }
1.591     raeburn  2330:             if (defined($in{'curr_kerb_ver'})) {
                   2331:                 if ($in{'curr_krb_ver'} eq '5') {
1.772     bisitz   2332:                     $check5 = ' checked="checked"';
1.591     raeburn  2333:                     $check4 = '';
                   2334:                 } else {
1.772     bisitz   2335:                     $check4 = ' checked="checked"';
1.591     raeburn  2336:                     $check5 = '';
                   2337:                 }
1.586     raeburn  2338:             }
1.591     raeburn  2339:             if (defined($in{'curr_autharg'})) {
1.165     raeburn  2340:                 $krbarg = $in{'curr_autharg'};
                   2341:             }
1.586     raeburn  2342:             if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591     raeburn  2343:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2344:                     $result = 
                   2345:     &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
                   2346:         $in{'curr_autharg'},$krbver);
                   2347:                 } else {
                   2348:                     $result =
                   2349:     &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
                   2350:                 }
                   2351:                 return $result; 
                   2352:             }
                   2353:         }
                   2354:     } else {
                   2355:         if ($authnum == 1) {
1.784     bisitz   2356:             $authtype = '<input type="hidden" name="login" value="krb" />';
1.165     raeburn  2357:         }
                   2358:     }
1.586     raeburn  2359:     if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
                   2360:         return;
1.587     raeburn  2361:     } elsif ($authtype eq '') {
1.591     raeburn  2362:         if (defined($in{'mode'})) {
1.587     raeburn  2363:             if ($in{'mode'} eq 'modifycourse') {
                   2364:                 if ($authnum == 1) {
1.784     bisitz   2365:                     $authtype = '<input type="hidden" name="login" value="krb" />';
1.587     raeburn  2366:                 }
                   2367:             }
                   2368:         }
1.586     raeburn  2369:     }
                   2370:     $jscall = "javascript:changed_radio('krb',$in{'formname'});";
                   2371:     if ($authtype eq '') {
                   2372:         $authtype = '<input type="radio" name="login" value="krb" '.
                   2373:                     'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
                   2374:                     $krbcheck.' />';
                   2375:     }
                   2376:     if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
                   2377:         ($can_assign{'krb4'} && !$can_assign{'krb5'} && 
                   2378:          $in{'curr_authtype'} eq 'krb5') ||
                   2379:         (!$can_assign{'krb4'} && $can_assign{'krb5'} && 
                   2380:          $in{'curr_authtype'} eq 'krb4')) {
                   2381:         $result .= &mt
1.144     matthew  2382:         ('[_1] Kerberos authenticated with domain [_2] '.
1.281     albertel 2383:          '[_3] Version 4 [_4] Version 5 [_5]',
1.586     raeburn  2384:          '<label>'.$authtype,
1.281     albertel 2385:          '</label><input type="text" size="10" name="krbarg" '.
1.165     raeburn  2386:              'value="'.$krbarg.'" '.
1.144     matthew  2387:              'onchange="'.$jscall.'" />',
1.281     albertel 2388:          '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
                   2389:          '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
                   2390: 	 '</label>');
1.586     raeburn  2391:     } elsif ($can_assign{'krb4'}) {
                   2392:         $result .= &mt
                   2393:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2394:          '[_3] Version 4 [_4]',
                   2395:          '<label>'.$authtype,
                   2396:          '</label><input type="text" size="10" name="krbarg" '.
                   2397:              'value="'.$krbarg.'" '.
                   2398:              'onchange="'.$jscall.'" />',
                   2399:          '<label><input type="hidden" name="krbver" value="4" />',
                   2400:          '</label>');
                   2401:     } elsif ($can_assign{'krb5'}) {
                   2402:         $result .= &mt
                   2403:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2404:          '[_3] Version 5 [_4]',
                   2405:          '<label>'.$authtype,
                   2406:          '</label><input type="text" size="10" name="krbarg" '.
                   2407:              'value="'.$krbarg.'" '.
                   2408:              'onchange="'.$jscall.'" />',
                   2409:          '<label><input type="hidden" name="krbver" value="5" />',
                   2410:          '</label>');
                   2411:     }
1.32      matthew  2412:     return $result;
                   2413: }
                   2414: 
                   2415: sub authform_internal{  
1.586     raeburn  2416:     my %in = (
1.32      matthew  2417:                 formname => 'document.cu',
                   2418:                 kerb_def_dom => 'MSU.EDU',
                   2419:                 @_,
                   2420:                 );
1.586     raeburn  2421:     my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
                   2422:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2423:     if (defined($in{'curr_authtype'})) {
                   2424:         if ($in{'curr_authtype'} eq 'int') {
1.586     raeburn  2425:             if ($can_assign{'int'}) {
1.772     bisitz   2426:                 $intcheck = 'checked="checked" ';
1.623     raeburn  2427:                 if (defined($in{'mode'})) {
                   2428:                     if ($in{'mode'} eq 'modifyuser') {
                   2429:                         $intcheck = '';
                   2430:                     }
                   2431:                 }
1.591     raeburn  2432:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2433:                     $intarg = $in{'curr_autharg'};
                   2434:                 }
                   2435:             } else {
                   2436:                 $result = &mt('Currently internally authenticated.');
                   2437:                 return $result;
1.165     raeburn  2438:             }
                   2439:         }
1.586     raeburn  2440:     } else {
                   2441:         if ($authnum == 1) {
1.784     bisitz   2442:             $authtype = '<input type="hidden" name="login" value="int" />';
1.586     raeburn  2443:         }
                   2444:     }
                   2445:     if (!$can_assign{'int'}) {
                   2446:         return;
1.587     raeburn  2447:     } elsif ($authtype eq '') {
1.591     raeburn  2448:         if (defined($in{'mode'})) {
1.587     raeburn  2449:             if ($in{'mode'} eq 'modifycourse') {
                   2450:                 if ($authnum == 1) {
1.784     bisitz   2451:                     $authtype = '<input type="hidden" name="login" value="int" />';
1.587     raeburn  2452:                 }
                   2453:             }
                   2454:         }
1.165     raeburn  2455:     }
1.586     raeburn  2456:     $jscall = "javascript:changed_radio('int',$in{'formname'});";
                   2457:     if ($authtype eq '') {
                   2458:         $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
                   2459:                     ' onchange="'.$jscall.'" onclick="'.$jscall.'" />';
                   2460:     }
1.605     bisitz   2461:     $autharg = '<input type="password" size="10" name="intarg" value="'.
1.586     raeburn  2462:                $intarg.'" onchange="'.$jscall.'" />';
                   2463:     $result = &mt
1.144     matthew  2464:         ('[_1] Internally authenticated (with initial password [_2])',
1.586     raeburn  2465:          '<label>'.$authtype,'</label>'.$autharg);
1.824     bisitz   2466:     $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  2467:     return $result;
                   2468: }
                   2469: 
                   2470: sub authform_local{  
                   2471:     my %in = (
                   2472:               formname => 'document.cu',
                   2473:               kerb_def_dom => 'MSU.EDU',
                   2474:               @_,
                   2475:               );
1.586     raeburn  2476:     my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
                   2477:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2478:     if (defined($in{'curr_authtype'})) {
                   2479:         if ($in{'curr_authtype'} eq 'loc') {
1.586     raeburn  2480:             if ($can_assign{'loc'}) {
1.772     bisitz   2481:                 $loccheck = 'checked="checked" ';
1.623     raeburn  2482:                 if (defined($in{'mode'})) {
                   2483:                     if ($in{'mode'} eq 'modifyuser') {
                   2484:                         $loccheck = '';
                   2485:                     }
                   2486:                 }
1.591     raeburn  2487:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2488:                     $locarg = $in{'curr_autharg'};
                   2489:                 }
                   2490:             } else {
                   2491:                 $result = &mt('Currently using local (institutional) authentication.');
                   2492:                 return $result;
1.165     raeburn  2493:             }
                   2494:         }
1.586     raeburn  2495:     } else {
                   2496:         if ($authnum == 1) {
1.784     bisitz   2497:             $authtype = '<input type="hidden" name="login" value="loc" />';
1.586     raeburn  2498:         }
                   2499:     }
                   2500:     if (!$can_assign{'loc'}) {
                   2501:         return;
1.587     raeburn  2502:     } elsif ($authtype eq '') {
1.591     raeburn  2503:         if (defined($in{'mode'})) {
1.587     raeburn  2504:             if ($in{'mode'} eq 'modifycourse') {
                   2505:                 if ($authnum == 1) {
1.784     bisitz   2506:                     $authtype = '<input type="hidden" name="login" value="loc" />';
1.587     raeburn  2507:                 }
                   2508:             }
                   2509:         }
1.165     raeburn  2510:     }
1.586     raeburn  2511:     $jscall = "javascript:changed_radio('loc',$in{'formname'});";
                   2512:     if ($authtype eq '') {
                   2513:         $authtype = '<input type="radio" name="login" value="loc" '.
                   2514:                     $loccheck.' onchange="'.$jscall.'" onclick="'.
                   2515:                     $jscall.'" />';
                   2516:     }
                   2517:     $autharg = '<input type="text" size="10" name="locarg" value="'.
                   2518:                $locarg.'" onchange="'.$jscall.'" />';
                   2519:     $result = &mt('[_1] Local Authentication with argument [_2]',
                   2520:                   '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  2521:     return $result;
                   2522: }
                   2523: 
                   2524: sub authform_filesystem{  
                   2525:     my %in = (
                   2526:               formname => 'document.cu',
                   2527:               kerb_def_dom => 'MSU.EDU',
                   2528:               @_,
                   2529:               );
1.586     raeburn  2530:     my ($fsyscheck,$result,$authtype,$autharg,$jscall);
                   2531:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.591     raeburn  2532:     if (defined($in{'curr_authtype'})) {
                   2533:         if ($in{'curr_authtype'} eq 'fsys') {
1.586     raeburn  2534:             if ($can_assign{'fsys'}) {
1.772     bisitz   2535:                 $fsyscheck = 'checked="checked" ';
1.623     raeburn  2536:                 if (defined($in{'mode'})) {
                   2537:                     if ($in{'mode'} eq 'modifyuser') {
                   2538:                         $fsyscheck = '';
                   2539:                     }
                   2540:                 }
1.586     raeburn  2541:             } else {
                   2542:                 $result = &mt('Currently Filesystem Authenticated.');
                   2543:                 return $result;
                   2544:             }           
                   2545:         }
                   2546:     } else {
                   2547:         if ($authnum == 1) {
1.784     bisitz   2548:             $authtype = '<input type="hidden" name="login" value="fsys" />';
1.586     raeburn  2549:         }
                   2550:     }
                   2551:     if (!$can_assign{'fsys'}) {
                   2552:         return;
1.587     raeburn  2553:     } elsif ($authtype eq '') {
1.591     raeburn  2554:         if (defined($in{'mode'})) {
1.587     raeburn  2555:             if ($in{'mode'} eq 'modifycourse') {
                   2556:                 if ($authnum == 1) {
1.784     bisitz   2557:                     $authtype = '<input type="hidden" name="login" value="fsys" />';
1.587     raeburn  2558:                 }
                   2559:             }
                   2560:         }
1.586     raeburn  2561:     }
                   2562:     $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
                   2563:     if ($authtype eq '') {
                   2564:         $authtype = '<input type="radio" name="login" value="fsys" '.
                   2565:                     $fsyscheck.' onchange="'.$jscall.'" onclick="'.
                   2566:                     $jscall.'" />';
                   2567:     }
                   2568:     $autharg = '<input type="text" size="10" name="fsysarg" value=""'.
                   2569:                ' onchange="'.$jscall.'" />';
                   2570:     $result = &mt
1.144     matthew  2571:         ('[_1] Filesystem Authenticated (with initial password [_2])',
1.281     albertel 2572:          '<label><input type="radio" name="login" value="fsys" '.
1.586     raeburn  2573:          $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.605     bisitz   2574:          '</label><input type="password" size="10" name="fsysarg" value="" '.
1.144     matthew  2575:                   'onchange="'.$jscall.'" />');
1.32      matthew  2576:     return $result;
                   2577: }
                   2578: 
1.586     raeburn  2579: sub get_assignable_auth {
                   2580:     my ($dom) = @_;
                   2581:     if ($dom eq '') {
                   2582:         $dom = $env{'request.role.domain'};
                   2583:     }
                   2584:     my %can_assign = (
                   2585:                           krb4 => 1,
                   2586:                           krb5 => 1,
                   2587:                           int  => 1,
                   2588:                           loc  => 1,
                   2589:                      );
                   2590:     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
                   2591:     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   2592:         if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
                   2593:             my $authhash = $domconfig{'usercreation'}{'authtypes'};
                   2594:             my $context;
                   2595:             if ($env{'request.role'} =~ /^au/) {
                   2596:                 $context = 'author';
                   2597:             } elsif ($env{'request.role'} =~ /^dc/) {
                   2598:                 $context = 'domain';
                   2599:             } elsif ($env{'request.course.id'}) {
                   2600:                 $context = 'course';
                   2601:             }
                   2602:             if ($context) {
                   2603:                 if (ref($authhash->{$context}) eq 'HASH') {
                   2604:                    %can_assign = %{$authhash->{$context}}; 
                   2605:                 }
                   2606:             }
                   2607:         }
                   2608:     }
                   2609:     my $authnum = 0;
                   2610:     foreach my $key (keys(%can_assign)) {
                   2611:         if ($can_assign{$key}) {
                   2612:             $authnum ++;
                   2613:         }
                   2614:     }
                   2615:     if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
                   2616:         $authnum --;
                   2617:     }
                   2618:     return ($authnum,%can_assign);
                   2619: }
                   2620: 
1.80      albertel 2621: ###############################################################
                   2622: ##    Get Kerberos Defaults for Domain                 ##
                   2623: ###############################################################
                   2624: ##
                   2625: ## Returns default kerberos version and an associated argument
                   2626: ## as listed in file domain.tab. If not listed, provides
                   2627: ## appropriate default domain and kerberos version.
                   2628: ##
                   2629: #-------------------------------------------
                   2630: 
                   2631: =pod
                   2632: 
1.648     raeburn  2633: =item * &get_kerberos_defaults()
1.80      albertel 2634: 
                   2635: get_kerberos_defaults($target_domain) returns the default kerberos
1.641     raeburn  2636: version and domain. If not found, it defaults to version 4 and the 
                   2637: domain of the server.
1.80      albertel 2638: 
1.648     raeburn  2639: =over 4
                   2640: 
1.80      albertel 2641: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
                   2642: 
1.648     raeburn  2643: =back
                   2644: 
                   2645: =back
                   2646: 
1.80      albertel 2647: =cut
                   2648: 
                   2649: #-------------------------------------------
                   2650: sub get_kerberos_defaults {
                   2651:     my $domain=shift;
1.641     raeburn  2652:     my ($krbdef,$krbdefdom);
                   2653:     my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
                   2654:     if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
                   2655:         $krbdef = $domdefaults{'auth_def'};
                   2656:         $krbdefdom = $domdefaults{'auth_arg_def'};
                   2657:     } else {
1.80      albertel 2658:         $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
                   2659:         my $krbdefdom=$1;
                   2660:         $krbdefdom=~tr/a-z/A-Z/;
                   2661:         $krbdef = "krb4";
                   2662:     }
                   2663:     return ($krbdef,$krbdefdom);
                   2664: }
1.112     bowersj2 2665: 
1.32      matthew  2666: 
1.46      matthew  2667: ###############################################################
                   2668: ##                Thesaurus Functions                        ##
                   2669: ###############################################################
1.20      www      2670: 
1.46      matthew  2671: =pod
1.20      www      2672: 
1.112     bowersj2 2673: =head1 Thesaurus Functions
                   2674: 
                   2675: =over 4
                   2676: 
1.648     raeburn  2677: =item * &initialize_keywords()
1.46      matthew  2678: 
                   2679: Initializes the package variable %Keywords if it is empty.  Uses the
                   2680: package variable $thesaurus_db_file.
                   2681: 
                   2682: =cut
                   2683: 
                   2684: ###################################################
                   2685: 
                   2686: sub initialize_keywords {
                   2687:     return 1 if (scalar keys(%Keywords));
                   2688:     # If we are here, %Keywords is empty, so fill it up
                   2689:     #   Make sure the file we need exists...
                   2690:     if (! -e $thesaurus_db_file) {
                   2691:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
                   2692:                                  " failed because it does not exist");
                   2693:         return 0;
                   2694:     }
                   2695:     #   Set up the hash as a database
                   2696:     my %thesaurus_db;
                   2697:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 2698:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  2699:         &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
                   2700:                                  $thesaurus_db_file);
                   2701:         return 0;
                   2702:     } 
                   2703:     #  Get the average number of appearances of a word.
                   2704:     my $avecount = $thesaurus_db{'average.count'};
                   2705:     #  Put keywords (those that appear > average) into %Keywords
                   2706:     while (my ($word,$data)=each (%thesaurus_db)) {
                   2707:         my ($count,undef) = split /:/,$data;
                   2708:         $Keywords{$word}++ if ($count > $avecount);
                   2709:     }
                   2710:     untie %thesaurus_db;
                   2711:     # Remove special values from %Keywords.
1.356     albertel 2712:     foreach my $value ('total.count','average.count') {
                   2713:         delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586     raeburn  2714:   }
1.46      matthew  2715:     return 1;
                   2716: }
                   2717: 
                   2718: ###################################################
                   2719: 
                   2720: =pod
                   2721: 
1.648     raeburn  2722: =item * &keyword($word)
1.46      matthew  2723: 
                   2724: Returns true if $word is a keyword.  A keyword is a word that appears more 
                   2725: than the average number of times in the thesaurus database.  Calls 
                   2726: &initialize_keywords
                   2727: 
                   2728: =cut
                   2729: 
                   2730: ###################################################
1.20      www      2731: 
                   2732: sub keyword {
1.46      matthew  2733:     return if (!&initialize_keywords());
                   2734:     my $word=lc(shift());
                   2735:     $word=~s/\W//g;
                   2736:     return exists($Keywords{$word});
1.20      www      2737: }
1.46      matthew  2738: 
                   2739: ###############################################################
                   2740: 
                   2741: =pod 
1.20      www      2742: 
1.648     raeburn  2743: =item * &get_related_words()
1.46      matthew  2744: 
1.160     matthew  2745: Look up a word in the thesaurus.  Takes a scalar argument and returns
1.46      matthew  2746: an array of words.  If the keyword is not in the thesaurus, an empty array
                   2747: will be returned.  The order of the words returned is determined by the
                   2748: database which holds them.
                   2749: 
                   2750: Uses global $thesaurus_db_file.
                   2751: 
                   2752: =cut
                   2753: 
                   2754: ###############################################################
                   2755: sub get_related_words {
                   2756:     my $keyword = shift;
                   2757:     my %thesaurus_db;
                   2758:     if (! -e $thesaurus_db_file) {
                   2759:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
                   2760:                                  "failed because the file does not exist");
                   2761:         return ();
                   2762:     }
                   2763:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 2764:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  2765:         return ();
                   2766:     } 
                   2767:     my @Words=();
1.429     www      2768:     my $count=0;
1.46      matthew  2769:     if (exists($thesaurus_db{$keyword})) {
1.356     albertel 2770: 	# The first element is the number of times
                   2771: 	# the word appears.  We do not need it now.
1.429     www      2772: 	my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
                   2773: 	my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
                   2774: 	my $threshold=$mostfrequentcount/10;
                   2775:         foreach my $possibleword (@RelatedWords) {
                   2776:             my ($word,$wordcount)=split(/\,/,$possibleword);
                   2777:             if ($wordcount>$threshold) {
                   2778: 		push(@Words,$word);
                   2779:                 $count++;
                   2780:                 if ($count>10) { last; }
                   2781: 	    }
1.20      www      2782:         }
                   2783:     }
1.46      matthew  2784:     untie %thesaurus_db;
                   2785:     return @Words;
1.14      harris41 2786: }
1.46      matthew  2787: 
1.112     bowersj2 2788: =pod
                   2789: 
                   2790: =back
                   2791: 
                   2792: =cut
1.61      www      2793: 
                   2794: # -------------------------------------------------------------- Plaintext name
1.81      albertel 2795: =pod
                   2796: 
1.112     bowersj2 2797: =head1 User Name Functions
                   2798: 
                   2799: =over 4
                   2800: 
1.648     raeburn  2801: =item * &plainname($uname,$udom,$first)
1.81      albertel 2802: 
1.112     bowersj2 2803: Takes a users logon name and returns it as a string in
1.226     albertel 2804: "first middle last generation" form 
                   2805: if $first is set to 'lastname' then it returns it as
                   2806: 'lastname generation, firstname middlename' if their is a lastname
1.81      albertel 2807: 
                   2808: =cut
1.61      www      2809: 
1.295     www      2810: 
1.81      albertel 2811: ###############################################################
1.61      www      2812: sub plainname {
1.226     albertel 2813:     my ($uname,$udom,$first)=@_;
1.537     albertel 2814:     return if (!defined($uname) || !defined($udom));
1.295     www      2815:     my %names=&getnames($uname,$udom);
1.226     albertel 2816:     my $name=&Apache::lonnet::format_name($names{'firstname'},
                   2817: 					  $names{'middlename'},
                   2818: 					  $names{'lastname'},
                   2819: 					  $names{'generation'},$first);
                   2820:     $name=~s/^\s+//;
1.62      www      2821:     $name=~s/\s+$//;
                   2822:     $name=~s/\s+/ /g;
1.353     albertel 2823:     if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62      www      2824:     return $name;
1.61      www      2825: }
1.66      www      2826: 
                   2827: # -------------------------------------------------------------------- Nickname
1.81      albertel 2828: =pod
                   2829: 
1.648     raeburn  2830: =item * &nickname($uname,$udom)
1.81      albertel 2831: 
                   2832: Gets a users name and returns it as a string as
                   2833: 
                   2834: "&quot;nickname&quot;"
1.66      www      2835: 
1.81      albertel 2836: if the user has a nickname or
                   2837: 
                   2838: "first middle last generation"
                   2839: 
                   2840: if the user does not
                   2841: 
                   2842: =cut
1.66      www      2843: 
                   2844: sub nickname {
                   2845:     my ($uname,$udom)=@_;
1.537     albertel 2846:     return if (!defined($uname) || !defined($udom));
1.295     www      2847:     my %names=&getnames($uname,$udom);
1.68      albertel 2848:     my $name=$names{'nickname'};
1.66      www      2849:     if ($name) {
                   2850:        $name='&quot;'.$name.'&quot;'; 
                   2851:     } else {
                   2852:        $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
                   2853: 	     $names{'lastname'}.' '.$names{'generation'};
                   2854:        $name=~s/\s+$//;
                   2855:        $name=~s/\s+/ /g;
                   2856:     }
                   2857:     return $name;
                   2858: }
                   2859: 
1.295     www      2860: sub getnames {
                   2861:     my ($uname,$udom)=@_;
1.537     albertel 2862:     return if (!defined($uname) || !defined($udom));
1.433     albertel 2863:     if ($udom eq 'public' && $uname eq 'public') {
                   2864: 	return ('lastname' => &mt('Public'));
                   2865:     }
1.295     www      2866:     my $id=$uname.':'.$udom;
                   2867:     my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
                   2868:     if ($cached) {
                   2869: 	return %{$names};
                   2870:     } else {
                   2871: 	my %loadnames=&Apache::lonnet::get('environment',
                   2872:                     ['firstname','middlename','lastname','generation','nickname'],
                   2873: 					 $udom,$uname);
                   2874: 	&Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
                   2875: 	return %loadnames;
                   2876:     }
                   2877: }
1.61      www      2878: 
1.542     raeburn  2879: # -------------------------------------------------------------------- getemails
1.648     raeburn  2880: 
1.542     raeburn  2881: =pod
                   2882: 
1.648     raeburn  2883: =item * &getemails($uname,$udom)
1.542     raeburn  2884: 
                   2885: Gets a user's email information and returns it as a hash with keys:
                   2886: notification, critnotification, permanentemail
                   2887: 
                   2888: For notification and critnotification, values are comma-separated lists 
1.648     raeburn  2889: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542     raeburn  2890:  
1.648     raeburn  2891: 
1.542     raeburn  2892: =cut
                   2893: 
1.648     raeburn  2894: 
1.466     albertel 2895: sub getemails {
                   2896:     my ($uname,$udom)=@_;
                   2897:     if ($udom eq 'public' && $uname eq 'public') {
                   2898: 	return;
                   2899:     }
1.467     www      2900:     if (!$udom) { $udom=$env{'user.domain'}; }
                   2901:     if (!$uname) { $uname=$env{'user.name'}; }
1.466     albertel 2902:     my $id=$uname.':'.$udom;
                   2903:     my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
                   2904:     if ($cached) {
                   2905: 	return %{$names};
                   2906:     } else {
                   2907: 	my %loadnames=&Apache::lonnet::get('environment',
                   2908:                     			   ['notification','critnotification',
                   2909: 					    'permanentemail'],
                   2910: 					   $udom,$uname);
                   2911: 	&Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
                   2912: 	return %loadnames;
                   2913:     }
                   2914: }
                   2915: 
1.551     albertel 2916: sub flush_email_cache {
                   2917:     my ($uname,$udom)=@_;
                   2918:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   2919:     if (!$uname) { $uname=$env{'user.name'};   }
                   2920:     return if ($udom eq 'public' && $uname eq 'public');
                   2921:     my $id=$uname.':'.$udom;
                   2922:     &Apache::lonnet::devalidate_cache_new('emailscache',$id);
                   2923: }
                   2924: 
1.728     raeburn  2925: # -------------------------------------------------------------------- getlangs
                   2926: 
                   2927: =pod
                   2928: 
                   2929: =item * &getlangs($uname,$udom)
                   2930: 
                   2931: Gets a user's language preference and returns it as a hash with key:
                   2932: language.
                   2933: 
                   2934: =cut
                   2935: 
                   2936: 
                   2937: sub getlangs {
                   2938:     my ($uname,$udom) = @_;
                   2939:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   2940:     if (!$uname) { $uname=$env{'user.name'};   }
                   2941:     my $id=$uname.':'.$udom;
                   2942:     my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
                   2943:     if ($cached) {
                   2944:         return %{$langs};
                   2945:     } else {
                   2946:         my %loadlangs=&Apache::lonnet::get('environment',['languages'],
                   2947:                                            $udom,$uname);
                   2948:         &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
                   2949:         return %loadlangs;
                   2950:     }
                   2951: }
                   2952: 
                   2953: sub flush_langs_cache {
                   2954:     my ($uname,$udom)=@_;
                   2955:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   2956:     if (!$uname) { $uname=$env{'user.name'};   }
                   2957:     return if ($udom eq 'public' && $uname eq 'public');
                   2958:     my $id=$uname.':'.$udom;
                   2959:     &Apache::lonnet::devalidate_cache_new('userlangs',$id);
                   2960: }
                   2961: 
1.61      www      2962: # ------------------------------------------------------------------ Screenname
1.81      albertel 2963: 
                   2964: =pod
                   2965: 
1.648     raeburn  2966: =item * &screenname($uname,$udom)
1.81      albertel 2967: 
                   2968: Gets a users screenname and returns it as a string
                   2969: 
                   2970: =cut
1.61      www      2971: 
                   2972: sub screenname {
                   2973:     my ($uname,$udom)=@_;
1.258     albertel 2974:     if ($uname eq $env{'user.name'} &&
                   2975: 	$udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212     albertel 2976:     my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68      albertel 2977:     return $names{'screenname'};
1.62      www      2978: }
                   2979: 
1.212     albertel 2980: 
1.802     bisitz   2981: # ------------------------------------------------------------- Confirm Wrapper
                   2982: =pod
                   2983: 
                   2984: =item confirmwrapper
                   2985: 
                   2986: Wrap messages about completion of operation in box
                   2987: 
                   2988: =cut
                   2989: 
                   2990: sub confirmwrapper {
                   2991:     my ($message)=@_;
                   2992:     if ($message) {
                   2993:         return "\n".'<div class="LC_confirm_box">'."\n"
                   2994:                .$message."\n"
                   2995:                .'</div>'."\n";
                   2996:     } else {
                   2997:         return $message;
                   2998:     }
                   2999: }
                   3000: 
1.62      www      3001: # ------------------------------------------------------------- Message Wrapper
                   3002: 
                   3003: sub messagewrapper {
1.369     www      3004:     my ($link,$username,$domain,$subject,$text)=@_;
1.62      www      3005:     return 
1.441     albertel 3006:         '<a href="/adm/email?compose=individual&amp;'.
                   3007:         'recname='.$username.'&amp;recdom='.$domain.
                   3008: 	'&amp;subject='.&escape($subject).'&amp;text='.&escape($text).'" '.
1.200     matthew  3009:         'title="'.&mt('Send message').'">'.$link.'</a>';
1.74      www      3010: }
1.802     bisitz   3011: 
1.74      www      3012: # --------------------------------------------------------------- Notes Wrapper
                   3013: 
                   3014: sub noteswrapper {
                   3015:     my ($link,$un,$do)=@_;
                   3016:     return 
1.896     amueller 3017: "<a href='/adm/email?recordftf=retrieve&amp;recname=$un&amp;recdom=$do'>$link</a>";
1.62      www      3018: }
1.802     bisitz   3019: 
1.62      www      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.892     amueller 3027:     return '<a href="/adm/'.$domain.'/'.$username.'/aboutme?forcestudent=1"'.
1.756     weissno  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: sub syllabuswrapper {
1.707     bisitz   3034:     my ($linktext,$coursedir,$domain)=@_;
1.208     matthew  3035:     return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61      www      3036: }
1.14      harris41 3037: 
1.802     bisitz   3038: # -----------------------------------------------------------------------------
                   3039: 
1.208     matthew  3040: sub track_student_link {
1.887     raeburn  3041:     my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
1.268     albertel 3042:     my $link ="/adm/trackstudent?";
1.208     matthew  3043:     my $title = 'View recent activity';
                   3044:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   3045:         defined($sdom)  && $sdom  !~ /^\s*$/) {
1.268     albertel 3046:         $link .= "selected_student=$sname:$sdom";
1.208     matthew  3047:         $title .= ' of this student';
1.268     albertel 3048:     } 
1.208     matthew  3049:     if (defined($target) && $target !~ /^\s*$/) {
                   3050:         $target = qq{target="$target"};
                   3051:     } else {
                   3052:         $target = '';
                   3053:     }
1.268     albertel 3054:     if ($start) { $link.='&amp;start='.$start; }
1.887     raeburn  3055:     if ($only_body) { $link .= '&amp;only_body=1'; }
1.554     albertel 3056:     $title = &mt($title);
                   3057:     $linktext = &mt($linktext);
1.448     albertel 3058:     return qq{<a href="$link" title="$title" $target>$linktext</a>}.
                   3059: 	&help_open_topic('View_recent_activity');
1.208     matthew  3060: }
                   3061: 
1.781     raeburn  3062: sub slot_reservations_link {
                   3063:     my ($linktext,$sname,$sdom,$target) = @_;
                   3064:     my $link ="/adm/slotrequest?command=showresv&amp;origin=aboutme";
                   3065:     my $title = 'View slot reservation history';
                   3066:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   3067:         defined($sdom)  && $sdom  !~ /^\s*$/) {
                   3068:         $link .= "&amp;uname=$sname&amp;udom=$sdom";
                   3069:         $title .= ' of this student';
                   3070:     }
                   3071:     if (defined($target) && $target !~ /^\s*$/) {
                   3072:         $target = qq{target="$target"};
                   3073:     } else {
                   3074:         $target = '';
                   3075:     }
                   3076:     $title = &mt($title);
                   3077:     $linktext = &mt($linktext);
                   3078:     return qq{<a href="$link" title="$title" $target>$linktext</a>};
                   3079: # FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
                   3080: 
                   3081: }
                   3082: 
1.508     www      3083: # ===================================================== Display a student photo
                   3084: 
                   3085: 
1.509     albertel 3086: sub student_image_tag {
1.508     www      3087:     my ($domain,$user)=@_;
                   3088:     my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
                   3089:     if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
                   3090: 	return '<img src="'.$imgsrc.'" align="right" />';
                   3091:     } else {
                   3092: 	return '';
                   3093:     }
                   3094: }
                   3095: 
1.112     bowersj2 3096: =pod
                   3097: 
                   3098: =back
                   3099: 
                   3100: =head1 Access .tab File Data
                   3101: 
                   3102: =over 4
                   3103: 
1.648     raeburn  3104: =item * &languageids() 
1.112     bowersj2 3105: 
                   3106: returns list of all language ids
                   3107: 
                   3108: =cut
                   3109: 
1.14      harris41 3110: sub languageids {
1.16      harris41 3111:     return sort(keys(%language));
1.14      harris41 3112: }
                   3113: 
1.112     bowersj2 3114: =pod
                   3115: 
1.648     raeburn  3116: =item * &languagedescription() 
1.112     bowersj2 3117: 
                   3118: returns description of a specified language id
                   3119: 
                   3120: =cut
                   3121: 
1.14      harris41 3122: sub languagedescription {
1.125     www      3123:     my $code=shift;
                   3124:     return  ($supported_language{$code}?'* ':'').
                   3125:             $language{$code}.
1.126     www      3126: 	    ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145     www      3127: }
                   3128: 
                   3129: sub plainlanguagedescription {
                   3130:     my $code=shift;
                   3131:     return $language{$code};
                   3132: }
                   3133: 
                   3134: sub supportedlanguagecode {
                   3135:     my $code=shift;
                   3136:     return $supported_language{$code};
1.97      www      3137: }
                   3138: 
1.112     bowersj2 3139: =pod
                   3140: 
1.648     raeburn  3141: =item * &copyrightids() 
1.112     bowersj2 3142: 
                   3143: returns list of all copyrights
                   3144: 
                   3145: =cut
                   3146: 
                   3147: sub copyrightids {
                   3148:     return sort(keys(%cprtag));
                   3149: }
                   3150: 
                   3151: =pod
                   3152: 
1.648     raeburn  3153: =item * &copyrightdescription() 
1.112     bowersj2 3154: 
                   3155: returns description of a specified copyright id
                   3156: 
                   3157: =cut
                   3158: 
                   3159: sub copyrightdescription {
1.166     www      3160:     return &mt($cprtag{shift(@_)});
1.112     bowersj2 3161: }
1.197     matthew  3162: 
                   3163: =pod
                   3164: 
1.648     raeburn  3165: =item * &source_copyrightids() 
1.192     taceyjo1 3166: 
                   3167: returns list of all source copyrights
                   3168: 
                   3169: =cut
                   3170: 
                   3171: sub source_copyrightids {
                   3172:     return sort(keys(%scprtag));
                   3173: }
                   3174: 
                   3175: =pod
                   3176: 
1.648     raeburn  3177: =item * &source_copyrightdescription() 
1.192     taceyjo1 3178: 
                   3179: returns description of a specified source copyright id
                   3180: 
                   3181: =cut
                   3182: 
                   3183: sub source_copyrightdescription {
                   3184:     return &mt($scprtag{shift(@_)});
                   3185: }
1.112     bowersj2 3186: 
                   3187: =pod
                   3188: 
1.648     raeburn  3189: =item * &filecategories() 
1.112     bowersj2 3190: 
                   3191: returns list of all file categories
                   3192: 
                   3193: =cut
                   3194: 
                   3195: sub filecategories {
                   3196:     return sort(keys(%category_extensions));
                   3197: }
                   3198: 
                   3199: =pod
                   3200: 
1.648     raeburn  3201: =item * &filecategorytypes() 
1.112     bowersj2 3202: 
                   3203: returns list of file types belonging to a given file
                   3204: category
                   3205: 
                   3206: =cut
                   3207: 
                   3208: sub filecategorytypes {
1.356     albertel 3209:     my ($cat) = @_;
                   3210:     return @{$category_extensions{lc($cat)}};
1.112     bowersj2 3211: }
                   3212: 
                   3213: =pod
                   3214: 
1.648     raeburn  3215: =item * &fileembstyle() 
1.112     bowersj2 3216: 
                   3217: returns embedding style for a specified file type
                   3218: 
                   3219: =cut
                   3220: 
                   3221: sub fileembstyle {
                   3222:     return $fe{lc(shift(@_))};
1.169     www      3223: }
                   3224: 
1.351     www      3225: sub filemimetype {
                   3226:     return $fm{lc(shift(@_))};
                   3227: }
                   3228: 
1.169     www      3229: 
                   3230: sub filecategoryselect {
                   3231:     my ($name,$value)=@_;
1.189     matthew  3232:     return &select_form($value,$name,
1.169     www      3233: 			'' => &mt('Any category'),
                   3234: 			map { $_,$_ } sort(keys(%category_extensions)));
1.112     bowersj2 3235: }
                   3236: 
                   3237: =pod
                   3238: 
1.648     raeburn  3239: =item * &filedescription() 
1.112     bowersj2 3240: 
                   3241: returns description for a specified file type
                   3242: 
                   3243: =cut
                   3244: 
                   3245: sub filedescription {
1.188     matthew  3246:     my $file_description = $fd{lc(shift())};
                   3247:     $file_description =~ s:([\[\]]):~$1:g;
                   3248:     return &mt($file_description);
1.112     bowersj2 3249: }
                   3250: 
                   3251: =pod
                   3252: 
1.648     raeburn  3253: =item * &filedescriptionex() 
1.112     bowersj2 3254: 
                   3255: returns description for a specified file type with
                   3256: extra formatting
                   3257: 
                   3258: =cut
                   3259: 
                   3260: sub filedescriptionex {
                   3261:     my $ex=shift;
1.188     matthew  3262:     my $file_description = $fd{lc($ex)};
                   3263:     $file_description =~ s:([\[\]]):~$1:g;
                   3264:     return '.'.$ex.' '.&mt($file_description);
1.112     bowersj2 3265: }
                   3266: 
                   3267: # End of .tab access
                   3268: =pod
                   3269: 
                   3270: =back
                   3271: 
                   3272: =cut
                   3273: 
                   3274: # ------------------------------------------------------------------ File Types
                   3275: sub fileextensions {
                   3276:     return sort(keys(%fe));
                   3277: }
                   3278: 
1.97      www      3279: # ----------------------------------------------------------- Display Languages
                   3280: # returns a hash with all desired display languages
                   3281: #
                   3282: 
                   3283: sub display_languages {
                   3284:     my %languages=();
1.695     raeburn  3285:     foreach my $lang (&Apache::lonlocal::preferred_languages()) {
1.356     albertel 3286: 	$languages{$lang}=1;
1.97      www      3287:     }
                   3288:     &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258     albertel 3289:     if ($env{'form.displaylanguage'}) {
1.356     albertel 3290: 	foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
                   3291: 	    $languages{$lang}=1;
1.97      www      3292:         }
                   3293:     }
                   3294:     return %languages;
1.14      harris41 3295: }
                   3296: 
1.582     albertel 3297: sub languages {
                   3298:     my ($possible_langs) = @_;
1.695     raeburn  3299:     my @preferred_langs = &Apache::lonlocal::preferred_languages();
1.582     albertel 3300:     if (!ref($possible_langs)) {
                   3301: 	if( wantarray ) {
                   3302: 	    return @preferred_langs;
                   3303: 	} else {
                   3304: 	    return $preferred_langs[0];
                   3305: 	}
                   3306:     }
                   3307:     my %possibilities = map { $_ => 1 } (@$possible_langs);
                   3308:     my @preferred_possibilities;
                   3309:     foreach my $preferred_lang (@preferred_langs) {
                   3310: 	if (exists($possibilities{$preferred_lang})) {
                   3311: 	    push(@preferred_possibilities, $preferred_lang);
                   3312: 	}
                   3313:     }
                   3314:     if( wantarray ) {
                   3315: 	return @preferred_possibilities;
                   3316:     }
                   3317:     return $preferred_possibilities[0];
                   3318: }
                   3319: 
1.742     raeburn  3320: sub user_lang {
                   3321:     my ($touname,$toudom,$fromcid) = @_;
                   3322:     my @userlangs;
                   3323:     if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
                   3324:         @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
                   3325:                     $env{'course.'.$fromcid.'.languages'}));
                   3326:     } else {
                   3327:         my %langhash = &getlangs($touname,$toudom);
                   3328:         if ($langhash{'languages'} ne '') {
                   3329:             @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
                   3330:         } else {
                   3331:             my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
                   3332:             if ($domdefs{'lang_def'} ne '') {
                   3333:                 @userlangs = ($domdefs{'lang_def'});
                   3334:             }
                   3335:         }
                   3336:     }
                   3337:     my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
                   3338:     my $user_lh = Apache::localize->get_handle(@languages);
                   3339:     return $user_lh;
                   3340: }
                   3341: 
                   3342: 
1.112     bowersj2 3343: ###############################################################
                   3344: ##               Student Answer Attempts                     ##
                   3345: ###############################################################
                   3346: 
                   3347: =pod
                   3348: 
                   3349: =head1 Alternate Problem Views
                   3350: 
                   3351: =over 4
                   3352: 
1.648     raeburn  3353: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.112     bowersj2 3354:     $getattempt, $regexp, $gradesub)
                   3355: 
                   3356: Return string with previous attempt on problem. Arguments:
                   3357: 
                   3358: =over 4
                   3359: 
                   3360: =item * $symb: Problem, including path
                   3361: 
                   3362: =item * $username: username of the desired student
                   3363: 
                   3364: =item * $domain: domain of the desired student
1.14      harris41 3365: 
1.112     bowersj2 3366: =item * $course: Course ID
1.14      harris41 3367: 
1.112     bowersj2 3368: =item * $getattempt: Leave blank for all attempts, otherwise put
                   3369:     something
1.14      harris41 3370: 
1.112     bowersj2 3371: =item * $regexp: if string matches this regexp, the string will be
                   3372:     sent to $gradesub
1.14      harris41 3373: 
1.112     bowersj2 3374: =item * $gradesub: routine that processes the string if it matches $regexp
1.14      harris41 3375: 
1.112     bowersj2 3376: =back
1.14      harris41 3377: 
1.112     bowersj2 3378: The output string is a table containing all desired attempts, if any.
1.16      harris41 3379: 
1.112     bowersj2 3380: =cut
1.1       albertel 3381: 
                   3382: sub get_previous_attempt {
1.43      ng       3383:   my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub)=@_;
1.1       albertel 3384:   my $prevattempts='';
1.43      ng       3385:   no strict 'refs';
1.1       albertel 3386:   if ($symb) {
1.3       albertel 3387:     my (%returnhash)=
                   3388:       &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1       albertel 3389:     if ($returnhash{'version'}) {
                   3390:       my %lasthash=();
                   3391:       my $version;
                   3392:       for ($version=1;$version<=$returnhash{'version'};$version++) {
1.356     albertel 3393:         foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
                   3394: 	  $lasthash{$key}=$returnhash{$version.':'.$key};
1.19      harris41 3395:         }
1.1       albertel 3396:       }
1.596     albertel 3397:       $prevattempts=&start_data_table().&start_data_table_header_row();
                   3398:       $prevattempts.='<th>'.&mt('History').'</th>';
1.356     albertel 3399:       foreach my $key (sort(keys(%lasthash))) {
                   3400: 	my ($ign,@parts) = split(/\./,$key);
1.41      ng       3401: 	if ($#parts > 0) {
1.31      albertel 3402: 	  my $data=$parts[-1];
                   3403: 	  pop(@parts);
1.596     albertel 3404: 	  $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.'&nbsp;</th>';
1.31      albertel 3405: 	} else {
1.41      ng       3406: 	  if ($#parts == 0) {
                   3407: 	    $prevattempts.='<th>'.$parts[0].'</th>';
                   3408: 	  } else {
                   3409: 	    $prevattempts.='<th>'.$ign.'</th>';
                   3410: 	  }
1.31      albertel 3411: 	}
1.16      harris41 3412:       }
1.596     albertel 3413:       $prevattempts.=&end_data_table_header_row();
1.40      ng       3414:       if ($getattempt eq '') {
                   3415: 	for ($version=1;$version<=$returnhash{'version'};$version++) {
1.596     albertel 3416: 	  $prevattempts.=&start_data_table_row().
                   3417: 	      '<td>'.&mt('Transaction [_1]',$version).'</td>';
1.356     albertel 3418: 	    foreach my $key (sort(keys(%lasthash))) {
1.581     albertel 3419: 		my $value = &format_previous_attempt_value($key,
                   3420: 							   $returnhash{$version.':'.$key});
                   3421: 		$prevattempts.='<td>'.$value.'&nbsp;</td>';   
1.40      ng       3422: 	    }
1.596     albertel 3423: 	  $prevattempts.=&end_data_table_row();
1.40      ng       3424: 	 }
1.1       albertel 3425:       }
1.596     albertel 3426:       $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356     albertel 3427:       foreach my $key (sort(keys(%lasthash))) {
1.581     albertel 3428: 	my $value = &format_previous_attempt_value($key,$lasthash{$key});
1.356     albertel 3429: 	if ($key =~/$regexp$/ && (defined &$gradesub)) {$value = &$gradesub($value)}
1.40      ng       3430: 	$prevattempts.='<td>'.$value.'&nbsp;</td>';
1.16      harris41 3431:       }
1.596     albertel 3432:       $prevattempts.= &end_data_table_row().&end_data_table();
1.1       albertel 3433:     } else {
1.596     albertel 3434:       $prevattempts=
                   3435: 	  &start_data_table().&start_data_table_row().
                   3436: 	  '<td>'.&mt('Nothing submitted - no attempts.').'</td>'.
                   3437: 	  &end_data_table_row().&end_data_table();
1.1       albertel 3438:     }
                   3439:   } else {
1.596     albertel 3440:     $prevattempts=
                   3441: 	  &start_data_table().&start_data_table_row().
                   3442: 	  '<td>'.&mt('No data.').'</td>'.
                   3443: 	  &end_data_table_row().&end_data_table();
1.1       albertel 3444:   }
1.10      albertel 3445: }
                   3446: 
1.581     albertel 3447: sub format_previous_attempt_value {
                   3448:     my ($key,$value) = @_;
                   3449:     if ($key =~ /timestamp/) {
                   3450: 	$value = &Apache::lonlocal::locallocaltime($value);
                   3451:     } elsif (ref($value) eq 'ARRAY') {
                   3452: 	$value = '('.join(', ', @{ $value }).')';
                   3453:     } else {
                   3454: 	$value = &unescape($value);
                   3455:     }
                   3456:     return $value;
                   3457: }
                   3458: 
                   3459: 
1.107     albertel 3460: sub relative_to_absolute {
                   3461:     my ($url,$output)=@_;
                   3462:     my $parser=HTML::TokeParser->new(\$output);
                   3463:     my $token;
                   3464:     my $thisdir=$url;
                   3465:     my @rlinks=();
                   3466:     while ($token=$parser->get_token) {
                   3467: 	if ($token->[0] eq 'S') {
                   3468: 	    if ($token->[1] eq 'a') {
                   3469: 		if ($token->[2]->{'href'}) {
                   3470: 		    $rlinks[$#rlinks+1]=$token->[2]->{'href'};
                   3471: 		}
                   3472: 	    } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
                   3473: 		$rlinks[$#rlinks+1]=$token->[2]->{'src'};
                   3474: 	    } elsif ($token->[1] eq 'base') {
                   3475: 		$thisdir=$token->[2]->{'href'};
                   3476: 	    }
                   3477: 	}
                   3478:     }
                   3479:     $thisdir=~s-/[^/]*$--;
1.356     albertel 3480:     foreach my $link (@rlinks) {
1.726     raeburn  3481: 	unless (($link=~/^https?\:\/\//i) ||
1.356     albertel 3482: 		($link=~/^\//) ||
                   3483: 		($link=~/^javascript:/i) ||
                   3484: 		($link=~/^mailto:/i) ||
                   3485: 		($link=~/^\#/)) {
                   3486: 	    my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
                   3487: 	    $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107     albertel 3488: 	}
                   3489:     }
                   3490: # -------------------------------------------------- Deal with Applet codebases
                   3491:     $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
                   3492:     return $output;
                   3493: }
                   3494: 
1.112     bowersj2 3495: =pod
                   3496: 
1.648     raeburn  3497: =item * &get_student_view()
1.112     bowersj2 3498: 
                   3499: show a snapshot of what student was looking at
                   3500: 
                   3501: =cut
                   3502: 
1.10      albertel 3503: sub get_student_view {
1.186     albertel 3504:   my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114     www      3505:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 3506:   my (%form);
1.10      albertel 3507:   my @elements=('symb','courseid','domain','username');
                   3508:   foreach my $element (@elements) {
1.186     albertel 3509:       $form{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 3510:   }
1.186     albertel 3511:   if (defined($moreenv)) {
                   3512:       %form=(%form,%{$moreenv});
                   3513:   }
1.236     albertel 3514:   if (defined($target)) { $form{'grade_target'} = $target; }
1.107     albertel 3515:   $feedurl=&Apache::lonnet::clutter($feedurl);
1.650     www      3516:   my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11      albertel 3517:   $userview=~s/\<body[^\>]*\>//gi;
                   3518:   $userview=~s/\<\/body\>//gi;
                   3519:   $userview=~s/\<html\>//gi;
                   3520:   $userview=~s/\<\/html\>//gi;
                   3521:   $userview=~s/\<head\>//gi;
                   3522:   $userview=~s/\<\/head\>//gi;
                   3523:   $userview=~s/action\s*\=/would_be_action\=/gi;
1.107     albertel 3524:   $userview=&relative_to_absolute($feedurl,$userview);
1.650     www      3525:   if (wantarray) {
                   3526:      return ($userview,$response);
                   3527:   } else {
                   3528:      return $userview;
                   3529:   }
                   3530: }
                   3531: 
                   3532: sub get_student_view_with_retries {
                   3533:   my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
                   3534: 
                   3535:     my $ok = 0;                 # True if we got a good response.
                   3536:     my $content;
                   3537:     my $response;
                   3538: 
                   3539:     # Try to get the student_view done. within the retries count:
                   3540:     
                   3541:     do {
                   3542:          ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
                   3543:          $ok      = $response->is_success;
                   3544:          if (!$ok) {
                   3545:             &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
                   3546:          }
                   3547:          $retries--;
                   3548:     } while (!$ok && ($retries > 0));
                   3549:     
                   3550:     if (!$ok) {
                   3551:        $content = '';          # On error return an empty content.
                   3552:     }
1.651     www      3553:     if (wantarray) {
                   3554:        return ($content, $response);
                   3555:     } else {
                   3556:        return $content;
                   3557:     }
1.11      albertel 3558: }
                   3559: 
1.112     bowersj2 3560: =pod
                   3561: 
1.648     raeburn  3562: =item * &get_student_answers() 
1.112     bowersj2 3563: 
                   3564: show a snapshot of how student was answering problem
                   3565: 
                   3566: =cut
                   3567: 
1.11      albertel 3568: sub get_student_answers {
1.100     sakharuk 3569:   my ($symb,$username,$domain,$courseid,%form) = @_;
1.114     www      3570:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 3571:   my (%moreenv);
1.11      albertel 3572:   my @elements=('symb','courseid','domain','username');
                   3573:   foreach my $element (@elements) {
1.186     albertel 3574:     $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 3575:   }
1.186     albertel 3576:   $moreenv{'grade_target'}='answer';
                   3577:   %moreenv=(%form,%moreenv);
1.497     raeburn  3578:   $feedurl = &Apache::lonnet::clutter($feedurl);
                   3579:   my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10      albertel 3580:   return $userview;
1.1       albertel 3581: }
1.116     albertel 3582: 
                   3583: =pod
                   3584: 
                   3585: =item * &submlink()
                   3586: 
1.242     albertel 3587: Inputs: $text $uname $udom $symb $target
1.116     albertel 3588: 
                   3589: Returns: A link to grades.pm such as to see the SUBM view of a student
                   3590: 
                   3591: =cut
                   3592: 
                   3593: ###############################################
                   3594: sub submlink {
1.242     albertel 3595:     my ($text,$uname,$udom,$symb,$target)=@_;
1.116     albertel 3596:     if (!($uname && $udom)) {
                   3597: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 3598: 	    &Apache::lonnet::whichuser($symb);
1.116     albertel 3599: 	if (!$symb) { $symb=$cursymb; }
                   3600:     }
1.254     matthew  3601:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      3602:     $symb=&escape($symb);
1.242     albertel 3603:     if ($target) { $target="target=\"$target\""; }
                   3604:     return '<a href="/adm/grades?&command=submission&'.
                   3605: 	'symb='.$symb.'&student='.$uname.
                   3606: 	'&userdom='.$udom.'" '.$target.'>'.$text.'</a>';
                   3607: }
                   3608: ##############################################
                   3609: 
                   3610: =pod
                   3611: 
                   3612: =item * &pgrdlink()
                   3613: 
                   3614: Inputs: $text $uname $udom $symb $target
                   3615: 
                   3616: Returns: A link to grades.pm such as to see the PGRD view of a student
                   3617: 
                   3618: =cut
                   3619: 
                   3620: ###############################################
                   3621: sub pgrdlink {
                   3622:     my $link=&submlink(@_);
                   3623:     $link=~s/(&command=submission)/$1&showgrading=yes/;
                   3624:     return $link;
                   3625: }
                   3626: ##############################################
                   3627: 
                   3628: =pod
                   3629: 
                   3630: =item * &pprmlink()
                   3631: 
                   3632: Inputs: $text $uname $udom $symb $target
                   3633: 
                   3634: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283     albertel 3635: student and a specific resource
1.242     albertel 3636: 
                   3637: =cut
                   3638: 
                   3639: ###############################################
                   3640: sub pprmlink {
                   3641:     my ($text,$uname,$udom,$symb,$target)=@_;
                   3642:     if (!($uname && $udom)) {
                   3643: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 3644: 	    &Apache::lonnet::whichuser($symb);
1.242     albertel 3645: 	if (!$symb) { $symb=$cursymb; }
                   3646:     }
1.254     matthew  3647:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      3648:     $symb=&escape($symb);
1.242     albertel 3649:     if ($target) { $target="target=\"$target\""; }
1.595     albertel 3650:     return '<a href="/adm/parmset?command=set&amp;'.
                   3651: 	'symb='.$symb.'&amp;uname='.$uname.
                   3652: 	'&amp;udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116     albertel 3653: }
                   3654: ##############################################
1.37      matthew  3655: 
1.112     bowersj2 3656: =pod
                   3657: 
                   3658: =back
                   3659: 
                   3660: =cut
                   3661: 
1.37      matthew  3662: ###############################################
1.51      www      3663: 
                   3664: 
                   3665: sub timehash {
1.687     raeburn  3666:     my ($thistime) = @_;
                   3667:     my $timezone = &Apache::lonlocal::gettimezone();
                   3668:     my $dt = DateTime->from_epoch(epoch => $thistime)
                   3669:                      ->set_time_zone($timezone);
                   3670:     my $wday = $dt->day_of_week();
                   3671:     if ($wday == 7) { $wday = 0; }
                   3672:     return ( 'second' => $dt->second(),
                   3673:              'minute' => $dt->minute(),
                   3674:              'hour'   => $dt->hour(),
                   3675:              'day'     => $dt->day_of_month(),
                   3676:              'month'   => $dt->month(),
                   3677:              'year'    => $dt->year(),
                   3678:              'weekday' => $wday,
                   3679:              'dayyear' => $dt->day_of_year(),
                   3680:              'dlsav'   => $dt->is_dst() );
1.51      www      3681: }
                   3682: 
1.370     www      3683: sub utc_string {
                   3684:     my ($date)=@_;
1.371     www      3685:     return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370     www      3686: }
                   3687: 
1.51      www      3688: sub maketime {
                   3689:     my %th=@_;
1.687     raeburn  3690:     my ($epoch_time,$timezone,$dt);
                   3691:     $timezone = &Apache::lonlocal::gettimezone();
                   3692:     eval {
                   3693:         $dt = DateTime->new( year   => $th{'year'},
                   3694:                              month  => $th{'month'},
                   3695:                              day    => $th{'day'},
                   3696:                              hour   => $th{'hour'},
                   3697:                              minute => $th{'minute'},
                   3698:                              second => $th{'second'},
                   3699:                              time_zone => $timezone,
                   3700:                          );
                   3701:     };
                   3702:     if (!$@) {
                   3703:         $epoch_time = $dt->epoch;
                   3704:         if ($epoch_time) {
                   3705:             return $epoch_time;
                   3706:         }
                   3707:     }
1.51      www      3708:     return POSIX::mktime(
                   3709:         ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210     www      3710:          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70      www      3711: }
                   3712: 
                   3713: #########################################
1.51      www      3714: 
                   3715: sub findallcourses {
1.482     raeburn  3716:     my ($roles,$uname,$udom) = @_;
1.355     albertel 3717:     my %roles;
                   3718:     if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348     albertel 3719:     my %courses;
1.51      www      3720:     my $now=time;
1.482     raeburn  3721:     if (!defined($uname)) {
                   3722:         $uname = $env{'user.name'};
                   3723:     }
                   3724:     if (!defined($udom)) {
                   3725:         $udom = $env{'user.domain'};
                   3726:     }
                   3727:     if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
                   3728:         my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
                   3729:         if (!%roles) {
                   3730:             %roles = (
                   3731:                        cc => 1,
                   3732:                        in => 1,
                   3733:                        ep => 1,
                   3734:                        ta => 1,
                   3735:                        cr => 1,
                   3736:                        st => 1,
                   3737:              );
                   3738:         }
                   3739:         foreach my $entry (keys(%roleshash)) {
                   3740:             my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
                   3741:             if ($trole =~ /^cr/) { 
                   3742:                 next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
                   3743:             } else {
                   3744:                 next if (!exists($roles{$trole}));
                   3745:             }
                   3746:             if ($tend) {
                   3747:                 next if ($tend < $now);
                   3748:             }
                   3749:             if ($tstart) {
                   3750:                 next if ($tstart > $now);
                   3751:             }
                   3752:             my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role,$realsec);
                   3753:             (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
                   3754:             if ($secpart eq '') {
                   3755:                 ($cnum,$role) = split(/_/,$cnumpart); 
                   3756:                 $sec = 'none';
                   3757:                 $realsec = '';
                   3758:             } else {
                   3759:                 $cnum = $cnumpart;
                   3760:                 ($sec,$role) = split(/_/,$secpart);
                   3761:                 $realsec = $sec;
1.490     raeburn  3762:             }
1.482     raeburn  3763:             $courses{$cdom.'_'.$cnum}{$sec} = $trole.'/'.$cdom.'/'.$cnum.'/'.$realsec;
                   3764:         }
                   3765:     } else {
                   3766:         foreach my $key (keys(%env)) {
1.483     albertel 3767: 	    if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
                   3768:                  $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482     raeburn  3769: 	        my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
                   3770: 	        next if ($role eq 'ca' || $role eq 'aa');
                   3771: 	        next if (%roles && !exists($roles{$role}));
                   3772: 	        my ($starttime,$endtime)=split(/\./,$env{$key});
                   3773:                 my $active=1;
                   3774:                 if ($starttime) {
                   3775: 		    if ($now<$starttime) { $active=0; }
                   3776:                 }
                   3777:                 if ($endtime) {
                   3778:                     if ($now>$endtime) { $active=0; }
                   3779:                 }
                   3780:                 if ($active) {
                   3781:                     if ($sec eq '') {
                   3782:                         $sec = 'none';
                   3783:                     }
                   3784:                     $courses{$cdom.'_'.$cnum}{$sec} = 
                   3785:                                      $role.'/'.$cdom.'/'.$cnum.'/'.$sec;
1.474     raeburn  3786:                 }
                   3787:             }
1.51      www      3788:         }
                   3789:     }
1.474     raeburn  3790:     return %courses;
1.51      www      3791: }
1.37      matthew  3792: 
1.54      www      3793: ###############################################
1.474     raeburn  3794: 
                   3795: sub blockcheck {
1.482     raeburn  3796:     my ($setters,$activity,$uname,$udom) = @_;
1.490     raeburn  3797: 
                   3798:     if (!defined($udom)) {
                   3799:         $udom = $env{'user.domain'};
                   3800:     }
                   3801:     if (!defined($uname)) {
                   3802:         $uname = $env{'user.name'};
                   3803:     }
                   3804: 
                   3805:     # If uname and udom are for a course, check for blocks in the course.
                   3806: 
                   3807:     if (&Apache::lonnet::is_course($udom,$uname)) {
                   3808:         my %records = &Apache::lonnet::dump('comm_block',$udom,$uname);
1.502     raeburn  3809:         my ($startblock,$endblock)=&get_blocks($setters,$activity,$udom,$uname);
1.490     raeburn  3810:         return ($startblock,$endblock);
                   3811:     }
1.474     raeburn  3812: 
1.502     raeburn  3813:     my $startblock = 0;
                   3814:     my $endblock = 0;
1.482     raeburn  3815:     my %live_courses = &findallcourses(undef,$uname,$udom);
1.474     raeburn  3816: 
1.490     raeburn  3817:     # If uname is for a user, and activity is course-specific, i.e.,
                   3818:     # boards, chat or groups, check for blocking in current course only.
1.474     raeburn  3819: 
1.490     raeburn  3820:     if (($activity eq 'boards' || $activity eq 'chat' ||
                   3821:          $activity eq 'groups') && ($env{'request.course.id'})) {
                   3822:         foreach my $key (keys(%live_courses)) {
                   3823:             if ($key ne $env{'request.course.id'}) {
                   3824:                 delete($live_courses{$key});
                   3825:             }
                   3826:         }
                   3827:     }
                   3828: 
                   3829:     my $otheruser = 0;
                   3830:     my %own_courses;
                   3831:     if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
                   3832:         # Resource belongs to user other than current user.
                   3833:         $otheruser = 1;
                   3834:         # Gather courses for current user
                   3835:         %own_courses = 
                   3836:             &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
                   3837:     }
                   3838: 
                   3839:     # Gather active course roles - course coordinator, instructor, 
                   3840:     # exam proctor, ta, student, or custom role.
1.474     raeburn  3841: 
                   3842:     foreach my $course (keys(%live_courses)) {
1.482     raeburn  3843:         my ($cdom,$cnum);
                   3844:         if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
                   3845:             $cdom = $env{'course.'.$course.'.domain'};
                   3846:             $cnum = $env{'course.'.$course.'.num'};
                   3847:         } else {
1.490     raeburn  3848:             ($cdom,$cnum) = split(/_/,$course); 
1.482     raeburn  3849:         }
                   3850:         my $no_ownblock = 0;
                   3851:         my $no_userblock = 0;
1.533     raeburn  3852:         if ($otheruser && $activity ne 'com') {
1.490     raeburn  3853:             # Check if current user has 'evb' priv for this
                   3854:             if (defined($own_courses{$course})) {
                   3855:                 foreach my $sec (keys(%{$own_courses{$course}})) {
                   3856:                     my $checkrole = 'cm./'.$cdom.'/'.$cnum;
                   3857:                     if ($sec ne 'none') {
                   3858:                         $checkrole .= '/'.$sec;
                   3859:                     }
                   3860:                     if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   3861:                         $no_ownblock = 1;
                   3862:                         last;
                   3863:                     }
                   3864:                 }
                   3865:             }
                   3866:             # if they have 'evb' priv and are currently not playing student
                   3867:             next if (($no_ownblock) &&
                   3868:                  ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
                   3869:         }
1.474     raeburn  3870:         foreach my $sec (keys(%{$live_courses{$course}})) {
1.482     raeburn  3871:             my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474     raeburn  3872:             if ($sec ne 'none') {
1.482     raeburn  3873:                 $checkrole .= '/'.$sec;
1.474     raeburn  3874:             }
1.490     raeburn  3875:             if ($otheruser) {
                   3876:                 # Resource belongs to user other than current user.
                   3877:                 # Assemble privs for that user, and check for 'evb' priv.
1.482     raeburn  3878:                 my ($trole,$tdom,$tnum,$tsec);
                   3879:                 my $entry = $live_courses{$course}{$sec};
                   3880:                 if ($entry =~ /^cr/) {
                   3881:                     ($trole,$tdom,$tnum,$tsec) = 
                   3882:                       ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
                   3883:                 } else {
                   3884:                     ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
                   3885:                 }
                   3886:                 my ($spec,$area,$trest,%allroles,%userroles);
                   3887:                 $area = '/'.$tdom.'/'.$tnum;
                   3888:                 $trest = $tnum;
                   3889:                 if ($tsec ne '') {
                   3890:                     $area .= '/'.$tsec;
                   3891:                     $trest .= '/'.$tsec;
                   3892:                 }
                   3893:                 $spec = $trole.'.'.$area;
                   3894:                 if ($trole =~ /^cr/) {
                   3895:                     &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
                   3896:                                                       $tdom,$spec,$trest,$area);
                   3897:                 } else {
                   3898:                     &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
                   3899:                                                        $tdom,$spec,$trest,$area);
                   3900:                 }
                   3901:                 my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
1.486     raeburn  3902:                 if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
                   3903:                     if ($1) {
                   3904:                         $no_userblock = 1;
                   3905:                         last;
                   3906:                     }
                   3907:                 }
1.490     raeburn  3908:             } else {
                   3909:                 # Resource belongs to current user
                   3910:                 # Check for 'evb' priv via lonnet::allowed().
1.482     raeburn  3911:                 if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   3912:                     $no_ownblock = 1;
                   3913:                     last;
                   3914:                 }
1.474     raeburn  3915:             }
                   3916:         }
                   3917:         # if they have the evb priv and are currently not playing student
1.482     raeburn  3918:         next if (($no_ownblock) &&
1.491     albertel 3919:                  ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482     raeburn  3920:         next if ($no_userblock);
1.474     raeburn  3921: 
1.866     kalberla 3922:         # Retrieve blocking times and identity of locker for course
1.490     raeburn  3923:         # of specified user, unless user has 'evb' privilege.
1.502     raeburn  3924:         
                   3925:         my ($start,$end)=&get_blocks($setters,$activity,$cdom,$cnum);
                   3926:         if (($start != 0) && 
                   3927:             (($startblock == 0) || ($startblock > $start))) {
                   3928:             $startblock = $start;
                   3929:         }
                   3930:         if (($end != 0)  &&
                   3931:             (($endblock == 0) || ($endblock < $end))) {
                   3932:             $endblock = $end;
                   3933:         }
1.490     raeburn  3934:     }
                   3935:     return ($startblock,$endblock);
                   3936: }
                   3937: 
                   3938: sub get_blocks {
                   3939:     my ($setters,$activity,$cdom,$cnum) = @_;
                   3940:     my $startblock = 0;
                   3941:     my $endblock = 0;
                   3942:     my $course = $cdom.'_'.$cnum;
                   3943:     $setters->{$course} = {};
                   3944:     $setters->{$course}{'staff'} = [];
                   3945:     $setters->{$course}{'times'} = [];
                   3946:     my %records = &Apache::lonnet::dump('comm_block',$cdom,$cnum);
                   3947:     foreach my $record (keys(%records)) {
                   3948:         my ($start,$end) = ($record =~ m/^(\d+)____(\d+)$/);
                   3949:         if ($start <= time && $end >= time) {
                   3950:             my ($staff_name,$staff_dom,$title,$blocks) =
                   3951:                 &parse_block_record($records{$record});
                   3952:             if ($blocks->{$activity} eq 'on') {
                   3953:                 push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
                   3954:                 push(@{$$setters{$course}{'times'}}, [$start,$end]);
1.491     albertel 3955:                 if ( ($startblock == 0) || ($startblock > $start) ) {
                   3956:                     $startblock = $start;
1.490     raeburn  3957:                 }
1.491     albertel 3958:                 if ( ($endblock == 0) || ($endblock < $end) ) {
                   3959:                     $endblock = $end;
1.474     raeburn  3960:                 }
                   3961:             }
                   3962:         }
                   3963:     }
                   3964:     return ($startblock,$endblock);
                   3965: }
                   3966: 
                   3967: sub parse_block_record {
                   3968:     my ($record) = @_;
                   3969:     my ($setuname,$setudom,$title,$blocks);
                   3970:     if (ref($record) eq 'HASH') {
                   3971:         ($setuname,$setudom) = split(/:/,$record->{'setter'});
                   3972:         $title = &unescape($record->{'event'});
                   3973:         $blocks = $record->{'blocks'};
                   3974:     } else {
                   3975:         my @data = split(/:/,$record,3);
                   3976:         if (scalar(@data) eq 2) {
                   3977:             $title = $data[1];
                   3978:             ($setuname,$setudom) = split(/@/,$data[0]);
                   3979:         } else {
                   3980:             ($setuname,$setudom,$title) = @data;
                   3981:         }
                   3982:         $blocks = { 'com' => 'on' };
                   3983:     }
                   3984:     return ($setuname,$setudom,$title,$blocks);
                   3985: }
                   3986: 
1.854     kalberla 3987: sub blocking_status {
                   3988:   my ($activity,$uname,$udom) = @_;
1.867     kalberla 3989:   my %setters;
1.890     droeschl 3990: 
                   3991:   # check for active blocking
1.867     kalberla 3992:   my ($startblock,$endblock)=&blockcheck(\%setters,$activity,$uname,$udom);
1.854     kalberla 3993: 
1.890     droeschl 3994:   my $blocked = $startblock && $endblock ? 1 : 0;
                   3995: 
                   3996:   # caller just wants to know whether a block is active
                   3997:   if (!wantarray) { return $blocked; }
                   3998: 
                   3999:   # build a link to a popup window containing the details
                   4000:   my $querystring  = "?activity=$activity";
                   4001:   # $uname and $udom decide whose portfolio the user is trying to look at
                   4002:      $querystring .= "&amp;udom=$udom"      if $udom;
                   4003:      $querystring .= "&amp;uname=$uname"    if $uname;
                   4004: 
                   4005:   my $output .= <<'END_MYBLOCK';
1.854     kalberla 4006:     function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
                   4007:         var options = "width=" + w + ",height=" + h + ",";
                   4008:         options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
                   4009:         options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
                   4010:         var newWin = window.open(url, wdwName, options);
                   4011:         newWin.focus();
                   4012:     }
1.890     droeschl 4013: END_MYBLOCK
1.854     kalberla 4014: 
1.890     droeschl 4015:   $output = Apache::lonhtmlcommon::scripttag($output);
                   4016:   
1.854     kalberla 4017:   my $popupUrl = "/adm/blockingstatus/$querystring";
1.890     droeschl 4018:   my $text = mt('Communication Blocked');
                   4019: 
1.867     kalberla 4020:   $output .= <<"END_BLOCK";
                   4021: <div class='LC_comblock'>
1.869     kalberla 4022:   <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890     droeschl 4023:   title='$text'>
                   4024:   <img class='LC_noBorder LC_middle' title='$text' src='/res/adm/pages/comblock.png' alt='$text'/></a>
1.869     kalberla 4025:   <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring' 
1.890     droeschl 4026:   title='$text'>$text</a>
1.867     kalberla 4027: </div>
                   4028: 
                   4029: END_BLOCK
1.474     raeburn  4030: 
1.854     kalberla 4031:   return ($blocked, $output);
                   4032: }
1.490     raeburn  4033: 
1.60      matthew  4034: ###############################################
                   4035: 
1.682     raeburn  4036: sub check_ip_acc {
                   4037:     my ($acc)=@_;
                   4038:     &Apache::lonxml::debug("acc is $acc");
                   4039:     if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
                   4040:         return 1;
                   4041:     }
                   4042:     my $allowed=0;
                   4043:     my $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'};
                   4044: 
                   4045:     my $name;
                   4046:     foreach my $pattern (split(',',$acc)) {
                   4047:         $pattern =~ s/^\s*//;
                   4048:         $pattern =~ s/\s*$//;
                   4049:         if ($pattern =~ /\*$/) {
                   4050:             #35.8.*
                   4051:             $pattern=~s/\*//;
                   4052:             if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
                   4053:         } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
                   4054:             #35.8.3.[34-56]
                   4055:             my $low=$2;
                   4056:             my $high=$3;
                   4057:             $pattern=$1;
                   4058:             if ($ip =~ /^\Q$pattern\E/) {
                   4059:                 my $last=(split(/\./,$ip))[3];
                   4060:                 if ($last <=$high && $last >=$low) { $allowed=1; }
                   4061:             }
                   4062:         } elsif ($pattern =~ /^\*/) {
                   4063:             #*.msu.edu
                   4064:             $pattern=~s/\*//;
                   4065:             if (!defined($name)) {
                   4066:                 use Socket;
                   4067:                 my $netaddr=inet_aton($ip);
                   4068:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   4069:             }
                   4070:             if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
                   4071:         } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
                   4072:             #127.0.0.1
                   4073:             if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
                   4074:         } else {
                   4075:             #some.name.com
                   4076:             if (!defined($name)) {
                   4077:                 use Socket;
                   4078:                 my $netaddr=inet_aton($ip);
                   4079:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   4080:             }
                   4081:             if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
                   4082:         }
                   4083:         if ($allowed) { last; }
                   4084:     }
                   4085:     return $allowed;
                   4086: }
                   4087: 
                   4088: ###############################################
                   4089: 
1.60      matthew  4090: =pod
                   4091: 
1.112     bowersj2 4092: =head1 Domain Template Functions
                   4093: 
                   4094: =over 4
                   4095: 
                   4096: =item * &determinedomain()
1.60      matthew  4097: 
                   4098: Inputs: $domain (usually will be undef)
                   4099: 
1.63      www      4100: Returns: Determines which domain should be used for designs
1.60      matthew  4101: 
                   4102: =cut
1.54      www      4103: 
1.60      matthew  4104: ###############################################
1.63      www      4105: sub determinedomain {
                   4106:     my $domain=shift;
1.531     albertel 4107:     if (! $domain) {
1.60      matthew  4108:         # Determine domain if we have not been given one
1.893     raeburn  4109:         $domain = &Apache::lonnet::default_login_domain();
1.258     albertel 4110:         if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
                   4111:         if ($env{'request.role.domain'}) { 
                   4112:             $domain=$env{'request.role.domain'}; 
1.60      matthew  4113:         }
                   4114:     }
1.63      www      4115:     return $domain;
                   4116: }
                   4117: ###############################################
1.517     raeburn  4118: 
1.518     albertel 4119: sub devalidate_domconfig_cache {
                   4120:     my ($udom)=@_;
                   4121:     &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
                   4122: }
                   4123: 
                   4124: # ---------------------- Get domain configuration for a domain
                   4125: sub get_domainconf {
                   4126:     my ($udom) = @_;
                   4127:     my $cachetime=1800;
                   4128:     my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
                   4129:     if (defined($cached)) { return %{$result}; }
                   4130: 
                   4131:     my %domconfig = &Apache::lonnet::get_dom('configuration',
                   4132: 					     ['login','rolecolors'],$udom);
1.632     raeburn  4133:     my (%designhash,%legacy);
1.518     albertel 4134:     if (keys(%domconfig) > 0) {
                   4135:         if (ref($domconfig{'login'}) eq 'HASH') {
1.632     raeburn  4136:             if (keys(%{$domconfig{'login'}})) {
                   4137:                 foreach my $key (keys(%{$domconfig{'login'}})) {
1.699     raeburn  4138:                     if (ref($domconfig{'login'}{$key}) eq 'HASH') {
                   4139:                         foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
                   4140:                             $designhash{$udom.'.login.'.$key.'_'.$img} = 
                   4141:                                 $domconfig{'login'}{$key}{$img};
                   4142:                         }
                   4143:                     } else {
                   4144:                         $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
                   4145:                     }
1.632     raeburn  4146:                 }
                   4147:             } else {
                   4148:                 $legacy{'login'} = 1;
1.518     albertel 4149:             }
1.632     raeburn  4150:         } else {
                   4151:             $legacy{'login'} = 1;
1.518     albertel 4152:         }
                   4153:         if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632     raeburn  4154:             if (keys(%{$domconfig{'rolecolors'}})) {
                   4155:                 foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
                   4156:                     if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
                   4157:                         foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
                   4158:                             $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
                   4159:                         }
1.518     albertel 4160:                     }
                   4161:                 }
1.632     raeburn  4162:             } else {
                   4163:                 $legacy{'rolecolors'} = 1;
1.518     albertel 4164:             }
1.632     raeburn  4165:         } else {
                   4166:             $legacy{'rolecolors'} = 1;
1.518     albertel 4167:         }
1.632     raeburn  4168:         if (keys(%legacy) > 0) {
                   4169:             my %legacyhash = &get_legacy_domconf($udom);
                   4170:             foreach my $item (keys(%legacyhash)) {
                   4171:                 if ($item =~ /^\Q$udom\E\.login/) {
                   4172:                     if ($legacy{'login'}) { 
                   4173:                         $designhash{$item} = $legacyhash{$item};
                   4174:                     }
                   4175:                 } else {
                   4176:                     if ($legacy{'rolecolors'}) {
                   4177:                         $designhash{$item} = $legacyhash{$item};
                   4178:                     }
1.518     albertel 4179:                 }
                   4180:             }
                   4181:         }
1.632     raeburn  4182:     } else {
                   4183:         %designhash = &get_legacy_domconf($udom); 
1.518     albertel 4184:     }
                   4185:     &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
                   4186: 				  $cachetime);
                   4187:     return %designhash;
                   4188: }
                   4189: 
1.632     raeburn  4190: sub get_legacy_domconf {
                   4191:     my ($udom) = @_;
                   4192:     my %legacyhash;
                   4193:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
                   4194:     my $designfile =  $designdir.'/'.$udom.'.tab';
                   4195:     if (-e $designfile) {
                   4196:         if ( open (my $fh,"<$designfile") ) {
                   4197:             while (my $line = <$fh>) {
                   4198:                 next if ($line =~ /^\#/);
                   4199:                 chomp($line);
                   4200:                 my ($key,$val)=(split(/\=/,$line));
                   4201:                 if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
                   4202:             }
                   4203:             close($fh);
                   4204:         }
                   4205:     }
                   4206:     if (-e '/home/httpd/html/adm/lonDomLogos/'.$udom.'.gif') {
                   4207:         $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
                   4208:     }
                   4209:     return %legacyhash;
                   4210: }
                   4211: 
1.63      www      4212: =pod
                   4213: 
1.112     bowersj2 4214: =item * &domainlogo()
1.63      www      4215: 
                   4216: Inputs: $domain (usually will be undef)
                   4217: 
                   4218: Returns: A link to a domain logo, if the domain logo exists.
                   4219: If the domain logo does not exist, a description of the domain.
                   4220: 
                   4221: =cut
1.112     bowersj2 4222: 
1.63      www      4223: ###############################################
                   4224: sub domainlogo {
1.517     raeburn  4225:     my $domain = &determinedomain(shift);
1.518     albertel 4226:     my %designhash = &get_domainconf($domain);    
1.517     raeburn  4227:     # See if there is a logo
                   4228:     if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519     raeburn  4229:         my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538     albertel 4230:         if ($imgsrc =~ m{^/(adm|res)/}) {
                   4231: 	    if ($imgsrc =~ m{^/res/}) {
                   4232: 		my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
                   4233: 		&Apache::lonnet::repcopy($local_name);
                   4234: 	    }
                   4235: 	   $imgsrc = &lonhttpdurl($imgsrc);
1.519     raeburn  4236:         } 
                   4237:         return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514     albertel 4238:     } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
                   4239:         return &Apache::lonnet::domain($domain,'description');
1.59      www      4240:     } else {
1.60      matthew  4241:         return '';
1.59      www      4242:     }
                   4243: }
1.63      www      4244: ##############################################
                   4245: 
                   4246: =pod
                   4247: 
1.112     bowersj2 4248: =item * &designparm()
1.63      www      4249: 
                   4250: Inputs: $which parameter; $domain (usually will be undef)
                   4251: 
                   4252: Returns: value of designparamter $which
                   4253: 
                   4254: =cut
1.112     bowersj2 4255: 
1.397     albertel 4256: 
1.400     albertel 4257: ##############################################
1.397     albertel 4258: sub designparm {
                   4259:     my ($which,$domain)=@_;
                   4260:     if (exists($env{'environment.color.'.$which})) {
1.817     bisitz   4261:         return $env{'environment.color.'.$which};
1.96      www      4262:     }
1.63      www      4263:     $domain=&determinedomain($domain);
1.518     albertel 4264:     my %domdesign = &get_domainconf($domain);
1.520     raeburn  4265:     my $output;
1.517     raeburn  4266:     if ($domdesign{$domain.'.'.$which} ne '') {
1.817     bisitz   4267:         $output = $domdesign{$domain.'.'.$which};
1.63      www      4268:     } else {
1.520     raeburn  4269:         $output = $defaultdesign{$which};
                   4270:     }
                   4271:     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635     raeburn  4272:         ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538     albertel 4273:         if ($output =~ m{^/(adm|res)/}) {
1.817     bisitz   4274:             if ($output =~ m{^/res/}) {
                   4275:                 my $local_name = &Apache::lonnet::filelocation('',$output);
                   4276:                 &Apache::lonnet::repcopy($local_name);
                   4277:             }
1.520     raeburn  4278:             $output = &lonhttpdurl($output);
                   4279:         }
1.63      www      4280:     }
1.520     raeburn  4281:     return $output;
1.63      www      4282: }
1.59      www      4283: 
1.822     bisitz   4284: ##############################################
                   4285: =pod
                   4286: 
1.832     bisitz   4287: =item * &authorspace()
                   4288: 
                   4289: Inputs: ./.
                   4290: 
                   4291: Returns: Path to the Construction Space of the current user's
                   4292:          accessed author space
                   4293:          The author space will be that of the current user
                   4294:          when accessing the own author space
                   4295:          and that of the co-author/assistent co-author
                   4296:          when accessing the co-author's/assistent co-author's
                   4297:          space
                   4298: 
                   4299: =cut
                   4300: 
                   4301: sub authorspace {
                   4302:     my $caname = '';
                   4303:     if ($env{'request.role'} =~ /^ca|^aa/) {
                   4304:         (undef,$caname) =
                   4305:             ($env{'request.role'}=~/($match_domain)\/($match_username)$/);
                   4306:     } else {
                   4307:         $caname = $env{'user.name'};
                   4308:     }
                   4309:     return '/priv/'.$caname.'/';
                   4310: }
                   4311: 
                   4312: ##############################################
                   4313: =pod
                   4314: 
1.822     bisitz   4315: =item * &head_subbox()
                   4316: 
                   4317: Inputs: $content (contains HTML code with page functions, etc.)
                   4318: 
                   4319: Returns: HTML div with $content
                   4320:          To be included in page header
                   4321: 
                   4322: =cut
                   4323: 
                   4324: sub head_subbox {
                   4325:     my ($content)=@_;
                   4326:     my $output =
1.844     bisitz   4327:         '<div id="LC_head_subbox">'
1.822     bisitz   4328:        .$content
                   4329:        .'</div>'
                   4330: }
                   4331: 
                   4332: ##############################################
                   4333: =pod
                   4334: 
                   4335: =item * &CSTR_pageheader()
                   4336: 
                   4337: Inputs: ./.
                   4338: 
                   4339: Returns: HTML div with CSTR path and recent box
                   4340:          To be included on Construction Space pages
                   4341: 
                   4342: =cut
                   4343: 
                   4344: sub CSTR_pageheader {
                   4345:     # this is for resources; directories have customtitle, and crumbs
                   4346:             # and select recent are created in lonpubdir.pm  
                   4347:     my ($uname,$thisdisfn)=
                   4348:         ($env{'request.filename'} =~ m|^/home/([^/]+)/public_html/(.*)|);
                   4349:     my $formaction='/priv/'.$uname.'/'.$thisdisfn;
                   4350:     $formaction=~s/\/+/\//g;
                   4351: 
                   4352:     my $parentpath = '';
                   4353:     my $lastitem = '';
                   4354:     if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
                   4355:         $parentpath = $1;
                   4356:         $lastitem = $2;
                   4357:     } else {
                   4358:         $lastitem = $thisdisfn;
                   4359:     }
                   4360:     return
                   4361:          '<div>'
                   4362:         .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
                   4363:         .'<b>'.&mt('Construction Space:').'</b> '
                   4364:         .'<form name="dirs" method="post" action="'.$formaction
                   4365:         .'" target="_top"><tt><b>' #FIXME lonpubdir: target="_parent"
                   4366:         .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."$lastitem</b></tt><br />"
                   4367:         #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/','_top','/priv','','+1',1)."</b></tt><br />"
                   4368:         .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
                   4369:         .'</form>'
                   4370:         .&Apache::lonmenu::constspaceform()
                   4371:         .'</div>';
                   4372: }
                   4373: 
1.60      matthew  4374: ###############################################
                   4375: ###############################################
                   4376: 
                   4377: =pod
                   4378: 
1.112     bowersj2 4379: =back
                   4380: 
1.549     albertel 4381: =head1 HTML Helpers
1.112     bowersj2 4382: 
                   4383: =over 4
                   4384: 
                   4385: =item * &bodytag()
1.60      matthew  4386: 
                   4387: Returns a uniform header for LON-CAPA web pages.
                   4388: 
                   4389: Inputs: 
                   4390: 
1.112     bowersj2 4391: =over 4
                   4392: 
                   4393: =item * $title, A title to be displayed on the page.
                   4394: 
                   4395: =item * $function, the current role (can be undef).
                   4396: 
                   4397: =item * $addentries, extra parameters for the <body> tag.
                   4398: 
                   4399: =item * $bodyonly, if defined, only return the <body> tag.
                   4400: 
                   4401: =item * $domain, if defined, force a given domain.
                   4402: 
                   4403: =item * $forcereg, if page should register as content page (relevant for 
1.86      www      4404:             text interface only)
1.60      matthew  4405: 
1.814     bisitz   4406: =item * $no_nav_bar, if true, keep the 'what is this' info but remove the
                   4407:                      navigational links
1.317     albertel 4408: 
1.338     albertel 4409: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
                   4410: 
1.361     albertel 4411: =item * $no_inline_link, if true and in remote mode, don't show the 
                   4412:          'Switch To Inline Menu' link
                   4413: 
1.460     albertel 4414: =item * $args, optional argument valid values are
                   4415:             no_auto_mt_title -> prevents &mt()ing the title arg
1.562     albertel 4416:             inherit_jsmath -> when creating popup window in a page,
                   4417:                               should it have jsmath forced on by the
                   4418:                               current page
1.460     albertel 4419: 
1.112     bowersj2 4420: =back
                   4421: 
1.60      matthew  4422: Returns: A uniform header for LON-CAPA web pages.  
                   4423: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
                   4424: If $bodyonly is undef or zero, an html string containing a <body> tag and 
                   4425: other decorations will be returned.
                   4426: 
                   4427: =cut
                   4428: 
1.54      www      4429: sub bodytag {
1.831     bisitz   4430:     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
1.816     bisitz   4431:         $no_nav_bar,$bgcolor,$no_inline_link,$args)=@_;
1.339     albertel 4432: 
1.460     albertel 4433:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.339     albertel 4434: 
1.183     matthew  4435:     $function = &get_users_function() if (!$function);
1.339     albertel 4436:     my $img =    &designparm($function.'.img',$domain);
                   4437:     my $font =   &designparm($function.'.font',$domain);
                   4438:     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
                   4439: 
1.803     bisitz   4440:     my %design = ( 'style'   => 'margin-top: 0',
1.535     albertel 4441: 		   'bgcolor' => $pgbg,
1.339     albertel 4442: 		   'text'    => $font,
                   4443:                    'alink'   => &designparm($function.'.alink',$domain),
                   4444: 		   'vlink'   => &designparm($function.'.vlink',$domain),
                   4445: 		   'link'    => &designparm($function.'.link',$domain),);
1.438     albertel 4446:     @design{keys(%$addentries)} = @$addentries{keys(%$addentries)}; 
1.339     albertel 4447: 
1.63      www      4448:  # role and realm
1.378     raeburn  4449:     my ($role,$realm) = split(/\./,$env{'request.role'},2);
                   4450:     if ($role  eq 'ca') {
1.479     albertel 4451:         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500     albertel 4452:         $realm = &plainname($rname,$rdom);
1.378     raeburn  4453:     } 
1.55      www      4454: # realm
1.258     albertel 4455:     if ($env{'request.course.id'}) {
1.378     raeburn  4456:         if ($env{'request.role'} !~ /^cr/) {
                   4457:             $role = &Apache::lonnet::plaintext($role,&course_type());
                   4458:         }
1.898     raeburn  4459:         if ($env{'request.course.sec'}) {
                   4460:             $role .= ('&nbsp;'x2).'-&nbsp;'.&mt('section:').'&nbsp;'.$env{'request.course.sec'};
                   4461:         }   
1.359     albertel 4462: 	$realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378     raeburn  4463:     } else {
                   4464:         $role = &Apache::lonnet::plaintext($role);
1.54      www      4465:     }
1.433     albertel 4466: 
1.359     albertel 4467:     if (!$realm) { $realm='&nbsp;'; }
1.55      www      4468: # Set messages
1.60      matthew  4469:     my $messages=&domainlogo($domain);
1.330     albertel 4470: 
1.438     albertel 4471:     my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329     albertel 4472: 
1.101     www      4473: # construct main body tag
1.359     albertel 4474:     my $bodytag = "<body $extra_body_attr>".
1.562     albertel 4475: 	&Apache::lontexconvert::init_math_support($args->{'inherit_jsmath'});
1.252     albertel 4476: 
1.530     albertel 4477:     if ($bodyonly) {
1.60      matthew  4478:         return $bodytag;
1.798     tempelho 4479:     } 
1.359     albertel 4480: 
1.410     albertel 4481:     my $name = &plainname($env{'user.name'},$env{'user.domain'});
1.433     albertel 4482:     if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
                   4483: 	undef($role);
1.434     albertel 4484:     } else {
                   4485: 	$name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'});
1.433     albertel 4486:     }
1.359     albertel 4487:     
1.762     bisitz   4488:     my $titleinfo = '<h1>'.$title.'</h1>';
1.359     albertel 4489:     #
                   4490:     # Extra info if you are the DC
                   4491:     my $dc_info = '';
                   4492:     if ($env{'user.adv'} && exists($env{'user.role.dc./'.
                   4493:                         $env{'course.'.$env{'request.course.id'}.
                   4494:                                  '.domain'}.'/'})) {
                   4495:         my $cid = $env{'request.course.id'};
                   4496:         $dc_info.= $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380     www      4497:         $dc_info =~ s/\s+$//;
1.359     albertel 4498:         $dc_info = '('.$dc_info.')';
                   4499:     }
                   4500: 
1.898     raeburn  4501:     $role = '<span class="LC_nobreak">('.$role.')</span>' if $role;
1.853     droeschl 4502:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   4503: 
1.837     bisitz   4504:     if ($env{'environment.remote'} eq 'off') {
1.359     albertel 4505:         # No Remote
1.903     droeschl 4506:         if ($no_nav_bar) { return $bodytag; } 
                   4507: 
                   4508:         if ($env{'request.state'} eq 'construct') { $forcereg=1; }
                   4509: 
                   4510:         #    if ($env{'request.state'} eq 'construct') {
                   4511:         #        $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
                   4512:         #    }
                   4513: 
                   4514:         $bodytag .= qq|<div id="LC_nav_bar">$name $role<br />
                   4515:             <em>$realm</em> $dc_info</div>| unless $env{'form.inhibitmenu'};
1.359     albertel 4516: 
1.903     droeschl 4517:         if (   $env{'form.inhibitmenu'} eq 'yes' 
                   4518:             || $ENV{'REQUEST_URI'} eq '/adm/logout'
                   4519:             || $env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
1.359     albertel 4520: 
1.903     droeschl 4521:             return $bodytag;
                   4522:         }
1.894     droeschl 4523: 
1.903     droeschl 4524:         $bodytag .= Apache::lonhtmlcommon::scripttag(
                   4525:             Apache::lonmenu::utilityfunctions(), 'start');
1.816     bisitz   4526: 
1.903     droeschl 4527:         $bodytag .= Apache::lonmenu::primary_menu();
1.852     droeschl 4528: 
1.903     droeschl 4529:         #don't show menus for public users
                   4530:         if($env{'user.name'} ne 'public' && $env{'user.domain'} ne 'public'){
                   4531:             $bodytag .= Apache::lonmenu::secondary_menu();
                   4532:             $bodytag .= Apache::lonmenu::serverform();
                   4533:             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); 
                   4534:             $bodytag .= Apache::lonmenu::innerregister($forcereg) if $forcereg;
                   4535:         }else{
                   4536:             # this is to seperate menu from content when there's no secondary
                   4537:             # menu. Especially needed for public accessible ressources.
                   4538:             $bodytag .= '<hr style="clear:both" />';
                   4539:             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); 
1.235     raeburn  4540:         }
1.903     droeschl 4541: 
                   4542:         #SD testing
                   4543:         #$bodytag .= Apache::lonmenu::menubuttons($forcereg);
1.235     raeburn  4544:         return $bodytag;
1.94      www      4545:     }
1.95      www      4546: 
1.93      www      4547: #
1.95      www      4548: # Top frame rendering, Remote is up
1.93      www      4549: #
1.359     albertel 4550: 
1.517     raeburn  4551:     my $imgsrc = $img;
                   4552:     if ($img =~ /^\/adm/) {
1.575     albertel 4553:         $imgsrc = &lonhttpdurl($img);
1.517     raeburn  4554:     }
                   4555:     my $upperleft='<img src="'.$imgsrc.'" alt="'.$function.'" />';
1.359     albertel 4556: 
1.305     www      4557:     # Explicit link to get inline menu
1.361     albertel 4558:     my $menu= ($no_inline_link?''
1.883     droeschl 4559: 	       :'<a href="/adm/remote?action=collapse" target="_top">'.&mt('Switch to Inline Menu Mode').'</a>');
1.853     droeschl 4560:     $bodytag .= qq|<div id="LC_nav_bar">$name $role
                   4561:             <em>$realm</em> $dc_info </div>
1.897     wenzelju 4562:             <ol class="LC_primary_menu LC_right">
1.853     droeschl 4563:                 <li>$menu</li>
                   4564:             </ol>| unless $env{'form.inhibitmenu'};
1.245     matthew  4565:     #
1.94      www      4566:     return(<<ENDBODY);
1.60      matthew  4567: $bodytag
1.359     albertel 4568: <table id="LC_title_bar" class="LC_with_remote">
1.791     tempelho 4569: <tr><td>$upperleft</td>
                   4570:     <td>$messages&nbsp;</td>
1.54      www      4571: </tr>
1.359     albertel 4572: <tr><td>$titleinfo $dc_info $menu</td>
1.368     albertel 4573: </tr>
1.356     albertel 4574: </table>
1.54      www      4575: ENDBODY
1.182     matthew  4576: }
                   4577: 
1.330     albertel 4578: sub make_attr_string {
                   4579:     my ($register,$attr_ref) = @_;
                   4580: 
                   4581:     if ($attr_ref && !ref($attr_ref)) {
                   4582: 	die("addentries Must be a hash ref ".
                   4583: 	    join(':',caller(1))." ".
                   4584: 	    join(':',caller(0))." ");
                   4585:     }
                   4586: 
                   4587:     if ($register) {
1.339     albertel 4588: 	my ($on_load,$on_unload);
                   4589: 	foreach my $key (keys(%{$attr_ref})) {
                   4590: 	    if      (lc($key) eq 'onload') {
                   4591: 		$on_load.=$attr_ref->{$key}.';';
                   4592: 		delete($attr_ref->{$key});
                   4593: 
                   4594: 	    } elsif (lc($key) eq 'onunload') {
                   4595: 		$on_unload.=$attr_ref->{$key}.';';
                   4596: 		delete($attr_ref->{$key});
                   4597: 	    }
                   4598: 	}
                   4599: 	$attr_ref->{'onload'}  =
                   4600: 	    &Apache::lonmenu::loadevents().  $on_load;
                   4601: 	$attr_ref->{'onunload'}=
                   4602: 	    &Apache::lonmenu::unloadevents().$on_unload;
                   4603:     }
                   4604: 
                   4605: # Accessibility font enhance
                   4606:     if ($env{'browser.fontenhance'} eq 'on') {
                   4607: 	my $style;
                   4608: 	foreach my $key (keys(%{$attr_ref})) {
                   4609: 	    if (lc($key) eq 'style') {
                   4610: 		$style.=$attr_ref->{$key}.';';
                   4611: 		delete($attr_ref->{$key});
                   4612: 	    }
                   4613: 	}
                   4614: 	$attr_ref->{'style'}=$style.'; font-size: x-large;';
1.330     albertel 4615:     }
1.339     albertel 4616: 
1.330     albertel 4617:     my $attr_string;
                   4618:     foreach my $attr (keys(%$attr_ref)) {
                   4619: 	$attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
                   4620:     }
                   4621:     return $attr_string;
                   4622: }
                   4623: 
                   4624: 
1.182     matthew  4625: ###############################################
1.251     albertel 4626: ###############################################
                   4627: 
                   4628: =pod
                   4629: 
                   4630: =item * &endbodytag()
                   4631: 
                   4632: Returns a uniform footer for LON-CAPA web pages.
                   4633: 
1.635     raeburn  4634: Inputs: 1 - optional reference to an args hash
                   4635: If in the hash, key for noredirectlink has a value which evaluates to true,
                   4636: a 'Continue' link is not displayed if the page contains an
                   4637: internal redirect in the <head></head> section,
                   4638: i.e., $env{'internal.head.redirect'} exists   
1.251     albertel 4639: 
                   4640: =cut
                   4641: 
                   4642: sub endbodytag {
1.635     raeburn  4643:     my ($args) = @_;
1.251     albertel 4644:     my $endbodytag='</body>';
1.269     albertel 4645:     $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
1.315     albertel 4646:     if ( exists( $env{'internal.head.redirect'} ) ) {
1.635     raeburn  4647:         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
                   4648: 	    $endbodytag=
                   4649: 	        "<br /><a href=\"$env{'internal.head.redirect'}\">".
                   4650: 	        &mt('Continue').'</a>'.
                   4651: 	        $endbodytag;
                   4652:         }
1.315     albertel 4653:     }
1.251     albertel 4654:     return $endbodytag;
                   4655: }
                   4656: 
1.352     albertel 4657: =pod
                   4658: 
                   4659: =item * &standard_css()
                   4660: 
                   4661: Returns a style sheet
                   4662: 
                   4663: Inputs: (all optional)
                   4664:             domain         -> force to color decorate a page for a specific
                   4665:                                domain
                   4666:             function       -> force usage of a specific rolish color scheme
                   4667:             bgcolor        -> override the default page bgcolor
                   4668: 
                   4669: =cut
                   4670: 
1.343     albertel 4671: sub standard_css {
1.345     albertel 4672:     my ($function,$domain,$bgcolor) = @_;
1.352     albertel 4673:     $function  = &get_users_function() if (!$function);
                   4674:     my $img    = &designparm($function.'.img',   $domain);
                   4675:     my $tabbg  = &designparm($function.'.tabbg', $domain);
                   4676:     my $font   = &designparm($function.'.font',  $domain);
1.801     tempelho 4677:     my $fontmenu = &designparm($function.'.fontmenu', $domain);
1.791     tempelho 4678: #second colour for later usage
1.345     albertel 4679:     my $sidebg = &designparm($function.'.sidebg',$domain);
1.382     albertel 4680:     my $pgbg_or_bgcolor =
                   4681: 	         $bgcolor ||
1.352     albertel 4682: 	         &designparm($function.'.pgbg',  $domain);
1.382     albertel 4683:     my $pgbg   = &designparm($function.'.pgbg',  $domain);
1.352     albertel 4684:     my $alink  = &designparm($function.'.alink', $domain);
                   4685:     my $vlink  = &designparm($function.'.vlink', $domain);
                   4686:     my $link   = &designparm($function.'.link',  $domain);
                   4687: 
1.704     muellerd 4688:     my $loginbg = &designparm('login.sidebg',$domain);
1.712     muellerd 4689:     my $bgcol = &designparm('login.bgcol',$domain);
                   4690:     my $textcol = &designparm('login.textcol',$domain);
1.704     muellerd 4691: 
1.602     albertel 4692:     my $sans                 = 'Verdana,Arial,Helvetica,sans-serif';
1.395     albertel 4693:     my $mono                 = 'monospace';
1.850     bisitz   4694:     my $data_table_head      = $sidebg;
                   4695:     my $data_table_light     = '#FAFAFA';
                   4696:     my $data_table_dark      = '#F0F0F0';
1.470     banghart 4697:     my $data_table_darker    = '#CCCCCC';
1.349     albertel 4698:     my $data_table_highlight = '#FFFF00';
1.352     albertel 4699:     my $mail_new             = '#FFBB77';
                   4700:     my $mail_new_hover       = '#DD9955';
                   4701:     my $mail_read            = '#BBBB77';
                   4702:     my $mail_read_hover      = '#999944';
                   4703:     my $mail_replied         = '#AAAA88';
                   4704:     my $mail_replied_hover   = '#888855';
                   4705:     my $mail_other           = '#99BBBB';
                   4706:     my $mail_other_hover     = '#669999';
1.391     albertel 4707:     my $table_header         = '#DDDDDD';
1.489     raeburn  4708:     my $feedback_link_bg     = '#BBBBBB';
1.701     harmsja  4709:     my $lg_border_color	     = '#C8C8C8';
1.392     albertel 4710: 
1.608     albertel 4711:     my $border = ($env{'browser.type'} eq 'explorer' ||
1.803     bisitz   4712: 		  $env{'browser.type'} eq 'safari'     ) ? '0 2px 0 2px'
                   4713: 	                                                 : '0 3px 0 4px';
1.448     albertel 4714: 
1.523     albertel 4715: 
1.343     albertel 4716:     return <<END;
1.795     www      4717: body {
                   4718:    font-family: $sans;
                   4719:    line-height:130%;
                   4720:    font-size:0.83em;
                   4721:    color:$font;
                   4722: }
                   4723: 
                   4724: a:link, a:visited { 
                   4725:   font-size:100%; 
                   4726: }
                   4727: 
                   4728: a:focus { 
                   4729:   color: red;
                   4730:   background: yellow 
                   4731: }
1.698     harmsja  4732: 
1.795     www      4733: form, .inline { 
                   4734:    display: inline; 
                   4735: }
1.721     harmsja  4736: 
1.795     www      4737: .LC_right {
                   4738:    text-align:right;
                   4739: }
                   4740: 
                   4741: .LC_middle {
                   4742:    vertical-align:middle;
                   4743: }
1.721     harmsja  4744: 
                   4745: /* just for tests */
1.754     droeschl 4746: .LC_400Box {width:400px; }
1.721     harmsja  4747: /* end */
                   4748: 
1.778     bisitz   4749: .LC_filename {
                   4750:   font-family: $mono;
                   4751:   white-space:pre;
                   4752: }
                   4753: 
                   4754: .LC_fileicon {
                   4755:   border: none;
                   4756:   height: 1.3em;
                   4757:   vertical-align: text-bottom;
                   4758:   margin-right: 0.3em;
                   4759:   text-decoration:none;
                   4760: }
                   4761: 
1.350     albertel 4762: .LC_error {
                   4763:   color: red;
                   4764:   font-size: larger;
                   4765: }
1.795     www      4766: 
1.457     albertel 4767: .LC_warning,
                   4768: .LC_diff_removed {
1.733     bisitz   4769:   color: red;
1.394     albertel 4770: }
1.532     albertel 4771: 
                   4772: .LC_info,
1.457     albertel 4773: .LC_success,
                   4774: .LC_diff_added {
1.350     albertel 4775:   color: green;
                   4776: }
1.795     www      4777: 
1.802     bisitz   4778: div.LC_confirm_box {
                   4779:   background-color: #FAFAFA;
                   4780:   border: 1px solid $lg_border_color;
                   4781:   margin-right: 0;
                   4782:   padding: 5px;
                   4783: }
                   4784: 
                   4785: div.LC_confirm_box .LC_error img,
                   4786: div.LC_confirm_box .LC_success img {
                   4787:   vertical-align: middle;
                   4788: }
                   4789: 
1.440     albertel 4790: .LC_icon {
1.771     droeschl 4791:   border: none;
1.790     droeschl 4792:   vertical-align: middle;
1.771     droeschl 4793: }
                   4794: 
1.543     albertel 4795: .LC_docs_spacer {
                   4796:   width: 25px;
                   4797:   height: 1px;
1.771     droeschl 4798:   border: none;
1.543     albertel 4799: }
1.346     albertel 4800: 
1.532     albertel 4801: .LC_internal_info {
1.735     bisitz   4802:   color: #999999;
1.532     albertel 4803: }
                   4804: 
1.794     www      4805: .LC_discussion {
                   4806:    background: $tabbg;
                   4807:    border: 1px solid black;
                   4808:    margin: 2px;
                   4809: }
                   4810: 
                   4811: .LC_disc_action_links_bar {
                   4812:    background: $tabbg;
1.803     bisitz   4813:    border: none;
1.795     www      4814:    margin: 4px;
1.794     www      4815: }
                   4816: 
                   4817: .LC_disc_action_left {
                   4818:    text-align: left;
                   4819: }
                   4820: 
                   4821: .LC_disc_action_right {
                   4822:    text-align: right;
                   4823: }
                   4824: 
                   4825: .LC_disc_new_item {
                   4826:    background: white;
                   4827:    border: 2px solid red;
                   4828:    margin: 2px;
                   4829: }
                   4830: 
                   4831: .LC_disc_old_item {
                   4832:    background: white;
                   4833:    border: 1px solid black;
                   4834:    margin: 2px;
                   4835: }
                   4836: 
1.458     albertel 4837: table.LC_pastsubmission {
                   4838:   border: 1px solid black;
                   4839:   margin: 2px;
                   4840: }
                   4841: 
1.795     www      4842: table#LC_top_nav,
                   4843: table#LC_menubuttons,
                   4844: table#LC_nav_location {
1.345     albertel 4845:   width: 100%;
                   4846:   background: $pgbg;
1.392     albertel 4847:   border: 2px;
1.402     albertel 4848:   border-collapse: separate;
1.803     bisitz   4849:   padding: 0;
1.345     albertel 4850: }
1.392     albertel 4851: 
1.801     tempelho 4852: table#LC_title_bar a {
                   4853:   color: $fontmenu;
                   4854: }
1.836     bisitz   4855: 
1.807     droeschl 4856: table#LC_title_bar {
1.819     tempelho 4857:   clear: both;
1.836     bisitz   4858:   display: none;
1.807     droeschl 4859: }
                   4860: 
1.795     www      4861: table#LC_title_bar,
                   4862: table.LC_breadcrumbs,
1.393     albertel 4863: table#LC_title_bar.LC_with_remote {
1.359     albertel 4864:   width: 100%;
1.392     albertel 4865:   border-color: $pgbg;
                   4866:   border-style: solid;
                   4867:   border-width: $border;
1.379     albertel 4868:   background: $pgbg;
1.801     tempelho 4869:   color: $fontmenu;
1.392     albertel 4870:   border-collapse: collapse;
1.803     bisitz   4871:   padding: 0;
1.819     tempelho 4872:   margin: 0;
1.359     albertel 4873: }
1.795     www      4874: 
1.359     albertel 4875: table#LC_title_bar td {
                   4876:   background: $tabbg;
                   4877: }
1.795     www      4878: 
1.706     harmsja  4879: table#LC_menubuttons img{
1.803     bisitz   4880:   border: none;
1.346     albertel 4881: }
1.795     www      4882: 
1.345     albertel 4883: table#LC_top_nav td {
                   4884:   background: $tabbg;
1.803     bisitz   4885:   border: none;
1.407     albertel 4886:   font-size: small;
1.706     harmsja  4887:   vertical-align:top;
                   4888:   padding:2px 5px 2px 5px;
1.345     albertel 4889: }
1.795     www      4890: 
                   4891: table#LC_top_nav td a,
                   4892: div#LC_top_nav a {
1.345     albertel 4893:   color: $font;
                   4894: }
1.795     www      4895: 
1.364     albertel 4896: table#LC_top_nav td.LC_top_nav_logo {
                   4897:   background: $tabbg;
1.432     albertel 4898:   text-align: left;
1.408     albertel 4899:   white-space: nowrap;
1.432     albertel 4900:   width: 31px;
1.408     albertel 4901: }
1.795     www      4902: 
1.408     albertel 4903: table#LC_top_nav td.LC_top_nav_logo img {
1.803     bisitz   4904:   border: none;
1.408     albertel 4905:   vertical-align: bottom;
1.364     albertel 4906: }
1.795     www      4907: 
1.777     tempelho 4908: table#LC_top_nav td.LC_top_nav_exit,
1.779     bisitz   4909: table#LC_top_nav td.LC_top_nav_help {
1.777     tempelho 4910:   width: 2.0em;
                   4911: }
1.795     www      4912: 
1.442     albertel 4913: table#LC_top_nav td.LC_top_nav_login {
                   4914:   width: 4.0em;
                   4915:   text-align: center;
                   4916: }
1.795     www      4917: 
1.842     droeschl 4918: .LC_breadcrumbs_component {
                   4919:     float: right;
                   4920:     margin: 0 1em;
1.357     albertel 4921: }
1.842     droeschl 4922: .LC_breadcrumbs_component img {
                   4923:     vertical-align: middle;
1.777     tempelho 4924: }
1.795     www      4925: 
1.383     albertel 4926: td.LC_table_cell_checkbox {
                   4927:   text-align: center;
                   4928: }
1.795     www      4929: 
1.779     bisitz   4930: table#LC_mainmenu td.LC_mainmenu_column {
                   4931:     vertical-align: top;
1.777     tempelho 4932: }
1.522     albertel 4933: 
1.795     www      4934: .LC_fontsize_small {
1.705     tempelho 4935:  font-size: 70%;
                   4936: }
                   4937: 
1.844     bisitz   4938: #LC_breadcrumbs {
1.819     tempelho 4939:  clear:both;
                   4940:  background: $sidebg;
1.822     bisitz   4941:  border-bottom: 1px solid $lg_border_color;
1.904     droeschl 4942:  line-height: 2.5em; 
                   4943:  /* SD working here
                   4944:  height: 2.5em;
                   4945:  overflow: hidden; */
1.822     bisitz   4946:  margin: 0;
1.819     tempelho 4947:  padding: 0;
                   4948: }
1.862     bisitz   4949: 
1.839     droeschl 4950: /* Preliminary fix to hide breadcrumbs inside remote control window */
1.844     bisitz   4951: #LC_remote #LC_breadcrumbs {
1.839     droeschl 4952:     display:none;
                   4953: }
1.819     tempelho 4954: 
1.844     bisitz   4955: #LC_head_subbox {
1.822     bisitz   4956:  clear:both;
                   4957:  background: #F8F8F8; /* $sidebg; */
                   4958:  border-bottom: 1px solid $lg_border_color;
                   4959:  margin: 0 0 10px 0;
                   4960:  padding: 5px;
                   4961: }
                   4962: 
1.795     www      4963: .LC_fontsize_medium {
1.705     tempelho 4964:  font-size: 85%;
                   4965: }
                   4966: 
1.795     www      4967: .LC_fontsize_large {
1.705     tempelho 4968:  font-size: 120%;
                   4969: }
                   4970: 
1.346     albertel 4971: .LC_menubuttons_inline_text {
                   4972:   color: $font;
1.698     harmsja  4973:   font-size: 90%;
1.701     harmsja  4974:   padding-left:3px;
1.346     albertel 4975: }
                   4976: 
1.526     www      4977: .LC_menubuttons_link {
                   4978:   text-decoration: none;
                   4979: }
1.795     www      4980: 
1.522     albertel 4981: .LC_menubuttons_category {
1.521     www      4982:   color: $font;
1.526     www      4983:   background: $pgbg;
1.521     www      4984:   font-size: larger;
                   4985:   font-weight: bold;
                   4986: }
                   4987: 
1.346     albertel 4988: td.LC_menubuttons_text {
1.779     bisitz   4989:  	color: $font;
1.346     albertel 4990: }
1.706     harmsja  4991: 
1.346     albertel 4992: .LC_current_location {
                   4993:   background: $tabbg;
                   4994: }
1.795     www      4995: 
1.346     albertel 4996: .LC_new_mail {
1.634     www      4997:   background: $tabbg;
1.346     albertel 4998:   font-weight: bold;
                   4999: }
1.347     albertel 5000: 
1.795     www      5001: table.LC_data_table,
                   5002: table.LC_mail_list {
1.347     albertel 5003:   border: 1px solid #000000;
1.402     albertel 5004:   border-collapse: separate;
1.426     albertel 5005:   border-spacing: 1px;
1.610     albertel 5006:   background: $pgbg;
1.347     albertel 5007: }
1.795     www      5008: 
1.422     albertel 5009: .LC_data_table_dense {
                   5010:   font-size: small;
                   5011: }
1.795     www      5012: 
1.507     raeburn  5013: table.LC_nested_outer {
                   5014:   border: 1px solid #000000;
1.589     raeburn  5015:   border-collapse: collapse;
1.803     bisitz   5016:   border-spacing: 0;
1.507     raeburn  5017:   width: 100%;
                   5018: }
1.795     www      5019: 
1.879     raeburn  5020: table.LC_innerpickbox,
1.507     raeburn  5021: table.LC_nested {
1.803     bisitz   5022:   border: none;
1.589     raeburn  5023:   border-collapse: collapse;
1.803     bisitz   5024:   border-spacing: 0;
1.507     raeburn  5025:   width: 100%;
                   5026: }
1.795     www      5027: 
                   5028: table.LC_data_table tr th, 
                   5029: table.LC_calendar tr th, 
                   5030: table.LC_mail_list tr th,
1.879     raeburn  5031: table.LC_prior_tries tr th,
                   5032: table.LC_innerpickbox tr th {
1.349     albertel 5033:   font-weight: bold;
                   5034:   background-color: $data_table_head;
1.801     tempelho 5035:   color:$fontmenu;
1.701     harmsja  5036:   font-size:90%;
1.347     albertel 5037: }
1.795     www      5038: 
1.879     raeburn  5039: table.LC_innerpickbox tr th,
                   5040: table.LC_innerpickbox tr td {
                   5041:   vertical-align: top;
                   5042: }
                   5043: 
1.711     raeburn  5044: table.LC_data_table tr.LC_info_row > td {
1.735     bisitz   5045:   background-color: #CCCCCC;
1.711     raeburn  5046:   font-weight: bold;
                   5047:   text-align: left;
                   5048: }
1.795     www      5049: 
1.779     bisitz   5050: table.LC_data_table tr.LC_odd_row > td,
1.809     bisitz   5051: table.LC_pick_box tr > td.LC_odd_row {
1.349     albertel 5052:   background-color: $data_table_light;
1.425     albertel 5053:   padding: 2px;
1.900     bisitz   5054:   vertical-align: top;
1.347     albertel 5055: }
1.795     www      5056: 
1.610     albertel 5057: table.LC_data_table tr.LC_even_row > td,
1.809     bisitz   5058: table.LC_pick_box tr > td.LC_even_row {
1.349     albertel 5059:   background-color: $data_table_dark;
1.709     bisitz   5060:   padding: 2px;
1.900     bisitz   5061:   vertical-align: top;
1.347     albertel 5062: }
1.795     www      5063: 
1.425     albertel 5064: table.LC_data_table tr.LC_data_table_highlight td {
                   5065:   background-color: $data_table_darker;
                   5066: }
1.795     www      5067: 
1.639     raeburn  5068: table.LC_data_table tr td.LC_leftcol_header {
                   5069:   background-color: $data_table_head;
                   5070:   font-weight: bold;
                   5071: }
1.795     www      5072: 
1.451     albertel 5073: table.LC_data_table tr.LC_empty_row td,
1.507     raeburn  5074: table.LC_nested tr.LC_empty_row td {
1.347     albertel 5075:   background-color: #FFFFFF;
1.421     albertel 5076:   font-weight: bold;
                   5077:   font-style: italic;
                   5078:   text-align: center;
                   5079:   padding: 8px;
1.347     albertel 5080: }
1.795     www      5081: 
1.890     droeschl 5082: table.LC_caption {
                   5083: }
                   5084: 
1.507     raeburn  5085: table.LC_nested tr.LC_empty_row td {
1.465     albertel 5086:   padding: 4ex
                   5087: }
1.795     www      5088: 
1.507     raeburn  5089: table.LC_nested_outer tr th {
                   5090:   font-weight: bold;
1.801     tempelho 5091:   color:$fontmenu;
1.507     raeburn  5092:   background-color: $data_table_head;
1.701     harmsja  5093:   font-size: small;
1.507     raeburn  5094:   border-bottom: 1px solid #000000;
                   5095: }
1.795     www      5096: 
1.507     raeburn  5097: table.LC_nested_outer tr td.LC_subheader {
                   5098:   background-color: $data_table_head;
                   5099:   font-weight: bold;
                   5100:   font-size: small;
                   5101:   border-bottom: 1px solid #000000;
                   5102:   text-align: right;
1.451     albertel 5103: }
1.795     www      5104: 
1.507     raeburn  5105: table.LC_nested tr.LC_info_row td {
1.735     bisitz   5106:   background-color: #CCCCCC;
1.451     albertel 5107:   font-weight: bold;
                   5108:   font-size: small;
1.507     raeburn  5109:   text-align: center;
                   5110: }
1.795     www      5111: 
1.589     raeburn  5112: table.LC_nested tr.LC_info_row td.LC_left_item,
                   5113: table.LC_nested_outer tr th.LC_left_item {
1.507     raeburn  5114:   text-align: left;
1.451     albertel 5115: }
1.795     www      5116: 
1.507     raeburn  5117: table.LC_nested td {
1.735     bisitz   5118:   background-color: #FFFFFF;
1.451     albertel 5119:   font-size: small;
1.507     raeburn  5120: }
1.795     www      5121: 
1.507     raeburn  5122: table.LC_nested_outer tr th.LC_right_item,
                   5123: table.LC_nested tr.LC_info_row td.LC_right_item,
                   5124: table.LC_nested tr.LC_odd_row td.LC_right_item,
                   5125: table.LC_nested tr td.LC_right_item {
1.451     albertel 5126:   text-align: right;
                   5127: }
                   5128: 
1.507     raeburn  5129: table.LC_nested tr.LC_odd_row td {
1.735     bisitz   5130:   background-color: #EEEEEE;
1.451     albertel 5131: }
                   5132: 
1.473     raeburn  5133: table.LC_createuser {
                   5134: }
                   5135: 
                   5136: table.LC_createuser tr.LC_section_row td {
1.701     harmsja  5137:   font-size: small;
1.473     raeburn  5138: }
                   5139: 
                   5140: table.LC_createuser tr.LC_info_row td  {
1.735     bisitz   5141:   background-color: #CCCCCC;
1.473     raeburn  5142:   font-weight: bold;
                   5143:   text-align: center;
                   5144: }
                   5145: 
1.349     albertel 5146: table.LC_calendar {
                   5147:   border: 1px solid #000000;
                   5148:   border-collapse: collapse;
                   5149: }
1.795     www      5150: 
1.349     albertel 5151: table.LC_calendar_pickdate {
                   5152:   font-size: xx-small;
                   5153: }
1.795     www      5154: 
1.349     albertel 5155: table.LC_calendar tr td {
                   5156:   border: 1px solid #000000;
                   5157:   vertical-align: top;
                   5158: }
1.795     www      5159: 
1.349     albertel 5160: table.LC_calendar tr td.LC_calendar_day_empty {
                   5161:   background-color: $data_table_dark;
                   5162: }
1.795     www      5163: 
1.779     bisitz   5164: table.LC_calendar tr td.LC_calendar_day_current {
                   5165:   background-color: $data_table_highlight;
1.777     tempelho 5166: }
1.795     www      5167: 
1.349     albertel 5168: table.LC_mail_list tr.LC_mail_new {
                   5169:   background-color: $mail_new;
                   5170: }
1.795     www      5171: 
1.349     albertel 5172: table.LC_mail_list tr.LC_mail_new:hover {
                   5173:   background-color: $mail_new_hover;
                   5174: }
1.795     www      5175: 
                   5176: table.LC_mail_list tr.LC_mail_even {
1.777     tempelho 5177: }
1.795     www      5178: 
                   5179: table.LC_mail_list tr.LC_mail_odd {
1.777     tempelho 5180: }
1.795     www      5181: 
1.349     albertel 5182: table.LC_mail_list tr.LC_mail_read {
                   5183:   background-color: $mail_read;
                   5184: }
1.795     www      5185: 
1.349     albertel 5186: table.LC_mail_list tr.LC_mail_read:hover {
                   5187:   background-color: $mail_read_hover;
                   5188: }
1.795     www      5189: 
1.349     albertel 5190: table.LC_mail_list tr.LC_mail_replied {
                   5191:   background-color: $mail_replied;
                   5192: }
1.795     www      5193: 
1.349     albertel 5194: table.LC_mail_list tr.LC_mail_replied:hover {
                   5195:   background-color: $mail_replied_hover;
                   5196: }
1.795     www      5197: 
1.349     albertel 5198: table.LC_mail_list tr.LC_mail_other {
                   5199:   background-color: $mail_other;
                   5200: }
1.795     www      5201: 
1.349     albertel 5202: table.LC_mail_list tr.LC_mail_other:hover {
                   5203:   background-color: $mail_other_hover;
                   5204: }
1.494     raeburn  5205: 
1.777     tempelho 5206: table.LC_data_table tr > td.LC_browser_file,
                   5207: table.LC_data_table tr > td.LC_browser_file_published {
1.899     bisitz   5208:   background: #AAEE77;
1.389     albertel 5209: }
1.795     www      5210: 
1.777     tempelho 5211: table.LC_data_table tr > td.LC_browser_file_locked,
                   5212: table.LC_data_table tr > td.LC_browser_file_unpublished {
1.389     albertel 5213:   background: #FFAA99;
1.387     albertel 5214: }
1.795     www      5215: 
1.777     tempelho 5216: table.LC_data_table tr > td.LC_browser_file_obsolete {
1.899     bisitz   5217:   background: #888888;
1.779     bisitz   5218: }
1.795     www      5219: 
1.777     tempelho 5220: table.LC_data_table tr > td.LC_browser_file_modified,
1.779     bisitz   5221: table.LC_data_table tr > td.LC_browser_file_metamodified {
1.899     bisitz   5222:   background: #F8F866;
1.777     tempelho 5223: }
1.795     www      5224: 
1.696     bisitz   5225: table.LC_data_table tr.LC_browser_folder > td {
1.899     bisitz   5226:   background: #E0E8FF;
1.387     albertel 5227: }
1.696     bisitz   5228: 
1.707     bisitz   5229: table.LC_data_table tr > td.LC_roles_is {
                   5230: /*  background: #77FF77; */
                   5231: }
1.795     www      5232: 
1.707     bisitz   5233: table.LC_data_table tr > td.LC_roles_future {
                   5234:   background: #FFFF77;
                   5235: }
1.795     www      5236: 
1.707     bisitz   5237: table.LC_data_table tr > td.LC_roles_will {
                   5238:   background: #FFAA77;
                   5239: }
1.795     www      5240: 
1.707     bisitz   5241: table.LC_data_table tr > td.LC_roles_expired {
                   5242:   background: #FF7777;
                   5243: }
1.795     www      5244: 
1.707     bisitz   5245: table.LC_data_table tr > td.LC_roles_will_not {
                   5246:   background: #AAFF77;
                   5247: }
1.795     www      5248: 
1.707     bisitz   5249: table.LC_data_table tr > td.LC_roles_selected {
                   5250:   background: #11CC55;
                   5251: }
                   5252: 
1.388     albertel 5253: span.LC_current_location {
1.701     harmsja  5254:   font-size:larger;
1.388     albertel 5255:   background: $pgbg;
                   5256: }
1.387     albertel 5257: 
1.395     albertel 5258: span.LC_parm_menu_item {
                   5259:   font-size: larger;
                   5260: }
1.795     www      5261: 
1.395     albertel 5262: span.LC_parm_scope_all {
                   5263:   color: red;
                   5264: }
1.795     www      5265: 
1.395     albertel 5266: span.LC_parm_scope_folder {
                   5267:   color: green;
                   5268: }
1.795     www      5269: 
1.395     albertel 5270: span.LC_parm_scope_resource {
                   5271:   color: orange;
                   5272: }
1.795     www      5273: 
1.395     albertel 5274: span.LC_parm_part {
                   5275:   color: blue;
                   5276: }
1.795     www      5277: 
1.395     albertel 5278: span.LC_parm_folder, span.LC_parm_symb {
                   5279:   font-size: x-small;
                   5280:   font-family: $mono;
                   5281:   color: #AAAAAA;
                   5282: }
                   5283: 
1.795     www      5284: td.LC_parm_overview_level_menu,
                   5285: td.LC_parm_overview_map_menu,
                   5286: td.LC_parm_overview_parm_selectors,
                   5287: td.LC_parm_overview_restrictions  {
1.396     albertel 5288:   border: 1px solid black;
                   5289:   border-collapse: collapse;
                   5290: }
1.795     www      5291: 
1.396     albertel 5292: table.LC_parm_overview_restrictions td {
                   5293:   border-width: 1px 4px 1px 4px;
                   5294:   border-style: solid;
                   5295:   border-color: $pgbg;
                   5296:   text-align: center;
                   5297: }
1.795     www      5298: 
1.396     albertel 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.795     www      5305: 
1.398     albertel 5306: table#LC_helpmenu {
1.803     bisitz   5307:   border: none;
1.398     albertel 5308:   height: 55px;
1.803     bisitz   5309:   border-spacing: 0;
1.398     albertel 5310: }
                   5311: 
                   5312: table#LC_helpmenu fieldset legend {
                   5313:   font-size: larger;
                   5314: }
1.795     www      5315: 
1.397     albertel 5316: table#LC_helpmenu_links {
                   5317:   width: 100%;
                   5318:   border: 1px solid black;
                   5319:   background: $pgbg;
1.803     bisitz   5320:   padding: 0;
1.397     albertel 5321:   border-spacing: 1px;
                   5322: }
1.795     www      5323: 
1.397     albertel 5324: table#LC_helpmenu_links tr td {
                   5325:   padding: 1px;
                   5326:   background: $tabbg;
1.399     albertel 5327:   text-align: center;
                   5328:   font-weight: bold;
1.397     albertel 5329: }
1.396     albertel 5330: 
1.795     www      5331: table#LC_helpmenu_links a:link,
                   5332: table#LC_helpmenu_links a:visited,
1.397     albertel 5333: table#LC_helpmenu_links a:active {
                   5334:   text-decoration: none;
                   5335:   color: $font;
                   5336: }
1.795     www      5337: 
1.397     albertel 5338: table#LC_helpmenu_links a:hover {
                   5339:   text-decoration: underline;
                   5340:   color: $vlink;
                   5341: }
1.396     albertel 5342: 
1.417     albertel 5343: .LC_chrt_popup_exists {
                   5344:   border: 1px solid #339933;
                   5345:   margin: -1px;
                   5346: }
1.795     www      5347: 
1.417     albertel 5348: .LC_chrt_popup_up {
                   5349:   border: 1px solid yellow;
                   5350:   margin: -1px;
                   5351: }
1.795     www      5352: 
1.417     albertel 5353: .LC_chrt_popup {
                   5354:   border: 1px solid #8888FF;
                   5355:   background: #CCCCFF;
                   5356: }
1.795     www      5357: 
1.421     albertel 5358: table.LC_pick_box {
                   5359:   border-collapse: separate;
                   5360:   background: white;
                   5361:   border: 1px solid black;
                   5362:   border-spacing: 1px;
                   5363: }
1.795     www      5364: 
1.421     albertel 5365: table.LC_pick_box td.LC_pick_box_title {
1.850     bisitz   5366:   background: $sidebg;
1.421     albertel 5367:   font-weight: bold;
1.900     bisitz   5368:   text-align: left;
1.740     bisitz   5369:   vertical-align: top;
1.421     albertel 5370:   width: 184px;
                   5371:   padding: 8px;
                   5372: }
1.795     www      5373: 
1.579     raeburn  5374: table.LC_pick_box td.LC_pick_box_value {
                   5375:   text-align: left;
                   5376:   padding: 8px;
                   5377: }
1.795     www      5378: 
1.579     raeburn  5379: table.LC_pick_box td.LC_pick_box_select {
                   5380:   text-align: left;
                   5381:   padding: 8px;
                   5382: }
1.795     www      5383: 
1.424     albertel 5384: table.LC_pick_box td.LC_pick_box_separator {
1.803     bisitz   5385:   padding: 0;
1.421     albertel 5386:   height: 1px;
                   5387:   background: black;
                   5388: }
1.795     www      5389: 
1.421     albertel 5390: table.LC_pick_box td.LC_pick_box_submit {
                   5391:   text-align: right;
                   5392: }
1.795     www      5393: 
1.579     raeburn  5394: table.LC_pick_box td.LC_evenrow_value {
                   5395:   text-align: left;
                   5396:   padding: 8px;
                   5397:   background-color: $data_table_light;
                   5398: }
1.795     www      5399: 
1.579     raeburn  5400: table.LC_pick_box td.LC_oddrow_value {
                   5401:   text-align: left;
                   5402:   padding: 8px;
                   5403:   background-color: $data_table_light;
                   5404: }
1.795     www      5405: 
1.579     raeburn  5406: span.LC_helpform_receipt_cat {
                   5407:   font-weight: bold;
                   5408: }
1.795     www      5409: 
1.424     albertel 5410: table.LC_group_priv_box {
                   5411:   background: white;
                   5412:   border: 1px solid black;
                   5413:   border-spacing: 1px;
                   5414: }
1.795     www      5415: 
1.424     albertel 5416: table.LC_group_priv_box td.LC_pick_box_title {
                   5417:   background: $tabbg;
                   5418:   font-weight: bold;
                   5419:   text-align: right;
                   5420:   width: 184px;
                   5421: }
1.795     www      5422: 
1.424     albertel 5423: table.LC_group_priv_box td.LC_groups_fixed {
                   5424:   background: $data_table_light;
                   5425:   text-align: center;
                   5426: }
1.795     www      5427: 
1.424     albertel 5428: table.LC_group_priv_box td.LC_groups_optional {
                   5429:   background: $data_table_dark;
                   5430:   text-align: center;
                   5431: }
1.795     www      5432: 
1.424     albertel 5433: table.LC_group_priv_box td.LC_groups_functionality {
                   5434:   background: $data_table_darker;
                   5435:   text-align: center;
                   5436:   font-weight: bold;
                   5437: }
1.795     www      5438: 
1.424     albertel 5439: table.LC_group_priv td {
                   5440:   text-align: left;
1.803     bisitz   5441:   padding: 0;
1.424     albertel 5442: }
                   5443: 
1.421     albertel 5444: table.LC_notify_front_page {
                   5445:   background: white;
                   5446:   border: 1px solid black;
                   5447:   padding: 8px;
                   5448: }
1.795     www      5449: 
1.421     albertel 5450: table.LC_notify_front_page td {
                   5451:   padding: 8px;
                   5452: }
1.795     www      5453: 
1.424     albertel 5454: .LC_navbuttons {
                   5455:   margin: 2ex 0ex 2ex 0ex;
                   5456: }
1.795     www      5457: 
1.423     albertel 5458: .LC_topic_bar {
                   5459:   font-weight: bold;
                   5460:   width: 100%;
                   5461:   background: $tabbg;
                   5462:   vertical-align: middle;
                   5463:   margin: 2ex 0ex 2ex 0ex;
1.805     bisitz   5464:   padding: 3px;
1.423     albertel 5465: }
1.795     www      5466: 
1.423     albertel 5467: .LC_topic_bar span {
                   5468:   vertical-align: middle;
                   5469: }
1.795     www      5470: 
1.423     albertel 5471: .LC_topic_bar img {
                   5472:   vertical-align: bottom;
                   5473: }
1.795     www      5474: 
1.423     albertel 5475: table.LC_course_group_status {
                   5476:   margin: 20px;
                   5477: }
1.795     www      5478: 
1.423     albertel 5479: table.LC_status_selector td {
                   5480:   vertical-align: top;
                   5481:   text-align: center;
1.424     albertel 5482:   padding: 4px;
                   5483: }
1.795     www      5484: 
1.599     albertel 5485: div.LC_feedback_link {
1.616     albertel 5486:   clear: both;
1.829     kalberla 5487:   background: $sidebg;
1.779     bisitz   5488:   width: 100%;
1.829     kalberla 5489:   padding-bottom: 10px;
                   5490:   border: 1px $tabbg solid;
1.833     kalberla 5491:   height: 22px;
                   5492:   line-height: 22px;
                   5493:   padding-top: 5px;
                   5494: }
                   5495: 
                   5496: div.LC_feedback_link img {
                   5497:   height: 22px;
1.867     kalberla 5498:   vertical-align:middle;
1.829     kalberla 5499: }
                   5500: 
                   5501: div.LC_feedback_link a{
                   5502:   text-decoration: none;
1.489     raeburn  5503: }
1.795     www      5504: 
1.867     kalberla 5505: div.LC_comblock {
                   5506:   display:inline; 
                   5507:   color:$font;
                   5508:   font-size:90%;
                   5509: }
                   5510: 
                   5511: div.LC_feedback_link div.LC_comblock {
                   5512:   padding-left:5px;
                   5513: }
                   5514: 
                   5515: div.LC_feedback_link div.LC_comblock a {
                   5516:   color:$font;
                   5517: }
                   5518: 
1.489     raeburn  5519: span.LC_feedback_link {
1.858     bisitz   5520:   /* background: $feedback_link_bg; */
1.599     albertel 5521:   font-size: larger;
                   5522: }
1.795     www      5523: 
1.599     albertel 5524: span.LC_message_link {
1.858     bisitz   5525:   /* background: $feedback_link_bg; */
1.599     albertel 5526:   font-size: larger;
                   5527:   position: absolute;
                   5528:   right: 1em;
1.489     raeburn  5529: }
1.421     albertel 5530: 
1.515     albertel 5531: table.LC_prior_tries {
1.524     albertel 5532:   border: 1px solid #000000;
                   5533:   border-collapse: separate;
                   5534:   border-spacing: 1px;
1.515     albertel 5535: }
1.523     albertel 5536: 
1.515     albertel 5537: table.LC_prior_tries td {
1.524     albertel 5538:   padding: 2px;
1.515     albertel 5539: }
1.523     albertel 5540: 
                   5541: .LC_answer_correct {
1.795     www      5542:   background: lightgreen;
                   5543:   color: darkgreen;
                   5544:   padding: 6px;
1.523     albertel 5545: }
1.795     www      5546: 
1.523     albertel 5547: .LC_answer_charged_try {
1.797     www      5548:   background: #FFAAAA;
1.795     www      5549:   color: darkred;
                   5550:   padding: 6px;
1.523     albertel 5551: }
1.795     www      5552: 
1.779     bisitz   5553: .LC_answer_not_charged_try,
1.523     albertel 5554: .LC_answer_no_grade,
                   5555: .LC_answer_late {
1.795     www      5556:   background: lightyellow;
1.523     albertel 5557:   color: black;
1.795     www      5558:   padding: 6px;
1.523     albertel 5559: }
1.795     www      5560: 
1.523     albertel 5561: .LC_answer_previous {
1.795     www      5562:   background: lightblue;
                   5563:   color: darkblue;
                   5564:   padding: 6px;
1.523     albertel 5565: }
1.795     www      5566: 
1.779     bisitz   5567: .LC_answer_no_message {
1.777     tempelho 5568:   background: #FFFFFF;
                   5569:   color: black;
1.795     www      5570:   padding: 6px;
1.779     bisitz   5571: }
1.795     www      5572: 
1.779     bisitz   5573: .LC_answer_unknown {
                   5574:   background: orange;
                   5575:   color: black;
1.795     www      5576:   padding: 6px;
1.777     tempelho 5577: }
1.795     www      5578: 
1.529     albertel 5579: span.LC_prior_numerical,
                   5580: span.LC_prior_string,
                   5581: span.LC_prior_custom,
                   5582: span.LC_prior_reaction,
                   5583: span.LC_prior_math {
1.523     albertel 5584:   font-family: monospace;
                   5585:   white-space: pre;
                   5586: }
                   5587: 
1.525     albertel 5588: span.LC_prior_string {
                   5589:   font-family: monospace;
                   5590:   white-space: pre;
                   5591: }
                   5592: 
1.523     albertel 5593: table.LC_prior_option {
                   5594:   width: 100%;
                   5595:   border-collapse: collapse;
                   5596: }
1.795     www      5597: 
                   5598: table.LC_prior_rank, 
                   5599: table.LC_prior_match {
1.528     albertel 5600:   border-collapse: collapse;
                   5601: }
1.795     www      5602: 
1.528     albertel 5603: table.LC_prior_option tr td,
                   5604: table.LC_prior_rank tr td,
                   5605: table.LC_prior_match tr td {
1.524     albertel 5606:   border: 1px solid #000000;
1.515     albertel 5607: }
                   5608: 
1.855     bisitz   5609: .LC_nobreak {
1.544     albertel 5610:   white-space: nowrap;
1.519     raeburn  5611: }
                   5612: 
1.576     raeburn  5613: span.LC_cusr_emph {
                   5614:   font-style: italic;
                   5615: }
                   5616: 
1.633     raeburn  5617: span.LC_cusr_subheading {
                   5618:   font-weight: normal;
                   5619:   font-size: 85%;
                   5620: }
                   5621: 
1.545     albertel 5622: table.LC_docs_documents {
                   5623:   background: #BBBBBB;
1.803     bisitz   5624:   border-width: 0;
1.545     albertel 5625:   border-collapse: collapse;
                   5626: }
1.795     www      5627: 
1.777     tempelho 5628: table.LC_docs_documents td.LC_docs_document {
1.779     bisitz   5629:   border: 2px solid black;
                   5630:   padding: 4px;
1.777     tempelho 5631: }
1.795     www      5632: 
1.861     bisitz   5633: div.LC_docs_entry_move {
1.859     bisitz   5634:   border: 1px solid #BBBBBB;
1.545     albertel 5635:   background: #DDDDDD;
1.861     bisitz   5636:   width: 22px;
1.859     bisitz   5637:   padding: 1px;
                   5638:   margin: 0;
1.545     albertel 5639: }
                   5640: 
1.861     bisitz   5641: table.LC_data_table tr > td.LC_docs_entry_commands,
                   5642: table.LC_data_table tr > td.LC_docs_entry_parameter {
1.545     albertel 5643:   background: #DDDDDD;
                   5644:   font-size: x-small;
                   5645: }
1.795     www      5646: 
1.861     bisitz   5647: .LC_docs_entry_parameter {
                   5648:   white-space: nowrap;
                   5649: }
                   5650: 
1.544     albertel 5651: .LC_docs_copy {
1.545     albertel 5652:   color: #000099;
1.544     albertel 5653: }
1.795     www      5654: 
1.544     albertel 5655: .LC_docs_cut {
1.545     albertel 5656:   color: #550044;
1.544     albertel 5657: }
1.795     www      5658: 
1.544     albertel 5659: .LC_docs_rename {
1.545     albertel 5660:   color: #009900;
1.544     albertel 5661: }
1.795     www      5662: 
1.544     albertel 5663: .LC_docs_remove {
1.545     albertel 5664:   color: #990000;
                   5665: }
                   5666: 
1.547     albertel 5667: .LC_docs_reinit_warn,
                   5668: .LC_docs_ext_edit {
                   5669:   font-size: x-small;
                   5670: }
                   5671: 
1.545     albertel 5672: table.LC_docs_adddocs td,
                   5673: table.LC_docs_adddocs th {
                   5674:   border: 1px solid #BBBBBB;
                   5675:   padding: 4px;
                   5676:   background: #DDDDDD;
1.543     albertel 5677: }
                   5678: 
1.584     albertel 5679: table.LC_sty_begin {
                   5680:   background: #BBFFBB;
                   5681: }
1.795     www      5682: 
1.584     albertel 5683: table.LC_sty_end {
                   5684:   background: #FFBBBB;
                   5685: }
                   5686: 
1.589     raeburn  5687: table.LC_double_column {
1.803     bisitz   5688:   border-width: 0;
1.589     raeburn  5689:   border-collapse: collapse;
                   5690:   width: 100%;
                   5691:   padding: 2px;
                   5692: }
                   5693: 
                   5694: table.LC_double_column tr td.LC_left_col {
1.590     raeburn  5695:   top: 2px;
1.589     raeburn  5696:   left: 2px;
                   5697:   width: 47%;
                   5698:   vertical-align: top;
                   5699: }
                   5700: 
                   5701: table.LC_double_column tr td.LC_right_col {
                   5702:   top: 2px;
1.779     bisitz   5703:   right: 2px;
1.589     raeburn  5704:   width: 47%;
                   5705:   vertical-align: top;
                   5706: }
                   5707: 
1.591     raeburn  5708: div.LC_left_float {
                   5709:   float: left;
                   5710:   padding-right: 5%;
1.597     albertel 5711:   padding-bottom: 4px;
1.591     raeburn  5712: }
                   5713: 
                   5714: div.LC_clear_float_header {
1.597     albertel 5715:   padding-bottom: 2px;
1.591     raeburn  5716: }
                   5717: 
                   5718: div.LC_clear_float_footer {
1.597     albertel 5719:   padding-top: 10px;
1.591     raeburn  5720:   clear: both;
                   5721: }
                   5722: 
1.597     albertel 5723: div.LC_grade_show_user {
                   5724:   margin-top: 20px;
                   5725:   border: 1px solid black;
                   5726: }
1.795     www      5727: 
1.597     albertel 5728: div.LC_grade_user_name {
                   5729:   background: #DDDDEE;
                   5730:   border-bottom: 1px solid black;
1.705     tempelho 5731:   font-weight: bold;
                   5732:   font-size: large;
1.597     albertel 5733: }
1.795     www      5734: 
1.597     albertel 5735: div.LC_grade_show_user_odd_row div.LC_grade_user_name {
                   5736:   background: #DDEEDD;
                   5737: }
                   5738: 
                   5739: div.LC_grade_show_problem,
                   5740: div.LC_grade_submissions,
                   5741: div.LC_grade_message_center,
                   5742: div.LC_grade_info_links,
                   5743: div.LC_grade_assign {
                   5744:   margin: 5px;
                   5745:   width: 99%;
                   5746:   background: #FFFFFF;
                   5747: }
1.795     www      5748: 
1.597     albertel 5749: div.LC_grade_show_problem_header,
                   5750: div.LC_grade_submissions_header,
                   5751: div.LC_grade_message_center_header,
                   5752: div.LC_grade_assign_header {
1.705     tempelho 5753:   font-weight: bold;
                   5754:   font-size: large;
1.597     albertel 5755: }
1.795     www      5756: 
1.597     albertel 5757: div.LC_grade_show_problem_problem,
                   5758: div.LC_grade_submissions_body,
                   5759: div.LC_grade_message_center_body,
                   5760: div.LC_grade_assign_body {
                   5761:   border: 1px solid black;
                   5762:   width: 99%;
                   5763:   background: #FFFFFF;
                   5764: }
1.795     www      5765: 
1.598     albertel 5766: span.LC_grade_check_note {
1.705     tempelho 5767:   font-weight: normal;
                   5768:   font-size: medium;
1.598     albertel 5769:   display: inline;
                   5770:   position: absolute;
                   5771:   right: 1em;
                   5772: }
1.597     albertel 5773: 
1.613     albertel 5774: table.LC_scantron_action {
                   5775:   width: 100%;
                   5776: }
1.795     www      5777: 
1.613     albertel 5778: table.LC_scantron_action tr th {
1.698     harmsja  5779:   font-weight:bold;
                   5780:   font-style:normal;
1.613     albertel 5781: }
1.795     www      5782: 
1.779     bisitz   5783: .LC_edit_problem_header,
1.614     albertel 5784: div.LC_edit_problem_footer {
1.705     tempelho 5785:   font-weight: normal;
                   5786:   font-size:  medium;
1.602     albertel 5787:   margin: 2px;
1.600     albertel 5788: }
1.795     www      5789: 
1.600     albertel 5790: div.LC_edit_problem_header,
1.602     albertel 5791: div.LC_edit_problem_header div,
1.614     albertel 5792: div.LC_edit_problem_footer,
                   5793: div.LC_edit_problem_footer div,
1.602     albertel 5794: div.LC_edit_problem_editxml_header,
                   5795: div.LC_edit_problem_editxml_header div {
1.600     albertel 5796:   margin-top: 5px;
                   5797: }
1.795     www      5798: 
1.600     albertel 5799: div.LC_edit_problem_header_title {
1.705     tempelho 5800:   font-weight: bold;
                   5801:   font-size: larger;
1.602     albertel 5802:   background: $tabbg;
                   5803:   padding: 3px;
                   5804: }
1.795     www      5805: 
1.602     albertel 5806: table.LC_edit_problem_header_title {
1.705     tempelho 5807:   font-size: larger;
                   5808:   font-weight:  bold;
1.602     albertel 5809:   width: 100%;
                   5810:   border-color: $pgbg;
                   5811:   border-style: solid;
                   5812:   border-width: $border;
1.600     albertel 5813:   background: $tabbg;
1.602     albertel 5814:   border-collapse: collapse;
1.803     bisitz   5815:   padding: 0;
1.602     albertel 5816: }
                   5817: 
                   5818: div.LC_edit_problem_discards {
                   5819:   float: left;
                   5820:   padding-bottom: 5px;
                   5821: }
1.795     www      5822: 
1.602     albertel 5823: div.LC_edit_problem_saves {
                   5824:   float: right;
                   5825:   padding-bottom: 5px;
1.600     albertel 5826: }
1.795     www      5827: 
1.679     riegler  5828: img.stift{
1.803     bisitz   5829:   border-width: 0;
                   5830:   vertical-align: middle;
1.677     riegler  5831: }
1.680     riegler  5832: 
1.681     riegler  5833: table#LC_mainmenu{
                   5834:  margin-top:10px;
                   5835:  width:80%;
                   5836: }
                   5837: 
1.680     riegler  5838: table#LC_mainmenu td.LC_mainmenu_col_fieldset{
                   5839:   vertical-align: top;
                   5840:   width: 45%;
                   5841: }
1.795     www      5842: 
1.779     bisitz   5843: .LC_mainmenu_fieldset_category {
                   5844:   color: $font;
                   5845:   background: $pgbg;
                   5846:   font-size: small;
                   5847:   font-weight: bold;
1.777     tempelho 5848: }
1.795     www      5849: 
1.716     raeburn  5850: div.LC_createcourse {
                   5851:     margin: 10px 10px 10px 10px;
                   5852: }
                   5853: 
1.693     droeschl 5854: /* ---- Remove when done ----
                   5855: # The following styles is part of the redesign of LON-CAPA and are
                   5856: # subject to change during this project.
                   5857: # Don't rely on their current functionality as they might be 
                   5858: # changed or removed.
                   5859: # --------------------------*/
                   5860: 
1.698     harmsja  5861: a:hover,
1.897     wenzelju 5862: ol.LC_primary_menu a:hover,
1.721     harmsja  5863: ol#LC_MenuBreadcrumbs a:hover,
                   5864: ol#LC_PathBreadcrumbs a:hover,
1.897     wenzelju 5865: ul#LC_secondary_menu a:hover,
1.721     harmsja  5866: .LC_FormSectionClearButton input:hover
1.795     www      5867: ul.LC_TabContent   li:hover a {
1.698     harmsja  5868: 	color:#BF2317;
1.904     droeschl 5869:     text-decoration:none;
1.693     droeschl 5870: }
                   5871: 
1.779     bisitz   5872: h1 {
1.813     bisitz   5873: 	padding: 0;
1.693     droeschl 5874: 	line-height:130%;
                   5875: }
1.698     harmsja  5876: 
1.795     www      5877: h2,h3,h4,h5,h6 {
1.803     bisitz   5878: 	margin: 5px 0 5px 0;
                   5879: 	padding: 0;
1.721     harmsja  5880: 	line-height:130%;
1.693     droeschl 5881: }
1.795     www      5882: 
                   5883: .LC_hcell {
1.698     harmsja  5884:         padding:3px 15px 3px 15px;
1.803     bisitz   5885:         margin: 0;
1.703     harmsja  5886: 	background-color:$tabbg;
1.801     tempelho 5887: 	color:$fontmenu;
1.779     bisitz   5888: 	border-bottom:solid 1px $lg_border_color;
1.693     droeschl 5889: }
1.795     www      5890: 
1.840     bisitz   5891: .LC_Box > .LC_hcell {
1.847     tempelho 5892:     margin: 0 -10px 10px -10px;
1.835     bisitz   5893: }
                   5894: 
1.721     harmsja  5895: .LC_noBorder {
1.803     bisitz   5896:         border: 0;
1.698     harmsja  5897: }
1.693     droeschl 5898: 
1.761     tempelho 5899: .LC_Right {
                   5900:         float: right;
1.803     bisitz   5901:         margin: 0;
                   5902:         padding: 0;
1.761     tempelho 5903: }
                   5904: 
1.721     harmsja  5905: .LC_FormSectionClearButton input {
1.779     bisitz   5906:         background-color:transparent;
1.803     bisitz   5907:         border: none;
1.698     harmsja  5908:         cursor:pointer;
                   5909:         text-decoration:underline;
1.693     droeschl 5910: }
1.763     bisitz   5911: 
                   5912: .LC_help_open_topic {
                   5913:         color: #FFFFFF;
                   5914:         background-color: #EEEEFF;
                   5915:         margin: 1px;
                   5916:         padding: 4px;
                   5917:         border: 1px solid #000033;
                   5918:         white-space: nowrap;
1.783     amueller 5919: /*		vertical-align: middle; */
1.759     neumanie 5920: }
1.693     droeschl 5921: 
1.698     harmsja  5922: dl,ul,div,fieldset {
1.803     bisitz   5923: 	margin: 10px 10px 10px 0;
1.806     bisitz   5924: /*	overflow: hidden; */
1.693     droeschl 5925: }
1.795     www      5926: 
1.838     bisitz   5927: fieldset > legend {
                   5928:     font-weight: bold;
                   5929:     padding: 0 5px 0 5px;
                   5930: }
                   5931: 
1.813     bisitz   5932: #LC_nav_bar {
1.807     droeschl 5933:     float: left;
1.852     droeschl 5934:     margin: 0.2em 0 0 0;
1.807     droeschl 5935: }
                   5936: 
1.813     bisitz   5937: #LC_nav_bar em{
1.807     droeschl 5938:     font-weight: bold;
                   5939:     font-style: normal;
                   5940: }
                   5941: 
1.897     wenzelju 5942: ol.LC_primary_menu {
1.807     droeschl 5943:     float: right;
1.852     droeschl 5944:     margin: 0.2em 0 0 0;
1.807     droeschl 5945: }
                   5946: 
1.852     droeschl 5947: ol#LC_PathBreadcrumbs {
1.803     bisitz   5948: 	margin: 0;
1.693     droeschl 5949: }
                   5950: 
1.897     wenzelju 5951: ol.LC_primary_menu li {
1.693     droeschl 5952: 	display: inline;
1.803     bisitz   5953: 	padding: 5px 5px 0 10px;
1.693     droeschl 5954: 	vertical-align: top;
                   5955: }
                   5956: 
1.897     wenzelju 5957: ol.LC_primary_menu li img {
1.693     droeschl 5958: 	vertical-align: bottom;
                   5959: }
                   5960: 
1.897     wenzelju 5961: ol.LC_primary_menu a {
1.693     droeschl 5962: 	font-size: 90%;
                   5963: 	color: RGB(80, 80, 80);
                   5964: 	text-decoration: none;
                   5965: }
1.795     www      5966: 
1.897     wenzelju 5967: ul#LC_secondary_menu {
1.807     droeschl 5968:     clear: both;
1.808     droeschl 5969:     color: $fontmenu;
                   5970:     background: $tabbg;
                   5971:     list-style: none;
                   5972:     padding: 0;
                   5973:     margin: 0;
                   5974:     width: 100%;
                   5975: }
                   5976: 
1.897     wenzelju 5977: ul#LC_secondary_menu li {
1.808     droeschl 5978:     font-weight: bold;
                   5979:     line-height: 1.8em;
                   5980:     padding: 0 0.8em; 
                   5981:     border-right: 1px solid black;
                   5982:     display: inline;
                   5983:     vertical-align: middle;
1.807     droeschl 5984: }
                   5985: 
1.847     tempelho 5986: ul.LC_TabContent {
1.721     harmsja  5987: 	display:block;
1.847     tempelho 5988: 	background: $sidebg;
1.858     bisitz   5989: 	border-bottom: solid 1px $lg_border_color;
1.721     harmsja  5990: 	list-style:none;
1.870     tempelho 5991: 	margin: 0 -10px;
1.803     bisitz   5992: 	padding: 0;
1.693     droeschl 5993: }
                   5994: 
1.795     www      5995: ul.LC_TabContent li,
                   5996: ul.LC_TabContentBigger li {
1.741     harmsja  5997: 	float:left;
                   5998: }
1.795     www      5999: 
1.897     wenzelju 6000: ul#LC_secondary_menu li a {
1.808     droeschl 6001:     color: $fontmenu;
1.693     droeschl 6002: 	text-decoration: none;
                   6003: }
1.795     www      6004: 
1.721     harmsja  6005: ul.LC_TabContent {
1.847     tempelho 6006: 	min-height:1.5em;
1.721     harmsja  6007: }
1.795     www      6008: 
                   6009: ul.LC_TabContent li {
1.741     harmsja  6010: 	vertical-align:middle;
1.803     bisitz   6011: 	padding: 0 10px 0 10px;
1.745     ehlerst  6012: 	background-color:$tabbg;
                   6013: 	border-bottom:solid 1px $lg_border_color;
1.721     harmsja  6014: }
1.795     www      6015: 
1.847     tempelho 6016: ul.LC_TabContent .right {
                   6017: 	float:right;
                   6018: }
                   6019: 
1.795     www      6020: ul.LC_TabContent li a, ul.LC_TabContent li {
1.721     harmsja  6021: 	color:rgb(47,47,47);
                   6022: 	text-decoration:none;
                   6023: 	font-size:95%;
                   6024: 	font-weight:bold;
1.761     tempelho 6025: 	padding-right: 16px;
1.721     harmsja  6026: }
1.795     www      6027: 
                   6028: ul.LC_TabContent li:hover, ul.LC_TabContent li.active {
1.761     tempelho 6029:         background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
1.841     tempelho 6030: 	border-bottom:solid 2px #FFFFFF;
1.761     tempelho 6031: 	padding-right: 16px;
1.744     ehlerst  6032: }
1.795     www      6033: 
1.870     tempelho 6034: #maincoursedoc {
                   6035: 	clear:both;
                   6036: }
                   6037: 
                   6038: ul.LC_TabContentBigger {
                   6039:         display:block;
                   6040:         list-style:none;
                   6041:         padding: 0;
                   6042: }
                   6043: 
1.795     www      6044: ul.LC_TabContentBigger li {
1.870     tempelho 6045:         vertical-align:bottom;
                   6046:         height: 30px;
                   6047:         font-size:110%;
                   6048:         font-weight:bold;
                   6049:         color: #737373;
1.841     tempelho 6050: }
                   6051: 
1.870     tempelho 6052: 
                   6053: ul.LC_TabContentBigger li a {
                   6054:         background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
                   6055: 	height: 30px;
                   6056: 	line-height: 30px;
                   6057: 	text-align: center;
                   6058: 	display: block;
                   6059: 	text-decoration: none;
1.741     harmsja  6060: }
1.795     www      6061: 
1.870     tempelho 6062: ul.LC_TabContentBigger li:hover a, 
                   6063: ul.LC_TabContentBigger li.active a {
                   6064: 	background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
1.857     tempelho 6065: 	color:$font;
1.870     tempelho 6066: 	text-decoration: underline;
1.744     ehlerst  6067: }
1.795     www      6068: 
1.870     tempelho 6069: 
                   6070: ul.LC_TabContentBigger li b {
                   6071: 	background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
                   6072: 	display: block;
                   6073: 	float: left;
                   6074: 	padding: 0 30px;
                   6075: }
                   6076: 
                   6077: ul.LC_TabContentBigger li:hover b,
                   6078: ul.LC_TabContentBigger li.active b {
                   6079:         background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
                   6080:         color:$font;
                   6081: 	border-bottom: 1px solid #FFFFFF;
1.741     harmsja  6082: }
1.693     droeschl 6083: 
1.870     tempelho 6084: 
1.862     bisitz   6085: ul.LC_CourseBreadcrumbs {
                   6086:   background: $sidebg;
                   6087:   line-height: 32px;
                   6088:   padding-left: 10px;
                   6089:   margin: 0 0 10px 0;
                   6090:   list-style-position: inside;
                   6091: 
                   6092: }
                   6093: 
1.795     www      6094: ol#LC_MenuBreadcrumbs, 
1.862     bisitz   6095: ol#LC_PathBreadcrumbs {
1.693     droeschl 6096: 	padding-left: 10px;
1.819     tempelho 6097: 	margin: 0;
1.693     droeschl 6098: 	list-style-position: inside;
1.904     droeschl 6099:     /* SD working here
                   6100:     white-space: nowrap; */
1.693     droeschl 6101: }
                   6102: 
1.795     www      6103: ol#LC_MenuBreadcrumbs li, 
                   6104: ol#LC_PathBreadcrumbs li, 
1.862     bisitz   6105: ul.LC_CourseBreadcrumbs li {
1.842     droeschl 6106:     display: inline;
                   6107:     white-space: nowrap;
1.904     droeschl 6108:     /* SD working here
                   6109:     white-space: normal; */
1.693     droeschl 6110: }
                   6111: 
1.823     bisitz   6112: ol#LC_MenuBreadcrumbs li a,
1.862     bisitz   6113: ul.LC_CourseBreadcrumbs li a {
1.693     droeschl 6114: 	text-decoration: none;
                   6115: 	font-size:90%;
                   6116: }
1.795     www      6117: 
                   6118: ol#LC_PathBreadcrumbs li a {
1.698     harmsja  6119: 	text-decoration:none;
                   6120: 	font-size:100%;
                   6121: 	font-weight:bold;
1.693     droeschl 6122: }
1.795     www      6123: 
1.840     bisitz   6124: .LC_Box {
1.835     bisitz   6125:     border: solid 1px $lg_border_color;
                   6126:     padding: 0 10px 10px 10px;
1.746     neumanie 6127: }
1.795     www      6128: 
                   6129: .LC_AboutMe_Image {
1.747     neumanie 6130: 	float:left;
                   6131: 	margin-right:10px;
                   6132: }
1.795     www      6133: 
                   6134: .LC_Clear_AboutMe_Image {
1.747     neumanie 6135: 	clear:left;
                   6136: }
1.795     www      6137: 
1.721     harmsja  6138: dl.LC_ListStyleClean dt {
1.693     droeschl 6139: 	padding-right: 5px;
                   6140: 	display: table-header-group;
                   6141: }
                   6142: 
1.721     harmsja  6143: dl.LC_ListStyleClean dd {
1.693     droeschl 6144: 	display: table-row;
                   6145: }
                   6146: 
1.721     harmsja  6147: .LC_ListStyleClean,
                   6148: .LC_ListStyleSimple,
                   6149: .LC_ListStyleNormal,
1.777     tempelho 6150: .LC_ListStyle_Border,
1.795     www      6151: .LC_ListStyleSpecial {
1.693     droeschl 6152: 	/*display:block;	*/
                   6153: 	list-style-position: inside;
                   6154: 	list-style-type: none;
                   6155: 	overflow: hidden;
1.803     bisitz   6156: 	padding: 0;
1.693     droeschl 6157: }
                   6158: 
1.721     harmsja  6159: .LC_ListStyleSimple li,
                   6160: .LC_ListStyleSimple dd,
                   6161: .LC_ListStyleNormal li,
                   6162: .LC_ListStyleNormal dd,
                   6163: .LC_ListStyleSpecial li,
1.795     www      6164: .LC_ListStyleSpecial dd {
1.803     bisitz   6165: 	margin: 0;
1.693     droeschl 6166: 	padding: 5px 5px 5px 10px;
                   6167: 	clear: both;
                   6168: }
                   6169: 
1.721     harmsja  6170: .LC_ListStyleClean li,
                   6171: .LC_ListStyleClean dd {
1.803     bisitz   6172: 	padding-top: 0;
                   6173: 	padding-bottom: 0;
1.693     droeschl 6174: }
                   6175: 
1.721     harmsja  6176: .LC_ListStyleSimple dd,
1.795     www      6177: .LC_ListStyleSimple li {
1.698     harmsja  6178: 	border-bottom: solid 1px $lg_border_color;
1.693     droeschl 6179: }
                   6180: 
1.721     harmsja  6181: .LC_ListStyleSpecial li,
                   6182: .LC_ListStyleSpecial dd {
1.693     droeschl 6183: 	list-style-type: none;
                   6184: 	background-color: RGB(220, 220, 220);
                   6185: 	margin-bottom: 4px;
                   6186: }
                   6187: 
1.721     harmsja  6188: table.LC_SimpleTable {
1.698     harmsja  6189: 	margin:5px;
                   6190: 	border:solid 1px $lg_border_color;
1.795     www      6191: }
1.693     droeschl 6192: 
1.721     harmsja  6193: table.LC_SimpleTable tr {
1.803     bisitz   6194: 	padding: 0;
1.698     harmsja  6195: 	border:solid 1px $lg_border_color;
1.693     droeschl 6196: }
1.795     www      6197: 
                   6198: table.LC_SimpleTable thead {
1.698     harmsja  6199: 	 background:rgb(220,220,220);
1.693     droeschl 6200: }
                   6201: 
1.721     harmsja  6202: div.LC_columnSection {
1.693     droeschl 6203: 	display: block;
                   6204: 	clear: both;
                   6205: 	overflow: hidden;
1.803     bisitz   6206: 	margin: 0;
1.693     droeschl 6207: }
                   6208: 
1.721     harmsja  6209: div.LC_columnSection>* {
1.693     droeschl 6210: 	float: left;
1.803     bisitz   6211: 	margin: 10px 20px 10px 0;
1.747     neumanie 6212: 	overflow:hidden;
1.693     droeschl 6213: }
1.721     harmsja  6214: 
1.694     tempelho 6215: .LC_loginpage_container {
                   6216: 	text-align:left;
                   6217: 	margin : 0 auto;
1.785     tempelho 6218: 	width:90%;
1.694     tempelho 6219: 	padding: 10px;
                   6220: 	height: auto;
1.712     muellerd 6221: 	background-color:#FFFFFF;
1.694     tempelho 6222: 	border:1px solid #CCCCCC;
                   6223: }
                   6224: 
                   6225: 
                   6226: .LC_loginpage_loginContainer {
                   6227: 	float:left;
1.712     muellerd 6228: 	width: 182px;
1.785     tempelho 6229: 	padding: 2px;
1.712     muellerd 6230: 	border:1px solid #CCCCCC;
                   6231: 	background-color:$loginbg;
1.694     tempelho 6232: }
                   6233: 
1.795     www      6234: .LC_loginpage_loginContainer h2 {
1.803     bisitz   6235: 	margin-top: 0;
1.712     muellerd 6236: 	display:block;
                   6237: 	background:$bgcol;
                   6238: 	color:$textcol;
                   6239: 	padding-left:5px;
                   6240: }
1.785     tempelho 6241: 
1.694     tempelho 6242: .LC_loginpage_loginInfo {
                   6243: 	float:left;
1.785     tempelho 6244: 	width:182px;
1.694     tempelho 6245: 	border:1px solid #CCCCCC;
1.785     tempelho 6246: 	padding:2px;
1.712     muellerd 6247: }
                   6248: 
1.694     tempelho 6249: .LC_loginpage_space {
1.754     droeschl 6250: 	clear: both;
                   6251: 	margin-bottom: 20px;
1.694     tempelho 6252: 	border-bottom: 1px solid #CCCCCC;
                   6253: }
                   6254: 
1.785     tempelho 6255: .LC_loginpage_floatLeft {
                   6256: 	float: left;
                   6257: 	width: 200px;
                   6258: 	margin: 0;
                   6259: }
                   6260: 
1.795     www      6261: table em {
1.754     droeschl 6262: 	font-weight: bold;
                   6263: 	font-style: normal;
1.748     schulted 6264: }
1.795     www      6265: 
1.779     bisitz   6266: table.LC_tableBrowseRes,
1.795     www      6267: table.LC_tableOfContent {
1.769     schulted 6268:         border:none;
1.858     bisitz   6269: 	border-spacing: 1px;
1.754     droeschl 6270: 	padding: 3px;
                   6271: 	background-color: #FFFFFF;
                   6272: 	font-size: 90%;
1.753     droeschl 6273: }
1.789     droeschl 6274: 
                   6275: table.LC_tableOfContent{
                   6276:     border-collapse: collapse;
                   6277: }
                   6278: 
1.771     droeschl 6279: table.LC_tableBrowseRes a,
1.768     schulted 6280: table.LC_tableOfContent a {
1.771     droeschl 6281:         background-color: transparent;
1.753     droeschl 6282: 	text-decoration: none;
                   6283: }
                   6284: 
1.771     droeschl 6285: table.LC_tableBrowseRes tr.LC_trOdd,
1.768     schulted 6286: table.LC_tableOfContent tr.LC_trOdd{
1.754     droeschl 6287: 	background-color: #EEEEEE;
1.753     droeschl 6288: }
                   6289: 
1.795     www      6290: table.LC_tableOfContent img {
1.753     droeschl 6291: 	border: none;
                   6292: 	height: 1.3em;
                   6293: 	vertical-align: text-bottom;
                   6294: 	margin-right: 0.3em;
                   6295: }
1.757     schulted 6296: 
1.795     www      6297: a#LC_content_toolbar_firsthomework {
1.774     ehlerst  6298: 	background-image:url(/res/adm/pages/open-first-problem.gif);
                   6299: }
                   6300: 
1.795     www      6301: a#LC_content_toolbar_launchnav {
1.774     ehlerst  6302: 	background-image:url(/res/adm/pages/start-navigation.gif);
                   6303: }
                   6304: 
1.795     www      6305: a#LC_content_toolbar_closenav {
1.774     ehlerst  6306: 	background-image:url(/res/adm/pages/close-navigation.gif);
                   6307: }
                   6308: 
1.795     www      6309: a#LC_content_toolbar_everything {
1.774     ehlerst  6310: 	background-image:url(/res/adm/pages/show-all.gif);
                   6311: }
                   6312: 
1.795     www      6313: a#LC_content_toolbar_uncompleted {
1.774     ehlerst  6314: 	background-image:url(/res/adm/pages/show-incomplete-problems.gif);
                   6315: }
                   6316: 
1.795     www      6317: #LC_content_toolbar_clearbubbles {
1.774     ehlerst  6318: 	background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
                   6319: }
                   6320: 
1.795     www      6321: a#LC_content_toolbar_changefolder {
1.757     schulted 6322: 	background : url(/res/adm/pages/close-all-folders.gif) top center ;
                   6323: }
                   6324: 
1.795     www      6325: a#LC_content_toolbar_changefolder_toggled {
1.757     schulted 6326: 	background-image:url(/res/adm/pages/open-all-folders.gif);
                   6327: }
                   6328: 
1.795     www      6329: ul#LC_toolbar li a:hover {
1.757     schulted 6330: 	background-position: bottom center;
                   6331: }
                   6332: 
1.795     www      6333: ul#LC_toolbar {
1.803     bisitz   6334: 	padding: 0;
1.757     schulted 6335: 	margin: 2px;
                   6336: 	list-style:none;
                   6337: 	position:relative;
                   6338: 	background-color:white;
                   6339: }
                   6340: 
1.795     www      6341: ul#LC_toolbar li {
1.757     schulted 6342: 	border:1px solid white;
1.803     bisitz   6343: 	padding: 0;
1.757     schulted 6344: 	margin: 0;
1.795     www      6345:         float: left;
1.767     droeschl 6346: 	display:inline;
1.757     schulted 6347: 	vertical-align:middle;
1.795     www      6348: } 
1.757     schulted 6349: 
1.783     amueller 6350: 
1.795     www      6351: a.LC_toolbarItem {
1.767     droeschl 6352: 	display:block;
1.803     bisitz   6353: 	padding: 0;
                   6354: 	margin: 0;
1.757     schulted 6355: 	height: 32px;
                   6356: 	width: 32px;
1.779     bisitz   6357: 	color:white;
1.803     bisitz   6358: 	border: none;
1.757     schulted 6359: 	background-repeat:no-repeat;
                   6360: 	background-color:transparent;
                   6361: }
                   6362: 
1.843     bisitz   6363: ul.LC_funclist li {
1.782     bisitz   6364:   float: left;
                   6365:   white-space: nowrap;
                   6366:   height: 35px; /* at least as high as heighest list item */
1.803     bisitz   6367:   margin: 0 15px 15px 10px;
1.782     bisitz   6368: }
                   6369: 
1.757     schulted 6370: 
1.343     albertel 6371: END
                   6372: }
                   6373: 
1.306     albertel 6374: =pod
                   6375: 
                   6376: =item * &headtag()
                   6377: 
                   6378: Returns a uniform footer for LON-CAPA web pages.
                   6379: 
1.307     albertel 6380: Inputs: $title - optional title for the head
                   6381:         $head_extra - optional extra HTML to put inside the <head>
1.315     albertel 6382:         $args - optional arguments
1.319     albertel 6383:             force_register - if is true call registerurl so the remote is 
                   6384:                              informed
1.415     albertel 6385:             redirect       -> array ref of
                   6386:                                    1- seconds before redirect occurs
                   6387:                                    2- url to redirect to
                   6388:                                    3- whether the side effect should occur
1.315     albertel 6389:                            (side effect of setting 
                   6390:                                $env{'internal.head.redirect'} to the url 
                   6391:                                redirected too)
1.352     albertel 6392:             domain         -> force to color decorate a page for a specific
                   6393:                                domain
                   6394:             function       -> force usage of a specific rolish color scheme
                   6395:             bgcolor        -> override the default page bgcolor
1.460     albertel 6396:             no_auto_mt_title
                   6397:                            -> prevent &mt()ing the title arg
1.464     albertel 6398: 
1.306     albertel 6399: =cut
                   6400: 
                   6401: sub headtag {
1.313     albertel 6402:     my ($title,$head_extra,$args) = @_;
1.306     albertel 6403:     
1.363     albertel 6404:     my $function = $args->{'function'} || &get_users_function();
                   6405:     my $domain   = $args->{'domain'}   || &determinedomain();
                   6406:     my $bgcolor  = $args->{'bgcolor'}  || &designparm($function.'.pgbg',$domain);
1.418     albertel 6407:     my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458     albertel 6408: 		   $Apache::lonnet::perlvar{'lonVersion'},
1.531     albertel 6409: 		   #time(),
1.418     albertel 6410: 		   $env{'environment.color.timestamp'},
1.363     albertel 6411: 		   $function,$domain,$bgcolor);
                   6412: 
1.369     www      6413:     $url = '/adm/css/'.&escape($url).'.css';
1.363     albertel 6414: 
1.308     albertel 6415:     my $result =
                   6416: 	'<head>'.
1.461     albertel 6417: 	&font_settings();
1.319     albertel 6418: 
1.461     albertel 6419:     if (!$args->{'frameset'}) {
                   6420: 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
                   6421:     }
1.319     albertel 6422:     if ($args->{'force_register'}) {
                   6423: 	$result .= &Apache::lonmenu::registerurl(1);
                   6424:     }
1.436     albertel 6425:     if (!$args->{'no_nav_bar'} 
                   6426: 	&& !$args->{'only_body'}
                   6427: 	&& !$args->{'frameset'}) {
                   6428: 	$result .= &help_menu_js();
                   6429:     }
1.319     albertel 6430: 
1.314     albertel 6431:     if (ref($args->{'redirect'})) {
1.414     albertel 6432: 	my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315     albertel 6433: 	$url = &Apache::lonenc::check_encrypt($url);
1.414     albertel 6434: 	if (!$inhibit_continue) {
                   6435: 	    $env{'internal.head.redirect'} = $url;
                   6436: 	}
1.313     albertel 6437: 	$result.=<<ADDMETA
                   6438: <meta http-equiv="pragma" content="no-cache" />
1.344     albertel 6439: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313     albertel 6440: ADDMETA
                   6441:     }
1.306     albertel 6442:     if (!defined($title)) {
                   6443: 	$title = 'The LearningOnline Network with CAPA';
                   6444:     }
1.460     albertel 6445:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
                   6446:     $result .= '<title> LON-CAPA '.$title.'</title>'
1.414     albertel 6447: 	.'<link rel="stylesheet" type="text/css" href="'.$url.'" />'
                   6448: 	.$head_extra;
1.306     albertel 6449:     return $result;
                   6450: }
                   6451: 
                   6452: =pod
                   6453: 
1.340     albertel 6454: =item * &font_settings()
                   6455: 
                   6456: Returns neccessary <meta> to set the proper encoding
                   6457: 
                   6458: Inputs: none
                   6459: 
                   6460: =cut
                   6461: 
                   6462: sub font_settings {
                   6463:     my $headerstring='';
1.647     www      6464:     if (!$env{'browser.mathml'} && $env{'browser.unicode'}) {
1.340     albertel 6465: 	$headerstring.=
                   6466: 	    '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
                   6467:     }
                   6468:     return $headerstring;
                   6469: }
                   6470: 
1.341     albertel 6471: =pod
                   6472: 
                   6473: =item * &xml_begin()
                   6474: 
                   6475: Returns the needed doctype and <html>
                   6476: 
                   6477: Inputs: none
                   6478: 
                   6479: =cut
                   6480: 
                   6481: sub xml_begin {
                   6482:     my $output='';
                   6483: 
1.592     albertel 6484:     if ($env{'internal.start_page'}==1) {
                   6485: 	&Apache::lonhtmlcommon::init_htmlareafields();
                   6486:     }
1.342     albertel 6487: 
1.341     albertel 6488:     if ($env{'browser.mathml'}) {
                   6489: 	$output='<?xml version="1.0"?>'
                   6490:             #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
                   6491: #            .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
                   6492:             
                   6493: #	    .'<!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">] >'
                   6494: 	    .'<!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">'
                   6495:             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" ' 
                   6496: 	    .'xmlns="http://www.w3.org/1999/xhtml">';
                   6497:     } else {
1.849     bisitz   6498: 	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
                   6499:            .'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
1.341     albertel 6500:     }
                   6501:     return $output;
                   6502: }
1.340     albertel 6503: 
                   6504: =pod
                   6505: 
1.306     albertel 6506: =item * &endheadtag()
                   6507: 
                   6508: Returns a uniform </head> for LON-CAPA web pages.
                   6509: 
                   6510: Inputs: none
                   6511: 
                   6512: =cut
                   6513: 
                   6514: sub endheadtag {
                   6515:     return '</head>';
                   6516: }
                   6517: 
                   6518: =pod
                   6519: 
                   6520: =item * &head()
                   6521: 
                   6522: Returns a uniform complete <head>..</head> section for LON-CAPA web pages.
                   6523: 
1.648     raeburn  6524: Inputs:
                   6525: 
                   6526: =over 4
                   6527: 
                   6528: $title - optional title for the page
                   6529: 
                   6530: $head_extra - optional extra HTML to put inside the <head>
                   6531: 
                   6532: =back
1.405     albertel 6533: 
1.306     albertel 6534: =cut
                   6535: 
                   6536: sub head {
1.325     albertel 6537:     my ($title,$head_extra,$args) = @_;
                   6538:     return &headtag($title,$head_extra,$args).&endheadtag();
1.306     albertel 6539: }
                   6540: 
                   6541: =pod
                   6542: 
                   6543: =item * &start_page()
                   6544: 
                   6545: Returns a complete <html> .. <body> section for LON-CAPA web pages.
                   6546: 
1.648     raeburn  6547: Inputs:
                   6548: 
                   6549: =over 4
                   6550: 
                   6551: $title - optional title for the page
                   6552: 
                   6553: $head_extra - optional extra HTML to incude inside the <head>
                   6554: 
                   6555: $args - additional optional args supported are:
                   6556: 
                   6557: =over 8
                   6558: 
                   6559:              only_body      -> is true will set &bodytag() onlybodytag
1.317     albertel 6560:                                     arg on
1.814     bisitz   6561:              no_nav_bar     -> is true will set &bodytag() no_nav_bar arg on
1.648     raeburn  6562:              add_entries    -> additional attributes to add to the  <body>
                   6563:              domain         -> force to color decorate a page for a 
1.317     albertel 6564:                                     specific domain
1.648     raeburn  6565:              function       -> force usage of a specific rolish color
1.317     albertel 6566:                                     scheme
1.648     raeburn  6567:              redirect       -> see &headtag()
                   6568:              bgcolor        -> override the default page bg color
                   6569:              js_ready       -> return a string ready for being used in 
1.317     albertel 6570:                                     a javascript writeln
1.648     raeburn  6571:              html_encode    -> return a string ready for being used in 
1.320     albertel 6572:                                     a html attribute
1.648     raeburn  6573:              force_register -> if is true will turn on the &bodytag()
1.317     albertel 6574:                                     $forcereg arg
1.648     raeburn  6575:              frameset       -> if true will start with a <frameset>
1.330     albertel 6576:                                     rather than <body>
1.648     raeburn  6577:              skip_phases    -> hash ref of 
1.338     albertel 6578:                                     head -> skip the <html><head> generation
                   6579:                                     body -> skip all <body> generation
1.648     raeburn  6580:              no_inline_link -> if true and in remote mode, don't show the 
1.361     albertel 6581:                                     'Switch To Inline Menu' link
1.648     raeburn  6582:              no_auto_mt_title -> prevent &mt()ing the title arg
                   6583:              inherit_jsmath -> when creating popup window in a page,
                   6584:                                     should it have jsmath forced on by the
                   6585:                                     current page
1.867     kalberla 6586:              bread_crumbs ->             Array containing breadcrumbs
                   6587:              bread_crumbs_components ->  if exists show it as headline else show only the breadcrumbs
1.361     albertel 6588: 
1.648     raeburn  6589: =back
1.460     albertel 6590: 
1.648     raeburn  6591: =back
1.562     albertel 6592: 
1.306     albertel 6593: =cut
                   6594: 
                   6595: sub start_page {
1.309     albertel 6596:     my ($title,$head_extra,$args) = @_;
1.318     albertel 6597:     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.313     albertel 6598:     my %head_args;
1.352     albertel 6599:     foreach my $arg ('redirect','force_register','domain','function',
1.460     albertel 6600: 		     'bgcolor','frameset','no_nav_bar','only_body',
                   6601: 		     'no_auto_mt_title') {
1.319     albertel 6602: 	if (defined($args->{$arg})) {
1.324     raeburn  6603: 	    $head_args{$arg} = $args->{$arg};
1.319     albertel 6604: 	}
1.313     albertel 6605:     }
1.319     albertel 6606: 
1.315     albertel 6607:     $env{'internal.start_page'}++;
1.338     albertel 6608:     my $result;
                   6609:     if (! exists($args->{'skip_phases'}{'head'}) ) {
                   6610: 	$result.=
1.341     albertel 6611: 	    &xml_begin().
1.338     albertel 6612: 	    &headtag($title,$head_extra,\%head_args).&endheadtag();
                   6613:     }
                   6614:     
                   6615:     if (! exists($args->{'skip_phases'}{'body'}) ) {
                   6616: 	if ($args->{'frameset'}) {
                   6617: 	    my $attr_string = &make_attr_string($args->{'force_register'},
                   6618: 						$args->{'add_entries'});
                   6619: 	    $result .= "\n<frameset $attr_string>\n";
1.831     bisitz   6620:         } else {
                   6621:             $result .=
                   6622:                 &bodytag($title, 
                   6623:                          $args->{'function'},       $args->{'add_entries'},
                   6624:                          $args->{'only_body'},      $args->{'domain'},
                   6625:                          $args->{'force_register'}, $args->{'no_nav_bar'},
                   6626:                          $args->{'bgcolor'},        $args->{'no_inline_link'},
                   6627:                          $args);
                   6628:         }
1.330     albertel 6629:     }
1.338     albertel 6630: 
1.315     albertel 6631:     if ($args->{'js_ready'}) {
1.713     kaisler  6632: 		$result = &js_ready($result);
1.315     albertel 6633:     }
1.320     albertel 6634:     if ($args->{'html_encode'}) {
1.713     kaisler  6635: 		$result = &html_encode($result);
                   6636:     }
                   6637: 
1.813     bisitz   6638:     # Preparation for new and consistent functionlist at top of screen
                   6639:     # if ($args->{'functionlist'}) {
                   6640:     #            $result .= &build_functionlist();
                   6641:     #}
                   6642: 
                   6643:     # Don't add anything more if only_body wanted
                   6644:     return $result if $args->{'only_body'};
                   6645: 
                   6646:     #Breadcrumbs
1.758     kaisler  6647:     if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
                   6648: 		&Apache::lonhtmlcommon::clear_breadcrumbs();
                   6649: 		#if any br links exists, add them to the breadcrumbs
                   6650: 		if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {         
                   6651: 			foreach my $crumb (@{$args->{'bread_crumbs'}}){
                   6652: 				&Apache::lonhtmlcommon::add_breadcrumb($crumb);
                   6653: 			}
                   6654: 		}
                   6655: 
                   6656: 		#if bread_crumbs_component exists show it as headline else show only the breadcrumbs
                   6657: 		if(exists($args->{'bread_crumbs_component'})){
                   6658: 			$result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'});
                   6659: 		}else{
                   6660: 			$result .= &Apache::lonhtmlcommon::breadcrumbs();
                   6661: 		}
1.320     albertel 6662:     }
1.315     albertel 6663:     return $result;
1.306     albertel 6664: }
                   6665: 
1.330     albertel 6666: 
1.306     albertel 6667: =pod
                   6668: 
                   6669: =item * &head()
                   6670: 
                   6671: Returns a complete </body></html> section for LON-CAPA web pages.
                   6672: 
1.315     albertel 6673: Inputs:         $args - additional optional args supported are:
                   6674:                  js_ready     -> return a string ready for being used in 
                   6675:                                  a javascript writeln
1.320     albertel 6676:                  html_encode  -> return a string ready for being used in 
                   6677:                                  a html attribute
1.330     albertel 6678:                  frameset     -> if true will start with a <frameset>
                   6679:                                  rather than <body>
1.493     albertel 6680:                  dicsussion   -> if true will get discussion from
                   6681:                                   lonxml::xmlend
                   6682:                                  (you can pass the target and parser arguments
                   6683:                                   through optional 'target' and 'parser' args
                   6684:                                   to this routine)
1.306     albertel 6685: 
                   6686: =cut
                   6687: 
                   6688: sub end_page {
1.315     albertel 6689:     my ($args) = @_;
                   6690:     $env{'internal.end_page'}++;
1.330     albertel 6691:     my $result;
1.335     albertel 6692:     if ($args->{'discussion'}) {
                   6693: 	my ($target,$parser);
                   6694: 	if (ref($args->{'discussion'})) {
                   6695: 	    ($target,$parser) =($args->{'discussion'}{'target'},
                   6696: 				$args->{'discussion'}{'parser'});
                   6697: 	}
                   6698: 	$result .= &Apache::lonxml::xmlend($target,$parser);
                   6699:     }
                   6700: 
1.330     albertel 6701:     if ($args->{'frameset'}) {
                   6702: 	$result .= '</frameset>';
                   6703:     } else {
1.635     raeburn  6704: 	$result .= &endbodytag($args);
1.330     albertel 6705:     }
                   6706:     $result .= "\n</html>";
                   6707: 
1.315     albertel 6708:     if ($args->{'js_ready'}) {
1.317     albertel 6709: 	$result = &js_ready($result);
1.315     albertel 6710:     }
1.335     albertel 6711: 
1.320     albertel 6712:     if ($args->{'html_encode'}) {
                   6713: 	$result = &html_encode($result);
                   6714:     }
1.335     albertel 6715: 
1.315     albertel 6716:     return $result;
                   6717: }
                   6718: 
1.320     albertel 6719: sub html_encode {
                   6720:     my ($result) = @_;
                   6721: 
1.322     albertel 6722:     $result = &HTML::Entities::encode($result,'<>&"');
1.320     albertel 6723:     
                   6724:     return $result;
                   6725: }
1.317     albertel 6726: sub js_ready {
                   6727:     my ($result) = @_;
                   6728: 
1.323     albertel 6729:     $result =~ s/[\n\r]/ /xmsg;
                   6730:     $result =~ s/\\/\\\\/xmsg;
                   6731:     $result =~ s/'/\\'/xmsg;
1.372     albertel 6732:     $result =~ s{</}{<\\/}xmsg;
1.317     albertel 6733:     
                   6734:     return $result;
                   6735: }
                   6736: 
1.315     albertel 6737: sub validate_page {
                   6738:     if (  exists($env{'internal.start_page'})
1.316     albertel 6739: 	  &&     $env{'internal.start_page'} > 1) {
                   6740: 	&Apache::lonnet::logthis('start_page called multiple times '.
1.318     albertel 6741: 				 $env{'internal.start_page'}.' '.
1.316     albertel 6742: 				 $ENV{'request.filename'});
1.315     albertel 6743:     }
                   6744:     if (  exists($env{'internal.end_page'})
1.316     albertel 6745: 	  &&     $env{'internal.end_page'} > 1) {
                   6746: 	&Apache::lonnet::logthis('end_page called multiple times '.
1.318     albertel 6747: 				 $env{'internal.end_page'}.' '.
1.316     albertel 6748: 				 $env{'request.filename'});
1.315     albertel 6749:     }
                   6750:     if (     exists($env{'internal.start_page'})
                   6751: 	&& ! exists($env{'internal.end_page'})) {
1.316     albertel 6752: 	&Apache::lonnet::logthis('start_page called without end_page '.
                   6753: 				 $env{'request.filename'});
1.315     albertel 6754:     }
                   6755:     if (   ! exists($env{'internal.start_page'})
                   6756: 	&&   exists($env{'internal.end_page'})) {
1.316     albertel 6757: 	&Apache::lonnet::logthis('end_page called without start_page'.
                   6758: 				 $env{'request.filename'});
1.315     albertel 6759:     }
1.306     albertel 6760: }
1.315     albertel 6761: 
1.318     albertel 6762: sub simple_error_page {
                   6763:     my ($r,$title,$msg) = @_;
                   6764:     my $page =
                   6765: 	&Apache::loncommon::start_page($title).
                   6766: 	&mt($msg).
                   6767: 	&Apache::loncommon::end_page();
                   6768:     if (ref($r)) {
                   6769: 	$r->print($page);
1.327     albertel 6770: 	return;
1.318     albertel 6771:     }
                   6772:     return $page;
                   6773: }
1.347     albertel 6774: 
                   6775: {
1.610     albertel 6776:     my @row_count;
1.347     albertel 6777:     sub start_data_table {
1.422     albertel 6778: 	my ($add_class) = @_;
                   6779: 	my $css_class = (join(' ','LC_data_table',$add_class));
1.610     albertel 6780: 	unshift(@row_count,0);
1.422     albertel 6781: 	return '<table class="'.$css_class.'">'."\n";
1.347     albertel 6782:     }
                   6783: 
                   6784:     sub end_data_table {
1.610     albertel 6785: 	shift(@row_count);
1.389     albertel 6786: 	return '</table>'."\n";;
1.347     albertel 6787:     }
                   6788: 
                   6789:     sub start_data_table_row {
1.422     albertel 6790: 	my ($add_class) = @_;
1.610     albertel 6791: 	$row_count[0]++;
                   6792: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900     bisitz   6793: 	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
1.422     albertel 6794: 	return  '<tr class="'.$css_class.'">'."\n";;
1.347     albertel 6795:     }
1.471     banghart 6796:     
                   6797:     sub continue_data_table_row {
                   6798: 	my ($add_class) = @_;
1.610     albertel 6799: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900     bisitz   6800: 	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');;
1.471     banghart 6801: 	return  '<tr class="'.$css_class.'">'."\n";;
                   6802:     }
1.347     albertel 6803: 
                   6804:     sub end_data_table_row {
1.389     albertel 6805: 	return '</tr>'."\n";;
1.347     albertel 6806:     }
1.367     www      6807: 
1.421     albertel 6808:     sub start_data_table_empty_row {
1.707     bisitz   6809: #	$row_count[0]++;
1.421     albertel 6810: 	return  '<tr class="LC_empty_row" >'."\n";;
                   6811:     }
                   6812: 
                   6813:     sub end_data_table_empty_row {
                   6814: 	return '</tr>'."\n";;
                   6815:     }
                   6816: 
1.367     www      6817:     sub start_data_table_header_row {
1.389     albertel 6818: 	return  '<tr class="LC_header_row">'."\n";;
1.367     www      6819:     }
                   6820: 
                   6821:     sub end_data_table_header_row {
1.389     albertel 6822: 	return '</tr>'."\n";;
1.367     www      6823:     }
1.890     droeschl 6824: 
                   6825:     sub data_table_caption {
                   6826:         my $caption = shift;
                   6827:         return "<caption class=\"LC_caption\">$caption</caption>";
                   6828:     }
1.347     albertel 6829: }
                   6830: 
1.548     albertel 6831: =pod
                   6832: 
                   6833: =item * &inhibit_menu_check($arg)
                   6834: 
                   6835: Checks for a inhibitmenu state and generates output to preserve it
                   6836: 
                   6837: Inputs:         $arg - can be any of
                   6838:                      - undef - in which case the return value is a string 
                   6839:                                to add  into arguments list of a uri
                   6840:                      - 'input' - in which case the return value is a HTML
                   6841:                                  <form> <input> field of type hidden to
                   6842:                                  preserve the value
                   6843:                      - a url - in which case the return value is the url with
                   6844:                                the neccesary cgi args added to preserve the
                   6845:                                inhibitmenu state
                   6846:                      - a ref to a url - no return value, but the string is
                   6847:                                         updated to include the neccessary cgi
                   6848:                                         args to preserve the inhibitmenu state
                   6849: 
                   6850: =cut
                   6851: 
                   6852: sub inhibit_menu_check {
                   6853:     my ($arg) = @_;
                   6854:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   6855:     if ($arg eq 'input') {
                   6856: 	if ($env{'form.inhibitmenu'}) {
                   6857: 	    return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
                   6858: 	} else {
                   6859: 	    return
                   6860: 	}
                   6861:     }
                   6862:     if ($env{'form.inhibitmenu'}) {
                   6863: 	if (ref($arg)) {
                   6864: 	    $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   6865: 	} elsif ($arg eq '') {
                   6866: 	    $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
                   6867: 	} else {
                   6868: 	    $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   6869: 	}
                   6870:     }
                   6871:     if (!ref($arg)) {
                   6872: 	return $arg;
                   6873:     }
                   6874: }
                   6875: 
1.251     albertel 6876: ###############################################
1.182     matthew  6877: 
                   6878: =pod
                   6879: 
1.549     albertel 6880: =back
                   6881: 
                   6882: =head1 User Information Routines
                   6883: 
                   6884: =over 4
                   6885: 
1.405     albertel 6886: =item * &get_users_function()
1.182     matthew  6887: 
                   6888: Used by &bodytag to determine the current users primary role.
                   6889: Returns either 'student','coordinator','admin', or 'author'.
                   6890: 
                   6891: =cut
                   6892: 
                   6893: ###############################################
                   6894: sub get_users_function {
1.815     tempelho 6895:     my $function = 'norole';
1.818     tempelho 6896:     if ($env{'request.role'}=~/^(st)/) {
                   6897:         $function='student';
                   6898:     }
1.258     albertel 6899:     if ($env{'request.role'}=~/^(cc|in|ta|ep)/) {
1.182     matthew  6900:         $function='coordinator';
                   6901:     }
1.258     albertel 6902:     if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182     matthew  6903:         $function='admin';
                   6904:     }
1.826     bisitz   6905:     if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.182     matthew  6906:         ($ENV{'REQUEST_URI'}=~/^(\/priv|\~)/)) {
                   6907:         $function='author';
                   6908:     }
                   6909:     return $function;
1.54      www      6910: }
1.99      www      6911: 
                   6912: ###############################################
                   6913: 
1.233     raeburn  6914: =pod
                   6915: 
1.821     raeburn  6916: =item * &show_course()
                   6917: 
                   6918: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
                   6919: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
                   6920: 
                   6921: Inputs:
                   6922: None
                   6923: 
                   6924: Outputs:
                   6925: Scalar: 1 if 'Course' to be used, 0 otherwise.
                   6926: 
                   6927: =cut
                   6928: 
                   6929: ###############################################
                   6930: sub show_course {
                   6931:     my $course = !$env{'user.adv'};
                   6932:     if (!$env{'user.adv'}) {
                   6933:         foreach my $env (keys(%env)) {
                   6934:             next if ($env !~ m/^user\.priv\./);
                   6935:             if ($env !~ m/^user\.priv\.(?:st|cm)/) {
                   6936:                 $course = 0;
                   6937:                 last;
                   6938:             }
                   6939:         }
                   6940:     }
                   6941:     return $course;
                   6942: }
                   6943: 
                   6944: ###############################################
                   6945: 
                   6946: =pod
                   6947: 
1.542     raeburn  6948: =item * &check_user_status()
1.274     raeburn  6949: 
                   6950: Determines current status of supplied role for a
                   6951: specific user. Roles can be active, previous or future.
                   6952: 
                   6953: Inputs: 
                   6954: user's domain, user's username, course's domain,
1.375     raeburn  6955: course's number, optional section ID.
1.274     raeburn  6956: 
                   6957: Outputs:
                   6958: role status: active, previous or future. 
                   6959: 
                   6960: =cut
                   6961: 
                   6962: sub check_user_status {
1.412     raeburn  6963:     my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.274     raeburn  6964:     my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
                   6965:     my @uroles = keys %userinfo;
                   6966:     my $srchstr;
                   6967:     my $active_chk = 'none';
1.412     raeburn  6968:     my $now = time;
1.274     raeburn  6969:     if (@uroles > 0) {
1.412     raeburn  6970:         if (($role eq 'cc') || ($sec eq '') || (!defined($sec))) {
1.274     raeburn  6971:             $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
                   6972:         } else {
1.412     raeburn  6973:             $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
                   6974:         }
                   6975:         if (grep/^\Q$srchstr\E$/,@uroles) {
1.274     raeburn  6976:             my $role_end = 0;
                   6977:             my $role_start = 0;
                   6978:             $active_chk = 'active';
1.412     raeburn  6979:             if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
                   6980:                 $role_end = $1;
                   6981:                 if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
                   6982:                     $role_start = $1;
1.274     raeburn  6983:                 }
                   6984:             }
                   6985:             if ($role_start > 0) {
1.412     raeburn  6986:                 if ($now < $role_start) {
1.274     raeburn  6987:                     $active_chk = 'future';
                   6988:                 }
                   6989:             }
                   6990:             if ($role_end > 0) {
1.412     raeburn  6991:                 if ($now > $role_end) {
1.274     raeburn  6992:                     $active_chk = 'previous';
                   6993:                 }
                   6994:             }
                   6995:         }
                   6996:     }
                   6997:     return $active_chk;
                   6998: }
                   6999: 
                   7000: ###############################################
                   7001: 
                   7002: =pod
                   7003: 
1.405     albertel 7004: =item * &get_sections()
1.233     raeburn  7005: 
                   7006: Determines all the sections for a course including
                   7007: sections with students and sections containing other roles.
1.419     raeburn  7008: Incoming parameters: 
                   7009: 
                   7010: 1. domain
                   7011: 2. course number 
                   7012: 3. reference to array containing roles for which sections should 
                   7013: be gathered (optional).
                   7014: 4. reference to array containing status types for which sections 
                   7015: should be gathered (optional).
                   7016: 
                   7017: If the third argument is undefined, sections are gathered for any role. 
                   7018: If the fourth argument is undefined, sections are gathered for any status.
                   7019: Permissible values are 'active' or 'future' or 'previous'.
1.233     raeburn  7020:  
1.374     raeburn  7021: Returns section hash (keys are section IDs, values are
                   7022: number of users in each section), subject to the
1.419     raeburn  7023: optional roles filter, optional status filter 
1.233     raeburn  7024: 
                   7025: =cut
                   7026: 
                   7027: ###############################################
                   7028: sub get_sections {
1.419     raeburn  7029:     my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366     albertel 7030:     if (!defined($cdom) || !defined($cnum)) {
                   7031:         my $cid =  $env{'request.course.id'};
                   7032: 
                   7033: 	return if (!defined($cid));
                   7034: 
                   7035:         $cdom = $env{'course.'.$cid.'.domain'};
                   7036:         $cnum = $env{'course.'.$cid.'.num'};
                   7037:     }
                   7038: 
                   7039:     my %sectioncount;
1.419     raeburn  7040:     my $now = time;
1.240     albertel 7041: 
1.366     albertel 7042:     if (!defined($possible_roles) || (grep(/^st$/,@$possible_roles))) {
1.276     albertel 7043: 	my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240     albertel 7044: 	my $sec_index = &Apache::loncoursedata::CL_SECTION();
                   7045: 	my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419     raeburn  7046:         my $start_index = &Apache::loncoursedata::CL_START();
                   7047:         my $end_index = &Apache::loncoursedata::CL_END();
                   7048:         my $status;
1.366     albertel 7049: 	while (my ($student,$data) = each(%$classlist)) {
1.419     raeburn  7050: 	    my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
                   7051: 				                     $data->[$status_index],
                   7052:                                                      $data->[$start_index],
                   7053:                                                      $data->[$end_index]);
                   7054:             if ($stu_status eq 'Active') {
                   7055:                 $status = 'active';
                   7056:             } elsif ($end < $now) {
                   7057:                 $status = 'previous';
                   7058:             } elsif ($start > $now) {
                   7059:                 $status = 'future';
                   7060:             } 
                   7061: 	    if ($section ne '-1' && $section !~ /^\s*$/) {
                   7062:                 if ((!defined($possible_status)) || (($status ne '') && 
                   7063:                     (grep/^\Q$status\E$/,@{$possible_status}))) { 
                   7064: 		    $sectioncount{$section}++;
                   7065:                 }
1.240     albertel 7066: 	    }
                   7067: 	}
                   7068:     }
                   7069:     my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   7070:     foreach my $user (sort(keys(%courseroles))) {
                   7071: 	if ($user !~ /^(\w{2})/) { next; }
                   7072: 	my ($role) = ($user =~ /^(\w{2})/);
                   7073: 	if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419     raeburn  7074: 	my ($section,$status);
1.240     albertel 7075: 	if ($role eq 'cr' &&
                   7076: 	    $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
                   7077: 	    $section=$1;
                   7078: 	}
                   7079: 	if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
                   7080: 	if (!defined($section) || $section eq '-1') { next; }
1.419     raeburn  7081:         my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
                   7082:         if ($end == -1 && $start == -1) {
                   7083:             next; #deleted role
                   7084:         }
                   7085:         if (!defined($possible_status)) { 
                   7086:             $sectioncount{$section}++;
                   7087:         } else {
                   7088:             if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
                   7089:                 $status = 'active';
                   7090:             } elsif ($end < $now) {
                   7091:                 $status = 'future';
                   7092:             } elsif ($start > $now) {
                   7093:                 $status = 'previous';
                   7094:             }
                   7095:             if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
                   7096:                 $sectioncount{$section}++;
                   7097:             }
                   7098:         }
1.233     raeburn  7099:     }
1.366     albertel 7100:     return %sectioncount;
1.233     raeburn  7101: }
                   7102: 
1.274     raeburn  7103: ###############################################
1.294     raeburn  7104: 
                   7105: =pod
1.405     albertel 7106: 
                   7107: =item * &get_course_users()
                   7108: 
1.275     raeburn  7109: Retrieves usernames:domains for users in the specified course
                   7110: with specific role(s), and access status. 
                   7111: 
                   7112: Incoming parameters:
1.277     albertel 7113: 1. course domain
                   7114: 2. course number
                   7115: 3. access status: users must have - either active, 
1.275     raeburn  7116: previous, future, or all.
1.277     albertel 7117: 4. reference to array of permissible roles
1.288     raeburn  7118: 5. reference to array of section restrictions (optional)
                   7119: 6. reference to results object (hash of hashes).
                   7120: 7. reference to optional userdata hash
1.609     raeburn  7121: 8. reference to optional statushash
1.630     raeburn  7122: 9. flag if privileged users (except those set to unhide in
                   7123:    course settings) should be excluded    
1.609     raeburn  7124: Keys of top level results hash are roles.
1.275     raeburn  7125: Keys of inner hashes are username:domain, with 
                   7126: values set to access type.
1.288     raeburn  7127: Optional userdata hash returns an array with arguments in the 
                   7128: same order as loncoursedata::get_classlist() for student data.
                   7129: 
1.609     raeburn  7130: Optional statushash returns
                   7131: 
1.288     raeburn  7132: Entries for end, start, section and status are blank because
                   7133: of the possibility of multiple values for non-student roles.
                   7134: 
1.275     raeburn  7135: =cut
1.405     albertel 7136: 
1.275     raeburn  7137: ###############################################
1.405     albertel 7138: 
1.275     raeburn  7139: sub get_course_users {
1.630     raeburn  7140:     my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288     raeburn  7141:     my %idx = ();
1.419     raeburn  7142:     my %seclists;
1.288     raeburn  7143: 
                   7144:     $idx{udom} = &Apache::loncoursedata::CL_SDOM();
                   7145:     $idx{uname} =  &Apache::loncoursedata::CL_SNAME();
                   7146:     $idx{end} = &Apache::loncoursedata::CL_END();
                   7147:     $idx{start} = &Apache::loncoursedata::CL_START();
                   7148:     $idx{id} = &Apache::loncoursedata::CL_ID();
                   7149:     $idx{section} = &Apache::loncoursedata::CL_SECTION();
                   7150:     $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
                   7151:     $idx{status} = &Apache::loncoursedata::CL_STATUS();
                   7152: 
1.290     albertel 7153:     if (grep(/^st$/,@{$roles})) {
1.276     albertel 7154:         my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278     raeburn  7155:         my $now = time;
1.277     albertel 7156:         foreach my $student (keys(%{$classlist})) {
1.288     raeburn  7157:             my $match = 0;
1.412     raeburn  7158:             my $secmatch = 0;
1.419     raeburn  7159:             my $section = $$classlist{$student}[$idx{section}];
1.609     raeburn  7160:             my $status = $$classlist{$student}[$idx{status}];
1.419     raeburn  7161:             if ($section eq '') {
                   7162:                 $section = 'none';
                   7163:             }
1.291     albertel 7164:             if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 7165:                 if (grep(/^all$/,@{$sections})) {
1.412     raeburn  7166:                     $secmatch = 1;
                   7167:                 } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420     albertel 7168:                     if (grep(/^none$/,@{$sections})) {
1.412     raeburn  7169:                         $secmatch = 1;
                   7170:                     }
                   7171:                 } else {  
1.419     raeburn  7172: 		    if (grep(/^\Q$section\E$/,@{$sections})) {
1.412     raeburn  7173: 		        $secmatch = 1;
                   7174:                     }
1.290     albertel 7175: 		}
1.412     raeburn  7176:                 if (!$secmatch) {
                   7177:                     next;
                   7178:                 }
1.419     raeburn  7179:             }
1.275     raeburn  7180:             if (defined($$types{'active'})) {
1.288     raeburn  7181:                 if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275     raeburn  7182:                     push(@{$$users{st}{$student}},'active');
1.288     raeburn  7183:                     $match = 1;
1.275     raeburn  7184:                 }
                   7185:             }
                   7186:             if (defined($$types{'previous'})) {
1.609     raeburn  7187:                 if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275     raeburn  7188:                     push(@{$$users{st}{$student}},'previous');
1.288     raeburn  7189:                     $match = 1;
1.275     raeburn  7190:                 }
                   7191:             }
                   7192:             if (defined($$types{'future'})) {
1.609     raeburn  7193:                 if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275     raeburn  7194:                     push(@{$$users{st}{$student}},'future');
1.288     raeburn  7195:                     $match = 1;
1.275     raeburn  7196:                 }
                   7197:             }
1.609     raeburn  7198:             if ($match) {
                   7199:                 push(@{$seclists{$student}},$section);
                   7200:                 if (ref($userdata) eq 'HASH') {
                   7201:                     $$userdata{$student} = $$classlist{$student};
                   7202:                 }
                   7203:                 if (ref($statushash) eq 'HASH') {
                   7204:                     $statushash->{$student}{'st'}{$section} = $status;
                   7205:                 }
1.288     raeburn  7206:             }
1.275     raeburn  7207:         }
                   7208:     }
1.412     raeburn  7209:     if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439     raeburn  7210:         my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   7211:         my $now = time;
1.609     raeburn  7212:         my %displaystatus = ( previous => 'Expired',
                   7213:                               active   => 'Active',
                   7214:                               future   => 'Future',
                   7215:                             );
1.630     raeburn  7216:         my %nothide;
                   7217:         if ($hidepriv) {
                   7218:             my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
                   7219:             foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
                   7220:                 if ($user !~ /:/) {
                   7221:                     $nothide{join(':',split(/[\@]/,$user))}=1;
                   7222:                 } else {
                   7223:                     $nothide{$user} = 1;
                   7224:                 }
                   7225:             }
                   7226:         }
1.439     raeburn  7227:         foreach my $person (sort(keys(%coursepersonnel))) {
1.288     raeburn  7228:             my $match = 0;
1.412     raeburn  7229:             my $secmatch = 0;
1.439     raeburn  7230:             my $status;
1.412     raeburn  7231:             my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275     raeburn  7232:             $user =~ s/:$//;
1.439     raeburn  7233:             my ($end,$start) = split(/:/,$coursepersonnel{$person});
                   7234:             if ($end == -1 || $start == -1) {
                   7235:                 next;
                   7236:             }
                   7237:             if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
                   7238:                 (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412     raeburn  7239:                 my ($uname,$udom) = split(/:/,$user);
                   7240:                 if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 7241:                     if (grep(/^all$/,@{$sections})) {
1.412     raeburn  7242:                         $secmatch = 1;
                   7243:                     } elsif ($usec eq '') {
1.420     albertel 7244:                         if (grep(/^none$/,@{$sections})) {
1.412     raeburn  7245:                             $secmatch = 1;
                   7246:                         }
                   7247:                     } else {
                   7248:                         if (grep(/^\Q$usec\E$/,@{$sections})) {
                   7249:                             $secmatch = 1;
                   7250:                         }
                   7251:                     }
                   7252:                     if (!$secmatch) {
                   7253:                         next;
                   7254:                     }
1.288     raeburn  7255:                 }
1.419     raeburn  7256:                 if ($usec eq '') {
                   7257:                     $usec = 'none';
                   7258:                 }
1.275     raeburn  7259:                 if ($uname ne '' && $udom ne '') {
1.630     raeburn  7260:                     if ($hidepriv) {
                   7261:                         if ((&Apache::lonnet::privileged($uname,$udom)) &&
                   7262:                             (!$nothide{$uname.':'.$udom})) {
                   7263:                             next;
                   7264:                         }
                   7265:                     }
1.503     raeburn  7266:                     if ($end > 0 && $end < $now) {
1.439     raeburn  7267:                         $status = 'previous';
                   7268:                     } elsif ($start > $now) {
                   7269:                         $status = 'future';
                   7270:                     } else {
                   7271:                         $status = 'active';
                   7272:                     }
1.277     albertel 7273:                     foreach my $type (keys(%{$types})) { 
1.275     raeburn  7274:                         if ($status eq $type) {
1.420     albertel 7275:                             if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419     raeburn  7276:                                 push(@{$$users{$role}{$user}},$type);
                   7277:                             }
1.288     raeburn  7278:                             $match = 1;
                   7279:                         }
                   7280:                     }
1.419     raeburn  7281:                     if (($match) && (ref($userdata) eq 'HASH')) {
                   7282:                         if (!exists($$userdata{$uname.':'.$udom})) {
                   7283: 			    &get_user_info($udom,$uname,\%idx,$userdata);
                   7284:                         }
1.420     albertel 7285:                         if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419     raeburn  7286:                             push(@{$seclists{$uname.':'.$udom}},$usec);
                   7287:                         }
1.609     raeburn  7288:                         if (ref($statushash) eq 'HASH') {
                   7289:                             $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
                   7290:                         }
1.275     raeburn  7291:                     }
                   7292:                 }
                   7293:             }
                   7294:         }
1.290     albertel 7295:         if (grep(/^ow$/,@{$roles})) {
1.279     raeburn  7296:             if ((defined($cdom)) && (defined($cnum))) {
                   7297:                 my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
                   7298:                 if ( defined($csettings{'internal.courseowner'}) ) {
                   7299:                     my $owner = $csettings{'internal.courseowner'};
1.609     raeburn  7300:                     next if ($owner eq '');
                   7301:                     my ($ownername,$ownerdom);
                   7302:                     if ($owner =~ /^([^:]+):([^:]+)$/) {
                   7303:                         $ownername = $1;
                   7304:                         $ownerdom = $2;
                   7305:                     } else {
                   7306:                         $ownername = $owner;
                   7307:                         $ownerdom = $cdom;
                   7308:                         $owner = $ownername.':'.$ownerdom;
1.439     raeburn  7309:                     }
                   7310:                     @{$$users{'ow'}{$owner}} = 'any';
1.290     albertel 7311:                     if (defined($userdata) && 
1.609     raeburn  7312: 			!exists($$userdata{$owner})) {
                   7313: 			&get_user_info($ownerdom,$ownername,\%idx,$userdata);
                   7314:                         if (!grep(/^none$/,@{$seclists{$owner}})) {
                   7315:                             push(@{$seclists{$owner}},'none');
                   7316:                         }
                   7317:                         if (ref($statushash) eq 'HASH') {
                   7318:                             $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419     raeburn  7319:                         }
1.290     albertel 7320: 		    }
1.279     raeburn  7321:                 }
                   7322:             }
                   7323:         }
1.419     raeburn  7324:         foreach my $user (keys(%seclists)) {
                   7325:             @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
                   7326:             $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
                   7327:         }
1.275     raeburn  7328:     }
                   7329:     return;
                   7330: }
                   7331: 
1.288     raeburn  7332: sub get_user_info {
                   7333:     my ($udom,$uname,$idx,$userdata) = @_;
1.289     albertel 7334:     $$userdata{$uname.':'.$udom}[$$idx{fullname}] = 
                   7335: 	&plainname($uname,$udom,'lastname');
1.291     albertel 7336:     $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297     raeburn  7337:     $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609     raeburn  7338:     my %idhash =  &Apache::lonnet::idrget($udom,($uname));
                   7339:     $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname}; 
1.288     raeburn  7340:     return;
                   7341: }
1.275     raeburn  7342: 
1.472     raeburn  7343: ###############################################
                   7344: 
                   7345: =pod
                   7346: 
                   7347: =item * &get_user_quota()
                   7348: 
                   7349: Retrieves quota assigned for storage of portfolio files for a user  
                   7350: 
                   7351: Incoming parameters:
                   7352: 1. user's username
                   7353: 2. user's domain
                   7354: 
                   7355: Returns:
1.536     raeburn  7356: 1. Disk quota (in Mb) assigned to student.
                   7357: 2. (Optional) Type of setting: custom or default
                   7358:    (individually assigned or default for user's 
                   7359:    institutional status).
                   7360: 3. (Optional) - User's institutional status (e.g., faculty, staff
                   7361:    or student - types as defined in localenroll::inst_usertypes 
                   7362:    for user's domain, which determines default quota for user.
                   7363: 4. (Optional) - Default quota which would apply to the user.
1.472     raeburn  7364: 
                   7365: If a value has been stored in the user's environment, 
1.536     raeburn  7366: it will return that, otherwise it returns the maximal default
                   7367: defined for the user's instituional status(es) in the domain.
1.472     raeburn  7368: 
                   7369: =cut
                   7370: 
                   7371: ###############################################
                   7372: 
                   7373: 
                   7374: sub get_user_quota {
                   7375:     my ($uname,$udom) = @_;
1.536     raeburn  7376:     my ($quota,$quotatype,$settingstatus,$defquota);
1.472     raeburn  7377:     if (!defined($udom)) {
                   7378:         $udom = $env{'user.domain'};
                   7379:     }
                   7380:     if (!defined($uname)) {
                   7381:         $uname = $env{'user.name'};
                   7382:     }
                   7383:     if (($udom eq '' || $uname eq '') ||
                   7384:         ($udom eq 'public') && ($uname eq 'public')) {
                   7385:         $quota = 0;
1.536     raeburn  7386:         $quotatype = 'default';
                   7387:         $defquota = 0; 
1.472     raeburn  7388:     } else {
1.536     raeburn  7389:         my $inststatus;
1.472     raeburn  7390:         if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
                   7391:             $quota = $env{'environment.portfolioquota'};
1.536     raeburn  7392:             $inststatus = $env{'environment.inststatus'};
1.472     raeburn  7393:         } else {
1.536     raeburn  7394:             my %userenv = 
                   7395:                 &Apache::lonnet::get('environment',['portfolioquota',
                   7396:                                      'inststatus'],$udom,$uname);
1.472     raeburn  7397:             my ($tmp) = keys(%userenv);
                   7398:             if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   7399:                 $quota = $userenv{'portfolioquota'};
1.536     raeburn  7400:                 $inststatus = $userenv{'inststatus'};
1.472     raeburn  7401:             } else {
                   7402:                 undef(%userenv);
                   7403:             }
                   7404:         }
1.536     raeburn  7405:         ($defquota,$settingstatus) = &default_quota($udom,$inststatus);
1.472     raeburn  7406:         if ($quota eq '') {
1.536     raeburn  7407:             $quota = $defquota;
                   7408:             $quotatype = 'default';
                   7409:         } else {
                   7410:             $quotatype = 'custom';
1.472     raeburn  7411:         }
                   7412:     }
1.536     raeburn  7413:     if (wantarray) {
                   7414:         return ($quota,$quotatype,$settingstatus,$defquota);
                   7415:     } else {
                   7416:         return $quota;
                   7417:     }
1.472     raeburn  7418: }
                   7419: 
                   7420: ###############################################
                   7421: 
                   7422: =pod
                   7423: 
                   7424: =item * &default_quota()
                   7425: 
1.536     raeburn  7426: Retrieves default quota assigned for storage of user portfolio files,
                   7427: given an (optional) user's institutional status.
1.472     raeburn  7428: 
                   7429: Incoming parameters:
                   7430: 1. domain
1.536     raeburn  7431: 2. (Optional) institutional status(es).  This is a : separated list of 
                   7432:    status types (e.g., faculty, staff, student etc.)
                   7433:    which apply to the user for whom the default is being retrieved.
                   7434:    If the institutional status string in undefined, the domain
                   7435:    default quota will be returned. 
1.472     raeburn  7436: 
                   7437: Returns:
                   7438: 1. Default disk quota (in Mb) for user portfolios in the domain.
1.536     raeburn  7439: 2. (Optional) institutional type which determined the value of the
                   7440:    default quota.
1.472     raeburn  7441: 
                   7442: If a value has been stored in the domain's configuration db,
                   7443: it will return that, otherwise it returns 20 (for backwards 
                   7444: compatibility with domains which have not set up a configuration
                   7445: db file; the original statically defined portfolio quota was 20 Mb). 
                   7446: 
1.536     raeburn  7447: If the user's status includes multiple types (e.g., staff and student),
                   7448: the largest default quota which applies to the user determines the
                   7449: default quota returned.
                   7450: 
1.780     raeburn  7451: =back
                   7452: 
1.472     raeburn  7453: =cut
                   7454: 
                   7455: ###############################################
                   7456: 
                   7457: 
                   7458: sub default_quota {
1.536     raeburn  7459:     my ($udom,$inststatus) = @_;
                   7460:     my ($defquota,$settingstatus);
                   7461:     my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622     raeburn  7462:                                             ['quotas'],$udom);
                   7463:     if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536     raeburn  7464:         if ($inststatus ne '') {
1.765     raeburn  7465:             my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536     raeburn  7466:             foreach my $item (@statuses) {
1.711     raeburn  7467:                 if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') {
                   7468:                     if ($quotahash{'quotas'}{'defaultquota'}{$item} ne '') {
                   7469:                         if ($defquota eq '') {
                   7470:                             $defquota = $quotahash{'quotas'}{'defaultquota'}{$item};
                   7471:                             $settingstatus = $item;
                   7472:                         } elsif ($quotahash{'quotas'}{'defaultquota'}{$item} > $defquota) {
                   7473:                             $defquota = $quotahash{'quotas'}{'defaultquota'}{$item};
                   7474:                             $settingstatus = $item;
                   7475:                         }
                   7476:                     }
                   7477:                 } else {
                   7478:                     if ($quotahash{'quotas'}{$item} ne '') {
                   7479:                         if ($defquota eq '') {
                   7480:                             $defquota = $quotahash{'quotas'}{$item};
                   7481:                             $settingstatus = $item;
                   7482:                         } elsif ($quotahash{'quotas'}{$item} > $defquota) {
                   7483:                             $defquota = $quotahash{'quotas'}{$item};
                   7484:                             $settingstatus = $item;
                   7485:                         }
1.536     raeburn  7486:                     }
                   7487:                 }
                   7488:             }
                   7489:         }
                   7490:         if ($defquota eq '') {
1.711     raeburn  7491:             if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') {
                   7492:                 $defquota = $quotahash{'quotas'}{'defaultquota'}{'default'};
                   7493:             } else {
                   7494:                 $defquota = $quotahash{'quotas'}{'default'};
                   7495:             }
1.536     raeburn  7496:             $settingstatus = 'default';
                   7497:         }
                   7498:     } else {
                   7499:         $settingstatus = 'default';
                   7500:         $defquota = 20;
                   7501:     }
                   7502:     if (wantarray) {
                   7503:         return ($defquota,$settingstatus);
1.472     raeburn  7504:     } else {
1.536     raeburn  7505:         return $defquota;
1.472     raeburn  7506:     }
                   7507: }
                   7508: 
1.384     raeburn  7509: sub get_secgrprole_info {
                   7510:     my ($cdom,$cnum,$needroles,$type)  = @_;
                   7511:     my %sections_count = &get_sections($cdom,$cnum);
                   7512:     my @sections =  (sort {$a <=> $b} keys(%sections_count));
                   7513:     my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
                   7514:     my @groups = sort(keys(%curr_groups));
                   7515:     my $allroles = [];
                   7516:     my $rolehash;
                   7517:     my $accesshash = {
                   7518:                      active => 'Currently has access',
                   7519:                      future => 'Will have future access',
                   7520:                      previous => 'Previously had access',
                   7521:                   };
                   7522:     if ($needroles) {
                   7523:         $rolehash = {'all' => 'all'};
1.385     albertel 7524:         my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   7525: 	if (&Apache::lonnet::error(%user_roles)) {
                   7526: 	    undef(%user_roles);
                   7527: 	}
                   7528:         foreach my $item (keys(%user_roles)) {
1.384     raeburn  7529:             my ($role)=split(/\:/,$item,2);
                   7530:             if ($role eq 'cr') { next; }
                   7531:             if ($role =~ /^cr/) {
                   7532:                 $$rolehash{$role} = (split('/',$role))[3];
                   7533:             } else {
                   7534:                 $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
                   7535:             }
                   7536:         }
                   7537:         foreach my $key (sort(keys(%{$rolehash}))) {
                   7538:             push(@{$allroles},$key);
                   7539:         }
                   7540:         push (@{$allroles},'st');
                   7541:         $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
                   7542:     }
                   7543:     return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
                   7544: }
                   7545: 
1.555     raeburn  7546: sub user_picker {
1.627     raeburn  7547:     my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype) = @_;
1.555     raeburn  7548:     my $currdom = $dom;
                   7549:     my %curr_selected = (
                   7550:                         srchin => 'dom',
1.580     raeburn  7551:                         srchby => 'lastname',
1.555     raeburn  7552:                       );
                   7553:     my $srchterm;
1.625     raeburn  7554:     if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555     raeburn  7555:         if ($srch->{'srchby'} ne '') {
                   7556:             $curr_selected{'srchby'} = $srch->{'srchby'};
                   7557:         }
                   7558:         if ($srch->{'srchin'} ne '') {
                   7559:             $curr_selected{'srchin'} = $srch->{'srchin'};
                   7560:         }
                   7561:         if ($srch->{'srchtype'} ne '') {
                   7562:             $curr_selected{'srchtype'} = $srch->{'srchtype'};
                   7563:         }
                   7564:         if ($srch->{'srchdomain'} ne '') {
                   7565:             $currdom = $srch->{'srchdomain'};
                   7566:         }
                   7567:         $srchterm = $srch->{'srchterm'};
                   7568:     }
                   7569:     my %lt=&Apache::lonlocal::texthash(
1.573     raeburn  7570:                     'usr'       => 'Search criteria',
1.563     raeburn  7571:                     'doma'      => 'Domain/institution to search',
1.558     albertel 7572:                     'uname'     => 'username',
                   7573:                     'lastname'  => 'last name',
1.555     raeburn  7574:                     'lastfirst' => 'last name, first name',
1.558     albertel 7575:                     'crs'       => 'in this course',
1.576     raeburn  7576:                     'dom'       => 'in selected LON-CAPA domain', 
1.558     albertel 7577:                     'alc'       => 'all LON-CAPA',
1.573     raeburn  7578:                     'instd'     => 'in institutional directory for selected domain',
1.558     albertel 7579:                     'exact'     => 'is',
                   7580:                     'contains'  => 'contains',
1.569     raeburn  7581:                     'begins'    => 'begins with',
1.571     raeburn  7582:                     'youm'      => "You must include some text to search for.",
                   7583:                     'thte'      => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
                   7584:                     'thet'      => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
                   7585:                     'yomc'      => "You must choose a domain when using an institutional directory search.",
                   7586:                     'ymcd'      => "You must choose a domain when using a domain search.",
                   7587:                     'whus'      => "When using searching by last,first you must include a comma as separator between last name and first name.",
                   7588:                     'whse'      => "When searching by last,first you must include at least one character in the first name.",
                   7589:                      'thfo'     => "The following need to be corrected before the search can be run:",
1.555     raeburn  7590:                                        );
1.563     raeburn  7591:     my $domform = &select_dom_form($currdom,'srchdomain',1,1);
                   7592:     my $srchinsel = ' <select name="srchin">';
1.555     raeburn  7593: 
                   7594:     my @srchins = ('crs','dom','alc','instd');
                   7595: 
                   7596:     foreach my $option (@srchins) {
                   7597:         # FIXME 'alc' option unavailable until 
                   7598:         #       loncreateuser::print_user_query_page()
                   7599:         #       has been completed.
                   7600:         next if ($option eq 'alc');
1.880     raeburn  7601:         next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));  
1.555     raeburn  7602:         next if ($option eq 'crs' && !$env{'request.course.id'});
1.563     raeburn  7603:         if ($curr_selected{'srchin'} eq $option) {
                   7604:             $srchinsel .= ' 
                   7605:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   7606:         } else {
                   7607:             $srchinsel .= '
                   7608:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   7609:         }
1.555     raeburn  7610:     }
1.563     raeburn  7611:     $srchinsel .= "\n  </select>\n";
1.555     raeburn  7612: 
                   7613:     my $srchbysel =  ' <select name="srchby">';
1.580     raeburn  7614:     foreach my $option ('lastname','lastfirst','uname') {
1.555     raeburn  7615:         if ($curr_selected{'srchby'} eq $option) {
                   7616:             $srchbysel .= '
                   7617:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   7618:         } else {
                   7619:             $srchbysel .= '
                   7620:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   7621:          }
                   7622:     }
                   7623:     $srchbysel .= "\n  </select>\n";
                   7624: 
                   7625:     my $srchtypesel = ' <select name="srchtype">';
1.580     raeburn  7626:     foreach my $option ('begins','contains','exact') {
1.555     raeburn  7627:         if ($curr_selected{'srchtype'} eq $option) {
                   7628:             $srchtypesel .= '
                   7629:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   7630:         } else {
                   7631:             $srchtypesel .= '
                   7632:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   7633:         }
                   7634:     }
                   7635:     $srchtypesel .= "\n  </select>\n";
                   7636: 
1.558     albertel 7637:     my ($newuserscript,$new_user_create);
1.556     raeburn  7638: 
                   7639:     if ($forcenewuser) {
1.576     raeburn  7640:         if (ref($srch) eq 'HASH') {
                   7641:             if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $env{'request.role.domain'}) {
1.627     raeburn  7642:                 if ($cancreate) {
                   7643:                     $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>';
                   7644:                 } else {
1.799     bisitz   7645:                     my $helplink = 'javascript:helpMenu('."'display'".')';
1.627     raeburn  7646:                     my %usertypetext = (
                   7647:                         official   => 'institutional',
                   7648:                         unofficial => 'non-institutional',
                   7649:                     );
1.799     bisitz   7650:                     $new_user_create = '<p class="LC_warning">'
                   7651:                                       .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
                   7652:                                       .' '
                   7653:                                       .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
                   7654:                                           ,'<a href="'.$helplink.'">','</a>')
                   7655:                                       .'</p><br />';
1.627     raeburn  7656:                 }
1.576     raeburn  7657:             }
                   7658:         }
                   7659: 
1.556     raeburn  7660:         $newuserscript = <<"ENDSCRIPT";
                   7661: 
1.570     raeburn  7662: function setSearch(createnew,callingForm) {
1.556     raeburn  7663:     if (createnew == 1) {
1.570     raeburn  7664:         for (var i=0; i<callingForm.srchby.length; i++) {
                   7665:             if (callingForm.srchby.options[i].value == 'uname') {
                   7666:                 callingForm.srchby.selectedIndex = i;
1.556     raeburn  7667:             }
                   7668:         }
1.570     raeburn  7669:         for (var i=0; i<callingForm.srchin.length; i++) {
                   7670:             if ( callingForm.srchin.options[i].value == 'dom') {
                   7671: 		callingForm.srchin.selectedIndex = i;
1.556     raeburn  7672:             }
                   7673:         }
1.570     raeburn  7674:         for (var i=0; i<callingForm.srchtype.length; i++) {
                   7675:             if (callingForm.srchtype.options[i].value == 'exact') {
                   7676:                 callingForm.srchtype.selectedIndex = i;
1.556     raeburn  7677:             }
                   7678:         }
1.570     raeburn  7679:         for (var i=0; i<callingForm.srchdomain.length; i++) {
                   7680:             if (callingForm.srchdomain.options[i].value == '$env{'request.role.domain'}') {
                   7681:                 callingForm.srchdomain.selectedIndex = i;
1.556     raeburn  7682:             }
                   7683:         }
                   7684:     }
                   7685: }
                   7686: ENDSCRIPT
1.558     albertel 7687: 
1.556     raeburn  7688:     }
                   7689: 
1.555     raeburn  7690:     my $output = <<"END_BLOCK";
1.556     raeburn  7691: <script type="text/javascript">
1.824     bisitz   7692: // <![CDATA[
1.570     raeburn  7693: function validateEntry(callingForm) {
1.558     albertel 7694: 
1.556     raeburn  7695:     var checkok = 1;
1.558     albertel 7696:     var srchin;
1.570     raeburn  7697:     for (var i=0; i<callingForm.srchin.length; i++) {
                   7698: 	if ( callingForm.srchin[i].checked ) {
                   7699: 	    srchin = callingForm.srchin[i].value;
1.558     albertel 7700: 	}
                   7701:     }
                   7702: 
1.570     raeburn  7703:     var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
                   7704:     var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
                   7705:     var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
                   7706:     var srchterm =  callingForm.srchterm.value;
                   7707:     var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556     raeburn  7708:     var msg = "";
                   7709: 
                   7710:     if (srchterm == "") {
                   7711:         checkok = 0;
1.571     raeburn  7712:         msg += "$lt{'youm'}\\n";
1.556     raeburn  7713:     }
                   7714: 
1.569     raeburn  7715:     if (srchtype== 'begins') {
                   7716:         if (srchterm.length < 2) {
                   7717:             checkok = 0;
1.571     raeburn  7718:             msg += "$lt{'thte'}\\n";
1.569     raeburn  7719:         }
                   7720:     }
                   7721: 
1.556     raeburn  7722:     if (srchtype== 'contains') {
                   7723:         if (srchterm.length < 3) {
                   7724:             checkok = 0;
1.571     raeburn  7725:             msg += "$lt{'thet'}\\n";
1.556     raeburn  7726:         }
                   7727:     }
                   7728:     if (srchin == 'instd') {
                   7729:         if (srchdomain == '') {
                   7730:             checkok = 0;
1.571     raeburn  7731:             msg += "$lt{'yomc'}\\n";
1.556     raeburn  7732:         }
                   7733:     }
                   7734:     if (srchin == 'dom') {
                   7735:         if (srchdomain == '') {
                   7736:             checkok = 0;
1.571     raeburn  7737:             msg += "$lt{'ymcd'}\\n";
1.556     raeburn  7738:         }
                   7739:     }
                   7740:     if (srchby == 'lastfirst') {
                   7741:         if (srchterm.indexOf(",") == -1) {
                   7742:             checkok = 0;
1.571     raeburn  7743:             msg += "$lt{'whus'}\\n";
1.556     raeburn  7744:         }
                   7745:         if (srchterm.indexOf(",") == srchterm.length -1) {
                   7746:             checkok = 0;
1.571     raeburn  7747:             msg += "$lt{'whse'}\\n";
1.556     raeburn  7748:         }
                   7749:     }
                   7750:     if (checkok == 0) {
1.571     raeburn  7751:         alert("$lt{'thfo'}\\n"+msg);
1.556     raeburn  7752:         return;
                   7753:     }
                   7754:     if (checkok == 1) {
1.570     raeburn  7755:         callingForm.submit();
1.556     raeburn  7756:     }
                   7757: }
                   7758: 
                   7759: $newuserscript
                   7760: 
1.824     bisitz   7761: // ]]>
1.556     raeburn  7762: </script>
1.558     albertel 7763: 
                   7764: $new_user_create
                   7765: 
1.555     raeburn  7766: END_BLOCK
1.558     albertel 7767: 
1.876     raeburn  7768:     $output .= &Apache::lonhtmlcommon::start_pick_box().
                   7769:                &Apache::lonhtmlcommon::row_title($lt{'doma'}).
                   7770:                $domform.
                   7771:                &Apache::lonhtmlcommon::row_closure().
                   7772:                &Apache::lonhtmlcommon::row_title($lt{'usr'}).
                   7773:                $srchbysel.
                   7774:                $srchtypesel. 
                   7775:                '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
                   7776:                $srchinsel.
                   7777:                &Apache::lonhtmlcommon::row_closure(1). 
                   7778:                &Apache::lonhtmlcommon::end_pick_box().
                   7779:                '<br />';
1.555     raeburn  7780:     return $output;
                   7781: }
                   7782: 
1.612     raeburn  7783: sub user_rule_check {
1.615     raeburn  7784:     my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.612     raeburn  7785:     my $response;
                   7786:     if (ref($usershash) eq 'HASH') {
                   7787:         foreach my $user (keys(%{$usershash})) {
                   7788:             my ($uname,$udom) = split(/:/,$user);
                   7789:             next if ($udom eq '' || $uname eq '');
1.615     raeburn  7790:             my ($id,$newuser);
1.612     raeburn  7791:             if (ref($usershash->{$user}) eq 'HASH') {
1.615     raeburn  7792:                 $newuser = $usershash->{$user}->{'newuser'};
1.612     raeburn  7793:                 $id = $usershash->{$user}->{'id'};
                   7794:             }
                   7795:             my $inst_response;
                   7796:             if (ref($checks) eq 'HASH') {
                   7797:                 if (defined($checks->{'username'})) {
1.615     raeburn  7798:                     ($inst_response,%{$inst_results->{$user}}) = 
1.612     raeburn  7799:                         &Apache::lonnet::get_instuser($udom,$uname);
                   7800:                 } elsif (defined($checks->{'id'})) {
1.615     raeburn  7801:                     ($inst_response,%{$inst_results->{$user}}) =
1.612     raeburn  7802:                         &Apache::lonnet::get_instuser($udom,undef,$id);
                   7803:                 }
1.615     raeburn  7804:             } else {
                   7805:                 ($inst_response,%{$inst_results->{$user}}) =
                   7806:                     &Apache::lonnet::get_instuser($udom,$uname);
                   7807:                 return;
1.612     raeburn  7808:             }
1.615     raeburn  7809:             if (!$got_rules->{$udom}) {
1.612     raeburn  7810:                 my %domconfig = &Apache::lonnet::get_dom('configuration',
                   7811:                                                   ['usercreation'],$udom);
                   7812:                 if (ref($domconfig{'usercreation'}) eq 'HASH') {
1.615     raeburn  7813:                     foreach my $item ('username','id') {
1.612     raeburn  7814:                         if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
                   7815:                             $$curr_rules{$udom}{$item} = 
                   7816:                                 $domconfig{'usercreation'}{$item.'_rule'};
1.585     raeburn  7817:                         }
                   7818:                     }
                   7819:                 }
1.615     raeburn  7820:                 $got_rules->{$udom} = 1;  
1.585     raeburn  7821:             }
1.612     raeburn  7822:             foreach my $item (keys(%{$checks})) {
                   7823:                 if (ref($$curr_rules{$udom}) eq 'HASH') {
                   7824:                     if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
                   7825:                         if (@{$$curr_rules{$udom}{$item}} > 0) {
                   7826:                             my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,$$curr_rules{$udom}{$item});
                   7827:                             foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
                   7828:                                 if ($rule_check{$rule}) {
                   7829:                                     $$rulematch{$user}{$item} = $rule;
                   7830:                                     if ($inst_response eq 'ok') {
1.615     raeburn  7831:                                         if (ref($inst_results) eq 'HASH') {
                   7832:                                             if (ref($inst_results->{$user}) eq 'HASH') {
                   7833:                                                 if (keys(%{$inst_results->{$user}}) == 0) {
                   7834:                                                     $$alerts{$item}{$udom}{$uname} = 1;
                   7835:                                                 }
1.612     raeburn  7836:                                             }
                   7837:                                         }
1.615     raeburn  7838:                                     }
                   7839:                                     last;
1.585     raeburn  7840:                                 }
                   7841:                             }
                   7842:                         }
                   7843:                     }
                   7844:                 }
                   7845:             }
                   7846:         }
                   7847:     }
1.612     raeburn  7848:     return;
                   7849: }
                   7850: 
                   7851: sub user_rule_formats {
                   7852:     my ($domain,$domdesc,$curr_rules,$check) = @_;
                   7853:     my %text = ( 
                   7854:                  'username' => 'Usernames',
                   7855:                  'id'       => 'IDs',
                   7856:                );
                   7857:     my $output;
                   7858:     my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
                   7859:     if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
                   7860:         if (@{$ruleorder} > 0) {
                   7861:             $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>';
                   7862:             foreach my $rule (@{$ruleorder}) {
                   7863:                 if (ref($curr_rules) eq 'ARRAY') {
                   7864:                     if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
                   7865:                         if (ref($rules->{$rule}) eq 'HASH') {
                   7866:                             $output .= '<li>'.$rules->{$rule}{'name'}.': '.
                   7867:                                         $rules->{$rule}{'desc'}.'</li>';
                   7868:                         }
                   7869:                     }
                   7870:                 }
                   7871:             }
                   7872:             $output .= '</ul>';
                   7873:         }
                   7874:     }
                   7875:     return $output;
                   7876: }
                   7877: 
                   7878: sub instrule_disallow_msg {
1.615     raeburn  7879:     my ($checkitem,$domdesc,$count,$mode) = @_;
1.612     raeburn  7880:     my $response;
                   7881:     my %text = (
                   7882:                   item   => 'username',
                   7883:                   items  => 'usernames',
                   7884:                   match  => 'matches',
                   7885:                   do     => 'does',
                   7886:                   action => 'a username',
                   7887:                   one    => 'one',
                   7888:                );
                   7889:     if ($count > 1) {
                   7890:         $text{'item'} = 'usernames';
                   7891:         $text{'match'} ='match';
                   7892:         $text{'do'} = 'do';
                   7893:         $text{'action'} = 'usernames',
                   7894:         $text{'one'} = 'ones';
                   7895:     }
                   7896:     if ($checkitem eq 'id') {
                   7897:         $text{'items'} = 'IDs';
                   7898:         $text{'item'} = 'ID';
                   7899:         $text{'action'} = 'an ID';
1.615     raeburn  7900:         if ($count > 1) {
                   7901:             $text{'item'} = 'IDs';
                   7902:             $text{'action'} = 'IDs';
                   7903:         }
1.612     raeburn  7904:     }
1.674     bisitz   7905:     $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  7906:     if ($mode eq 'upload') {
                   7907:         if ($checkitem eq 'username') {
                   7908:             $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'}.");
                   7909:         } elsif ($checkitem eq 'id') {
1.674     bisitz   7910:             $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  7911:         }
1.669     raeburn  7912:     } elsif ($mode eq 'selfcreate') {
                   7913:         if ($checkitem eq 'id') {
                   7914:             $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.");
                   7915:         }
1.615     raeburn  7916:     } else {
                   7917:         if ($checkitem eq 'username') {
                   7918:             $response .= &mt("You must choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
                   7919:         } elsif ($checkitem eq 'id') {
                   7920:             $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.");
                   7921:         }
1.612     raeburn  7922:     }
                   7923:     return $response;
1.585     raeburn  7924: }
                   7925: 
1.624     raeburn  7926: sub personal_data_fieldtitles {
                   7927:     my %fieldtitles = &Apache::lonlocal::texthash (
                   7928:                         id => 'Student/Employee ID',
                   7929:                         permanentemail => 'E-mail address',
                   7930:                         lastname => 'Last Name',
                   7931:                         firstname => 'First Name',
                   7932:                         middlename => 'Middle Name',
                   7933:                         generation => 'Generation',
                   7934:                         gen => 'Generation',
1.765     raeburn  7935:                         inststatus => 'Affiliation',
1.624     raeburn  7936:                    );
                   7937:     return %fieldtitles;
                   7938: }
                   7939: 
1.642     raeburn  7940: sub sorted_inst_types {
                   7941:     my ($dom) = @_;
                   7942:     my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
                   7943:     my $othertitle = &mt('All users');
                   7944:     if ($env{'request.course.id'}) {
1.668     raeburn  7945:         $othertitle  = &mt('Any users');
1.642     raeburn  7946:     }
                   7947:     my @types;
                   7948:     if (ref($order) eq 'ARRAY') {
                   7949:         @types = @{$order};
                   7950:     }
                   7951:     if (@types == 0) {
                   7952:         if (ref($usertypes) eq 'HASH') {
                   7953:             @types = sort(keys(%{$usertypes}));
                   7954:         }
                   7955:     }
                   7956:     if (keys(%{$usertypes}) > 0) {
                   7957:         $othertitle = &mt('Other users');
                   7958:     }
                   7959:     return ($othertitle,$usertypes,\@types);
                   7960: }
                   7961: 
1.645     raeburn  7962: sub get_institutional_codes {
                   7963:     my ($settings,$allcourses,$LC_code) = @_;
                   7964: # Get complete list of course sections to update
                   7965:     my @currsections = ();
                   7966:     my @currxlists = ();
                   7967:     my $coursecode = $$settings{'internal.coursecode'};
                   7968: 
                   7969:     if ($$settings{'internal.sectionnums'} ne '') {
                   7970:         @currsections = split(/,/,$$settings{'internal.sectionnums'});
                   7971:     }
                   7972: 
                   7973:     if ($$settings{'internal.crosslistings'} ne '') {
                   7974:         @currxlists = split(/,/,$$settings{'internal.crosslistings'});
                   7975:     }
                   7976: 
                   7977:     if (@currxlists > 0) {
                   7978:         foreach (@currxlists) {
                   7979:             if (m/^([^:]+):(\w*)$/) {
                   7980:                 unless (grep/^$1$/,@{$allcourses}) {
                   7981:                     push @{$allcourses},$1;
                   7982:                     $$LC_code{$1} = $2;
                   7983:                 }
                   7984:             }
                   7985:         }
                   7986:     }
                   7987:  
                   7988:     if (@currsections > 0) {
                   7989:         foreach (@currsections) {
                   7990:             if (m/^(\w+):(\w*)$/) {
                   7991:                 my $sec = $coursecode.$1;
                   7992:                 my $lc_sec = $2;
                   7993:                 unless (grep/^$sec$/,@{$allcourses}) {
                   7994:                     push @{$allcourses},$sec;
                   7995:                     $$LC_code{$sec} = $lc_sec;
                   7996:                 }
                   7997:             }
                   7998:         }
                   7999:     }
                   8000:     return;
                   8001: }
                   8002: 
1.112     bowersj2 8003: =pod
                   8004: 
1.780     raeburn  8005: =head1 Slot Helpers
                   8006: 
                   8007: =over 4
                   8008: 
                   8009: =item * sorted_slots()
                   8010: 
                   8011: Sorts an array of slot names in order of slot start time (earliest first). 
                   8012: 
                   8013: Inputs:
                   8014: 
                   8015: =over 4
                   8016: 
                   8017: slotsarr  - Reference to array of unsorted slot names.
                   8018: 
                   8019: slots     - Reference to hash of hash, where outer hash keys are slot names.
                   8020: 
1.549     albertel 8021: =back
                   8022: 
1.780     raeburn  8023: Returns:
                   8024: 
                   8025: =over 4
                   8026: 
                   8027: sorted   - An array of slot names sorted by the start time of the slot.
                   8028: 
                   8029: =back
                   8030: 
                   8031: =back
                   8032: 
                   8033: =cut
                   8034: 
                   8035: 
                   8036: sub sorted_slots {
                   8037:     my ($slotsarr,$slots) = @_;
                   8038:     my @sorted;
                   8039:     if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
                   8040:         @sorted =
                   8041:             sort {
                   8042:                      if (ref($slots->{$a}) && ref($slots->{$b})) {
                   8043:                          return $slots->{$a}{'starttime'} <=> $slots->{$b}{'starttime'}
                   8044:                      }
                   8045:                      if (ref($slots->{$a})) { return -1;}
                   8046:                      if (ref($slots->{$b})) { return 1;}
                   8047:                      return 0;
                   8048:                  } @{$slotsarr};
                   8049:     }
                   8050:     return @sorted;
                   8051: }
                   8052: 
                   8053: 
                   8054: =pod
                   8055: 
1.549     albertel 8056: =head1 HTTP Helpers
                   8057: 
                   8058: =over 4
                   8059: 
1.648     raeburn  8060: =item * &get_unprocessed_cgi($query,$possible_names)
1.112     bowersj2 8061: 
1.258     albertel 8062: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112     bowersj2 8063: $query.  The parameters listed in $possible_names (an array reference),
1.258     albertel 8064: will be set in $env{'form.name'} if they do not already exist.
1.112     bowersj2 8065: 
                   8066: Typically called with $ENV{'QUERY_STRING'} as the first parameter.  
                   8067: $possible_names is an ref to an array of form element names.  As an example:
                   8068: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258     albertel 8069: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112     bowersj2 8070: 
                   8071: =cut
1.1       albertel 8072: 
1.6       albertel 8073: sub get_unprocessed_cgi {
1.25      albertel 8074:   my ($query,$possible_names)= @_;
1.26      matthew  8075:   # $Apache::lonxml::debug=1;
1.356     albertel 8076:   foreach my $pair (split(/&/,$query)) {
                   8077:     my ($name, $value) = split(/=/,$pair);
1.369     www      8078:     $name = &unescape($name);
1.25      albertel 8079:     if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
                   8080:       $value =~ tr/+/ /;
                   8081:       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258     albertel 8082:       unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25      albertel 8083:     }
1.16      harris41 8084:   }
1.6       albertel 8085: }
                   8086: 
1.112     bowersj2 8087: =pod
                   8088: 
1.648     raeburn  8089: =item * &cacheheader() 
1.112     bowersj2 8090: 
                   8091: returns cache-controlling header code
                   8092: 
                   8093: =cut
                   8094: 
1.7       albertel 8095: sub cacheheader {
1.258     albertel 8096:     unless ($env{'request.method'} eq 'GET') { return ''; }
1.216     albertel 8097:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
                   8098:     my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7       albertel 8099:                 <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
                   8100:                 <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216     albertel 8101:     return $output;
1.7       albertel 8102: }
                   8103: 
1.112     bowersj2 8104: =pod
                   8105: 
1.648     raeburn  8106: =item * &no_cache($r) 
1.112     bowersj2 8107: 
                   8108: specifies header code to not have cache
                   8109: 
                   8110: =cut
                   8111: 
1.9       albertel 8112: sub no_cache {
1.216     albertel 8113:     my ($r) = @_;
                   8114:     if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258     albertel 8115: 	$env{'request.method'} ne 'GET') { return ''; }
1.216     albertel 8116:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
                   8117:     $r->no_cache(1);
                   8118:     $r->header_out("Expires" => $date);
                   8119:     $r->header_out("Pragma" => "no-cache");
1.123     www      8120: }
                   8121: 
                   8122: sub content_type {
1.181     albertel 8123:     my ($r,$type,$charset) = @_;
1.299     foxr     8124:     if ($r) {
                   8125: 	#  Note that printout.pl calls this with undef for $r.
                   8126: 	&no_cache($r);
                   8127:     }
1.258     albertel 8128:     if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181     albertel 8129:     unless ($charset) {
                   8130: 	$charset=&Apache::lonlocal::current_encoding;
                   8131:     }
                   8132:     if ($charset) { $type.='; charset='.$charset; }
                   8133:     if ($r) {
                   8134: 	$r->content_type($type);
                   8135:     } else {
                   8136: 	print("Content-type: $type\n\n");
                   8137:     }
1.9       albertel 8138: }
1.25      albertel 8139: 
1.112     bowersj2 8140: =pod
                   8141: 
1.648     raeburn  8142: =item * &add_to_env($name,$value) 
1.112     bowersj2 8143: 
1.258     albertel 8144: adds $name to the %env hash with value
1.112     bowersj2 8145: $value, if $name already exists, the entry is converted to an array
                   8146: reference and $value is added to the array.
                   8147: 
                   8148: =cut
                   8149: 
1.25      albertel 8150: sub add_to_env {
                   8151:   my ($name,$value)=@_;
1.258     albertel 8152:   if (defined($env{$name})) {
                   8153:     if (ref($env{$name})) {
1.25      albertel 8154:       #already have multiple values
1.258     albertel 8155:       push(@{ $env{$name} },$value);
1.25      albertel 8156:     } else {
                   8157:       #first time seeing multiple values, convert hash entry to an arrayref
1.258     albertel 8158:       my $first=$env{$name};
                   8159:       undef($env{$name});
                   8160:       push(@{ $env{$name} },$first,$value);
1.25      albertel 8161:     }
                   8162:   } else {
1.258     albertel 8163:     $env{$name}=$value;
1.25      albertel 8164:   }
1.31      albertel 8165: }
1.149     albertel 8166: 
                   8167: =pod
                   8168: 
1.648     raeburn  8169: =item * &get_env_multiple($name) 
1.149     albertel 8170: 
1.258     albertel 8171: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149     albertel 8172: values may be defined and end up as an array ref.
                   8173: 
                   8174: returns an array of values
                   8175: 
                   8176: =cut
                   8177: 
                   8178: sub get_env_multiple {
                   8179:     my ($name) = @_;
                   8180:     my @values;
1.258     albertel 8181:     if (defined($env{$name})) {
1.149     albertel 8182:         # exists is it an array
1.258     albertel 8183:         if (ref($env{$name})) {
                   8184:             @values=@{ $env{$name} };
1.149     albertel 8185:         } else {
1.258     albertel 8186:             $values[0]=$env{$name};
1.149     albertel 8187:         }
                   8188:     }
                   8189:     return(@values);
                   8190: }
                   8191: 
1.660     raeburn  8192: sub ask_for_embedded_content {
                   8193:     my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
                   8194:     my $upload_output = '
                   8195:    <form name="upload_embedded" action="'.$actionurl.'"
                   8196:                   method="post" enctype="multipart/form-data">';
                   8197:     $upload_output .= $state;
1.661     raeburn  8198:     $upload_output .= '<b>Upload embedded files</b>:<br />'.&start_data_table();
1.660     raeburn  8199: 
                   8200:     my $num = 0;
                   8201:     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%{$allfiles})) {
                   8202:         $upload_output .= &start_data_table_row().
                   8203:             '<td>'.$embed_file.'</td><td>';
                   8204:         if ($args->{'ignore_remote_references'}
                   8205:             && $embed_file =~ m{^\w+://}) {
                   8206:             $upload_output.='<span class="LC_warning">'.&mt("URL points to other server.").'</span>';
                   8207:         } elsif ($args->{'error_on_invalid_names'}
                   8208:             && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
                   8209: 
                   8210:             $upload_output.='<span class="LC_warning">'.&mt("Invalid characters").'</span>';
                   8211: 
                   8212:         } else {
                   8213:             $upload_output .='
1.661     raeburn  8214:            <input name="embedded_item_'.$num.'" type="file" value="" />
1.660     raeburn  8215:            <input name="embedded_orig_'.$num.'" type="hidden" value="'.&escape($embed_file).'" />';
                   8216:             my $attrib = join(':',@{$$allfiles{$embed_file}});
                   8217:             $upload_output .=
                   8218:                 "\n\t\t".
                   8219:                 '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
                   8220:                 $attrib.'" />';
                   8221:             if (exists($$codebase{$embed_file})) {
                   8222:                 $upload_output .=
                   8223:                     "\n\t\t".
                   8224:                     '<input name="codebase_'.$num.'" type="hidden" value="'.
                   8225:                     &escape($$codebase{$embed_file}).'" />';
                   8226:             }
                   8227:         }
                   8228:         $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row();
                   8229:         $num++;
                   8230:     }
                   8231:     $upload_output .= &Apache::loncommon::end_data_table().'<br />
                   8232:    <input type ="hidden" name="number_embedded_items" value="'.$num.'" />
                   8233:    <input type ="submit" value="'.&mt('Upload Listed Files').'" />
                   8234:    '.&mt('(only files for which a location has been provided will be uploaded)').'
                   8235:    </form>';
                   8236:     return $upload_output;
                   8237: }
                   8238: 
1.661     raeburn  8239: sub upload_embedded {
                   8240:     my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
                   8241:         $current_disk_usage) = @_;
                   8242:     my $output;
                   8243:     for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
                   8244:         next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
                   8245:         my $orig_uploaded_filename =
                   8246:             $env{'form.embedded_item_'.$i.'.filename'};
                   8247: 
                   8248:         $env{'form.embedded_orig_'.$i} =
                   8249:             &unescape($env{'form.embedded_orig_'.$i});
                   8250:         my ($path,$fname) =
                   8251:             ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
                   8252:         # no path, whole string is fname
                   8253:         if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
                   8254: 
                   8255:         $path = $env{'form.currentpath'}.$path;
                   8256:         $fname = &Apache::lonnet::clean_filename($fname);
                   8257:         # See if there is anything left
                   8258:         next if ($fname eq '');
                   8259: 
                   8260:         # Check if file already exists as a file or directory.
                   8261:         my ($state,$msg);
                   8262:         if ($context eq 'portfolio') {
                   8263:             my $port_path = $dirpath;
                   8264:             if ($group ne '') {
                   8265:                 $port_path = "groups/$group/$port_path";
                   8266:             }
                   8267:             ($state,$msg) = &check_for_upload($path,$fname,$group,'embedded_item_'.$i,
                   8268:                                               $dir_root,$port_path,$disk_quota,
                   8269:                                               $current_disk_usage,$uname,$udom);
                   8270:             if ($state eq 'will_exceed_quota'
                   8271:                 || $state eq 'file_locked'
                   8272:                 || $state eq 'file_exists' ) {
                   8273:                 $output .= $msg;
                   8274:                 next;
                   8275:             }
                   8276:         } elsif (($context eq 'author') || ($context eq 'testbank')) {
                   8277:             ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
                   8278:             if ($state eq 'exists') {
                   8279:                 $output .= $msg;
                   8280:                 next;
                   8281:             }
                   8282:         }
                   8283:         # Check if extension is valid
                   8284:         if (($fname =~ /\.(\w+)$/) &&
                   8285:             (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
                   8286:             $output .= &mt('Invalid file extension ([_1]) - reserved for LONCAPA use - rename the file with a different extension and re-upload. ',$1);
                   8287:             next;
                   8288:         } elsif (($fname =~ /\.(\w+)$/) &&
                   8289:                  (!defined(&Apache::loncommon::fileembstyle($1)))) {
                   8290:             $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1);
                   8291:             next;
                   8292:         } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
                   8293:             $output .= &mt('File name not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2);
                   8294:             next;
                   8295:         }
                   8296: 
                   8297:         $env{'form.embedded_item_'.$i.'.filename'}=$fname;
                   8298:         if ($context eq 'portfolio') {
                   8299:             my $result=
                   8300:                 &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
                   8301:                                                 $dirpath.$path);
                   8302:             if ($result !~ m|^/uploaded/|) {
                   8303:                 $output .= '<span class="LC_error">'
                   8304:                       .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
                   8305:                            ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
                   8306:                       .'</span><br />';
                   8307:                 next;
                   8308:             } else {
                   8309:                 $output .= '<p>'.&mt('Uploaded [_1]','<span class="LC_filename">'.
                   8310:                            $path.$fname.'</span>').'</p>';     
                   8311:             }
                   8312:         } else {
                   8313: # Save the file
                   8314:             my $target = $env{'form.embedded_item_'.$i};
                   8315:             my $fullpath = $dir_root.$dirpath.'/'.$path;
                   8316:             my $dest = $fullpath.$fname;
                   8317:             my $url = $url_root.$dirpath.'/'.$path.$fname;
                   8318:             my @parts=split(/\//,$fullpath);
                   8319:             my $count;
                   8320:             my $filepath = $dir_root;
                   8321:             for ($count=4;$count<=$#parts;$count++) {
                   8322:                 $filepath .= "/$parts[$count]";
                   8323:                 if ((-e $filepath)!=1) {
                   8324:                     mkdir($filepath,0770);
                   8325:                 }
                   8326:             }
                   8327:             my $fh;
                   8328:             if (!open($fh,'>'.$dest)) {
                   8329:                 &Apache::lonnet::logthis('Failed to create '.$dest);
                   8330:                 $output .= '<span class="LC_error">'.
                   8331:                            &mt('An error occurred while trying to upload [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
                   8332:                            '</span><br />';
                   8333:             } else {
                   8334:                 if (!print $fh $env{'form.embedded_item_'.$i}) {
                   8335:                     &Apache::lonnet::logthis('Failed to write to '.$dest);
                   8336:                     $output .= '<span class="LC_error">'.
                   8337:                               &mt('An error occurred while writing the file [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
                   8338:                               '</span><br />';
                   8339:                 } else {
                   8340:                     if ($context eq 'testbank') {
                   8341:                         $output .= &mt('Embedded file uploaded successfully:').
                   8342:                                    '&nbsp;<a href="'.$url.'">'.
                   8343:                                    $orig_uploaded_filename.'</a><br />';
                   8344:                     } else {
1.705     tempelho 8345:                         $output .= '<span class=\"LC_fontsize_large\">'.
1.661     raeburn  8346:                                    &mt('View embedded file: [_1]','<a href="'.$url.'">'.
1.705     tempelho 8347:                                    $orig_uploaded_filename.'</a>').'</span><br />';
1.661     raeburn  8348:                     }
                   8349:                 }
                   8350:                 close($fh);
                   8351:             }
                   8352:         }
                   8353:     }
                   8354:     return $output;
                   8355: }
                   8356: 
                   8357: sub check_for_existing {
                   8358:     my ($path,$fname,$element) = @_;
                   8359:     my ($state,$msg);
                   8360:     if (-d $path.'/'.$fname) {
                   8361:         $state = 'exists';
                   8362:         $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   8363:     } elsif (-e $path.'/'.$fname) {
                   8364:         $state = 'exists';
                   8365:         $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   8366:     }
                   8367:     if ($state eq 'exists') {
                   8368:         $msg = '<span class="LC_error">'.$msg.'</span><br />';
                   8369:     }
                   8370:     return ($state,$msg);
                   8371: }
                   8372: 
                   8373: sub check_for_upload {
                   8374:     my ($path,$fname,$group,$element,$portfolio_root,$port_path,
                   8375:         $disk_quota,$current_disk_usage,$uname,$udom) = @_;
                   8376:     my $filesize = (length($env{'form.'.$element})) / 1000; #express in k (1024?)
                   8377:     my $getpropath = 1;
                   8378:     my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,
                   8379:                                             $getpropath);
                   8380:     my $found_file = 0;
                   8381:     my $locked_file = 0;
                   8382:     foreach my $line (@dir_list) {
                   8383:         my ($file_name)=split(/\&/,$line,2);
                   8384:         if ($file_name eq $fname){
                   8385:             $file_name = $path.$file_name;
                   8386:             if ($group ne '') {
                   8387:                 $file_name = $group.$file_name;
                   8388:             }
                   8389:             $found_file = 1;
                   8390:             if (&Apache::lonnet::is_locked($file_name,$udom,$uname) eq 'true') {
                   8391:                 $locked_file = 1;
                   8392:             }
                   8393:         }
                   8394:     }
                   8395:     if (($current_disk_usage + $filesize) > $disk_quota){
                   8396:         my $msg = '<span class="LC_error">'.
                   8397:                 &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</span>'.
                   8398:                   '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage);
                   8399:         return ('will_exceed_quota',$msg);
                   8400:     } elsif ($found_file) {
                   8401:         if ($locked_file) {
                   8402:             my $msg = '<span class="LC_error">';
                   8403:             $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>');
                   8404:             $msg .= '</span><br />';
                   8405:             $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
                   8406:             return ('file_locked',$msg);
                   8407:         } else {
                   8408:             my $msg = '<span class="LC_error">';
                   8409:             $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'});
                   8410:             $msg .= '</span>';
                   8411:             $msg .= '<br />';
                   8412:             $msg .= &mt('To upload, rename or delete existing [_1] in [_2].','<span class="LC_filename">'.$fname.'</span>', $port_path.$env{'form.currentpath'});
                   8413:             return ('file_exists',$msg);
                   8414:         }
                   8415:     }
                   8416: }
                   8417: 
1.31      albertel 8418: 
1.41      ng       8419: =pod
1.45      matthew  8420: 
1.464     albertel 8421: =back
1.41      ng       8422: 
1.112     bowersj2 8423: =head1 CSV Upload/Handling functions
1.38      albertel 8424: 
1.41      ng       8425: =over 4
                   8426: 
1.648     raeburn  8427: =item * &upfile_store($r)
1.41      ng       8428: 
                   8429: Store uploaded file, $r should be the HTTP Request object,
1.258     albertel 8430: needs $env{'form.upfile'}
1.41      ng       8431: returns $datatoken to be put into hidden field
                   8432: 
                   8433: =cut
1.31      albertel 8434: 
                   8435: sub upfile_store {
                   8436:     my $r=shift;
1.258     albertel 8437:     $env{'form.upfile'}=~s/\r/\n/gs;
                   8438:     $env{'form.upfile'}=~s/\f/\n/gs;
                   8439:     $env{'form.upfile'}=~s/\n+/\n/gs;
                   8440:     $env{'form.upfile'}=~s/\n+$//gs;
1.31      albertel 8441: 
1.258     albertel 8442:     my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
                   8443: 	'_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31      albertel 8444:     {
1.158     raeburn  8445:         my $datafile = $r->dir_config('lonDaemons').
                   8446:                            '/tmp/'.$datatoken.'.tmp';
                   8447:         if ( open(my $fh,">$datafile") ) {
1.258     albertel 8448:             print $fh $env{'form.upfile'};
1.158     raeburn  8449:             close($fh);
                   8450:         }
1.31      albertel 8451:     }
                   8452:     return $datatoken;
                   8453: }
                   8454: 
1.56      matthew  8455: =pod
                   8456: 
1.648     raeburn  8457: =item * &load_tmp_file($r)
1.41      ng       8458: 
                   8459: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258     albertel 8460: needs $env{'form.datatoken'},
                   8461: sets $env{'form.upfile'} to the contents of the file
1.41      ng       8462: 
                   8463: =cut
1.31      albertel 8464: 
                   8465: sub load_tmp_file {
                   8466:     my $r=shift;
                   8467:     my @studentdata=();
                   8468:     {
1.158     raeburn  8469:         my $studentfile = $r->dir_config('lonDaemons').
1.258     albertel 8470:                               '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158     raeburn  8471:         if ( open(my $fh,"<$studentfile") ) {
                   8472:             @studentdata=<$fh>;
                   8473:             close($fh);
                   8474:         }
1.31      albertel 8475:     }
1.258     albertel 8476:     $env{'form.upfile'}=join('',@studentdata);
1.31      albertel 8477: }
                   8478: 
1.56      matthew  8479: =pod
                   8480: 
1.648     raeburn  8481: =item * &upfile_record_sep()
1.41      ng       8482: 
                   8483: Separate uploaded file into records
                   8484: returns array of records,
1.258     albertel 8485: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41      ng       8486: 
                   8487: =cut
1.31      albertel 8488: 
                   8489: sub upfile_record_sep {
1.258     albertel 8490:     if ($env{'form.upfiletype'} eq 'xml') {
1.31      albertel 8491:     } else {
1.248     albertel 8492: 	my @records;
1.258     albertel 8493: 	foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248     albertel 8494: 	    if ($line=~/^\s*$/) { next; }
                   8495: 	    push(@records,$line);
                   8496: 	}
                   8497: 	return @records;
1.31      albertel 8498:     }
                   8499: }
                   8500: 
1.56      matthew  8501: =pod
                   8502: 
1.648     raeburn  8503: =item * &record_sep($record)
1.41      ng       8504: 
1.258     albertel 8505: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41      ng       8506: 
                   8507: =cut
                   8508: 
1.263     www      8509: sub takeleft {
                   8510:     my $index=shift;
                   8511:     return substr('0000'.$index,-4,4);
                   8512: }
                   8513: 
1.31      albertel 8514: sub record_sep {
                   8515:     my $record=shift;
                   8516:     my %components=();
1.258     albertel 8517:     if ($env{'form.upfiletype'} eq 'xml') {
                   8518:     } elsif ($env{'form.upfiletype'} eq 'space') {
1.31      albertel 8519:         my $i=0;
1.356     albertel 8520:         foreach my $field (split(/\s+/,$record)) {
1.31      albertel 8521:             $field=~s/^(\"|\')//;
                   8522:             $field=~s/(\"|\')$//;
1.263     www      8523:             $components{&takeleft($i)}=$field;
1.31      albertel 8524:             $i++;
                   8525:         }
1.258     albertel 8526:     } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31      albertel 8527:         my $i=0;
1.356     albertel 8528:         foreach my $field (split(/\t/,$record)) {
1.31      albertel 8529:             $field=~s/^(\"|\')//;
                   8530:             $field=~s/(\"|\')$//;
1.263     www      8531:             $components{&takeleft($i)}=$field;
1.31      albertel 8532:             $i++;
                   8533:         }
                   8534:     } else {
1.561     www      8535:         my $separator=',';
1.480     banghart 8536:         if ($env{'form.upfiletype'} eq 'semisv') {
1.561     www      8537:             $separator=';';
1.480     banghart 8538:         }
1.31      albertel 8539:         my $i=0;
1.561     www      8540: # the character we are looking for to indicate the end of a quote or a record 
                   8541:         my $looking_for=$separator;
                   8542: # do not add the characters to the fields
                   8543:         my $ignore=0;
                   8544: # we just encountered a separator (or the beginning of the record)
                   8545:         my $just_found_separator=1;
                   8546: # store the field we are working on here
                   8547:         my $field='';
                   8548: # work our way through all characters in record
                   8549:         foreach my $character ($record=~/(.)/g) {
                   8550:             if ($character eq $looking_for) {
                   8551:                if ($character ne $separator) {
                   8552: # Found the end of a quote, again looking for separator
                   8553:                   $looking_for=$separator;
                   8554:                   $ignore=1;
                   8555:                } else {
                   8556: # Found a separator, store away what we got
                   8557:                   $components{&takeleft($i)}=$field;
                   8558: 	          $i++;
                   8559:                   $just_found_separator=1;
                   8560:                   $ignore=0;
                   8561:                   $field='';
                   8562:                }
                   8563:                next;
                   8564:             }
                   8565: # single or double quotation marks after a separator indicate beginning of a quote
                   8566: # we are now looking for the end of the quote and need to ignore separators
                   8567:             if ((($character eq '"') || ($character eq "'")) && ($just_found_separator))  {
                   8568:                $looking_for=$character;
                   8569:                next;
                   8570:             }
                   8571: # ignore would be true after we reached the end of a quote
                   8572:             if ($ignore) { next; }
                   8573:             if (($just_found_separator) && ($character=~/\s/)) { next; }
                   8574:             $field.=$character;
                   8575:             $just_found_separator=0; 
1.31      albertel 8576:         }
1.561     www      8577: # catch the very last entry, since we never encountered the separator
                   8578:         $components{&takeleft($i)}=$field;
1.31      albertel 8579:     }
                   8580:     return %components;
                   8581: }
                   8582: 
1.144     matthew  8583: ######################################################
                   8584: ######################################################
                   8585: 
1.56      matthew  8586: =pod
                   8587: 
1.648     raeburn  8588: =item * &upfile_select_html()
1.41      ng       8589: 
1.144     matthew  8590: Return HTML code to select a file from the users machine and specify 
                   8591: the file type.
1.41      ng       8592: 
                   8593: =cut
                   8594: 
1.144     matthew  8595: ######################################################
                   8596: ######################################################
1.31      albertel 8597: sub upfile_select_html {
1.144     matthew  8598:     my %Types = (
                   8599:                  csv   => &mt('CSV (comma separated values, spreadsheet)'),
1.480     banghart 8600:                  semisv => &mt('Semicolon separated values'),
1.144     matthew  8601:                  space => &mt('Space separated'),
                   8602:                  tab   => &mt('Tabulator separated'),
                   8603: #                 xml   => &mt('HTML/XML'),
                   8604:                  );
                   8605:     my $Str = '<input type="file" name="upfile" size="50" />'.
1.727     riegler  8606:         '<br />'.&mt('Type').': <select name="upfiletype">';
1.144     matthew  8607:     foreach my $type (sort(keys(%Types))) {
                   8608:         $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
                   8609:     }
                   8610:     $Str .= "</select>\n";
                   8611:     return $Str;
1.31      albertel 8612: }
                   8613: 
1.301     albertel 8614: sub get_samples {
                   8615:     my ($records,$toget) = @_;
                   8616:     my @samples=({});
                   8617:     my $got=0;
                   8618:     foreach my $rec (@$records) {
                   8619: 	my %temp = &record_sep($rec);
                   8620: 	if (! grep(/\S/, values(%temp))) { next; }
                   8621: 	if (%temp) {
                   8622: 	    $samples[$got]=\%temp;
                   8623: 	    $got++;
                   8624: 	    if ($got == $toget) { last; }
                   8625: 	}
                   8626:     }
                   8627:     return \@samples;
                   8628: }
                   8629: 
1.144     matthew  8630: ######################################################
                   8631: ######################################################
                   8632: 
1.56      matthew  8633: =pod
                   8634: 
1.648     raeburn  8635: =item * &csv_print_samples($r,$records)
1.41      ng       8636: 
                   8637: Prints a table of sample values from each column uploaded $r is an
                   8638: Apache Request ref, $records is an arrayref from
                   8639: &Apache::loncommon::upfile_record_sep
                   8640: 
                   8641: =cut
                   8642: 
1.144     matthew  8643: ######################################################
                   8644: ######################################################
1.31      albertel 8645: sub csv_print_samples {
                   8646:     my ($r,$records) = @_;
1.662     bisitz   8647:     my $samples = &get_samples($records,5);
1.301     albertel 8648: 
1.594     raeburn  8649:     $r->print(&mt('Samples').'<br />'.&start_data_table().
                   8650:               &start_data_table_header_row());
1.356     albertel 8651:     foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { 
1.845     bisitz   8652:         $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.594     raeburn  8653:     $r->print(&end_data_table_header_row());
1.301     albertel 8654:     foreach my $hash (@$samples) {
1.594     raeburn  8655: 	$r->print(&start_data_table_row());
1.356     albertel 8656: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31      albertel 8657: 	    $r->print('<td>');
1.356     albertel 8658: 	    if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31      albertel 8659: 	    $r->print('</td>');
                   8660: 	}
1.594     raeburn  8661: 	$r->print(&end_data_table_row());
1.31      albertel 8662:     }
1.594     raeburn  8663:     $r->print(&end_data_table().'<br />'."\n");
1.31      albertel 8664: }
                   8665: 
1.144     matthew  8666: ######################################################
                   8667: ######################################################
                   8668: 
1.56      matthew  8669: =pod
                   8670: 
1.648     raeburn  8671: =item * &csv_print_select_table($r,$records,$d)
1.41      ng       8672: 
                   8673: Prints a table to create associations between values and table columns.
1.144     matthew  8674: 
1.41      ng       8675: $r is an Apache Request ref,
                   8676: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174     matthew  8677: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41      ng       8678: 
                   8679: =cut
                   8680: 
1.144     matthew  8681: ######################################################
                   8682: ######################################################
1.31      albertel 8683: sub csv_print_select_table {
                   8684:     my ($r,$records,$d) = @_;
1.301     albertel 8685:     my $i=0;
                   8686:     my $samples = &get_samples($records,1);
1.144     matthew  8687:     $r->print(&mt('Associate columns with student attributes.')."\n".
1.594     raeburn  8688: 	      &start_data_table().&start_data_table_header_row().
1.144     matthew  8689:               '<th>'.&mt('Attribute').'</th>'.
1.594     raeburn  8690:               '<th>'.&mt('Column').'</th>'.
                   8691:               &end_data_table_header_row()."\n");
1.356     albertel 8692:     foreach my $array_ref (@$d) {
                   8693: 	my ($value,$display,$defaultcol)=@{ $array_ref };
1.729     raeburn  8694: 	$r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31      albertel 8695: 
1.875     bisitz   8696: 	$r->print('<td><select name="f'.$i.'"'.
1.32      matthew  8697: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 8698: 	$r->print('<option value="none"></option>');
1.356     albertel 8699: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
                   8700: 	    $r->print('<option value="'.$sample.'"'.
                   8701:                       ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662     bisitz   8702:                       '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31      albertel 8703: 	}
1.594     raeburn  8704: 	$r->print('</select></td>'.&end_data_table_row()."\n");
1.31      albertel 8705: 	$i++;
                   8706:     }
1.594     raeburn  8707:     $r->print(&end_data_table());
1.31      albertel 8708:     $i--;
                   8709:     return $i;
                   8710: }
1.56      matthew  8711: 
1.144     matthew  8712: ######################################################
                   8713: ######################################################
                   8714: 
1.56      matthew  8715: =pod
1.31      albertel 8716: 
1.648     raeburn  8717: =item * &csv_samples_select_table($r,$records,$d)
1.41      ng       8718: 
                   8719: Prints a table of sample values from the upload and can make associate samples to internal names.
                   8720: 
                   8721: $r is an Apache Request ref,
                   8722: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
                   8723: $d is an array of 2 element arrays (internal name, displayed name)
                   8724: 
                   8725: =cut
                   8726: 
1.144     matthew  8727: ######################################################
                   8728: ######################################################
1.31      albertel 8729: sub csv_samples_select_table {
                   8730:     my ($r,$records,$d) = @_;
                   8731:     my $i=0;
1.144     matthew  8732:     #
1.662     bisitz   8733:     my $max_samples = 5;
                   8734:     my $samples = &get_samples($records,$max_samples);
1.594     raeburn  8735:     $r->print(&start_data_table().
                   8736:               &start_data_table_header_row().'<th>'.
                   8737:               &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
                   8738:               &end_data_table_header_row());
1.301     albertel 8739: 
                   8740:     foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594     raeburn  8741: 	$r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32      matthew  8742: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.301     albertel 8743: 	foreach my $option (@$d) {
                   8744: 	    my ($value,$display,$defaultcol)=@{ $option };
1.174     matthew  8745: 	    $r->print('<option value="'.$value.'"'.
1.253     albertel 8746:                       ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174     matthew  8747:                       $display.'</option>');
1.31      albertel 8748: 	}
                   8749: 	$r->print('</select></td><td>');
1.662     bisitz   8750: 	foreach my $line (0..($max_samples-1)) {
1.301     albertel 8751: 	    if (defined($samples->[$line]{$key})) { 
                   8752: 		$r->print($samples->[$line]{$key}."<br />\n"); 
                   8753: 	    }
                   8754: 	}
1.594     raeburn  8755: 	$r->print('</td>'.&end_data_table_row());
1.31      albertel 8756: 	$i++;
                   8757:     }
1.594     raeburn  8758:     $r->print(&end_data_table());
1.31      albertel 8759:     $i--;
                   8760:     return($i);
1.115     matthew  8761: }
                   8762: 
1.144     matthew  8763: ######################################################
                   8764: ######################################################
                   8765: 
1.115     matthew  8766: =pod
                   8767: 
1.648     raeburn  8768: =item * &clean_excel_name($name)
1.115     matthew  8769: 
                   8770: Returns a replacement for $name which does not contain any illegal characters.
                   8771: 
                   8772: =cut
                   8773: 
1.144     matthew  8774: ######################################################
                   8775: ######################################################
1.115     matthew  8776: sub clean_excel_name {
                   8777:     my ($name) = @_;
                   8778:     $name =~ s/[:\*\?\/\\]//g;
                   8779:     if (length($name) > 31) {
                   8780:         $name = substr($name,0,31);
                   8781:     }
                   8782:     return $name;
1.25      albertel 8783: }
1.84      albertel 8784: 
1.85      albertel 8785: =pod
                   8786: 
1.648     raeburn  8787: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85      albertel 8788: 
                   8789: Returns either 1 or undef
                   8790: 
                   8791: 1 if the part is to be hidden, undef if it is to be shown
                   8792: 
                   8793: Arguments are:
                   8794: 
                   8795: $id the id of the part to be checked
                   8796: $symb, optional the symb of the resource to check
                   8797: $udom, optional the domain of the user to check for
                   8798: $uname, optional the username of the user to check for
                   8799: 
                   8800: =cut
1.84      albertel 8801: 
                   8802: sub check_if_partid_hidden {
                   8803:     my ($id,$symb,$udom,$uname) = @_;
1.133     albertel 8804:     my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84      albertel 8805: 					 $symb,$udom,$uname);
1.141     albertel 8806:     my $truth=1;
                   8807:     #if the string starts with !, then the list is the list to show not hide
                   8808:     if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84      albertel 8809:     my @hiddenlist=split(/,/,$hiddenparts);
                   8810:     foreach my $checkid (@hiddenlist) {
1.141     albertel 8811: 	if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84      albertel 8812:     }
1.141     albertel 8813:     return !$truth;
1.84      albertel 8814: }
1.127     matthew  8815: 
1.138     matthew  8816: 
                   8817: ############################################################
                   8818: ############################################################
                   8819: 
                   8820: =pod
                   8821: 
1.157     matthew  8822: =back 
                   8823: 
1.138     matthew  8824: =head1 cgi-bin script and graphing routines
                   8825: 
1.157     matthew  8826: =over 4
                   8827: 
1.648     raeburn  8828: =item * &get_cgi_id()
1.138     matthew  8829: 
                   8830: Inputs: none
                   8831: 
                   8832: Returns an id which can be used to pass environment variables
                   8833: to various cgi-bin scripts.  These environment variables will
                   8834: be removed from the users environment after a given time by
                   8835: the routine &Apache::lonnet::transfer_profile_to_env.
                   8836: 
                   8837: =cut
                   8838: 
                   8839: ############################################################
                   8840: ############################################################
1.152     albertel 8841: my $uniq=0;
1.136     matthew  8842: sub get_cgi_id {
1.154     albertel 8843:     $uniq=($uniq+1)%100000;
1.280     albertel 8844:     return (time.'_'.$$.'_'.$uniq);
1.136     matthew  8845: }
                   8846: 
1.127     matthew  8847: ############################################################
                   8848: ############################################################
                   8849: 
                   8850: =pod
                   8851: 
1.648     raeburn  8852: =item * &DrawBarGraph()
1.127     matthew  8853: 
1.138     matthew  8854: Facilitates the plotting of data in a (stacked) bar graph.
                   8855: Puts plot definition data into the users environment in order for 
                   8856: graph.png to plot it.  Returns an <img> tag for the plot.
                   8857: The bars on the plot are labeled '1','2',...,'n'.
                   8858: 
                   8859: Inputs:
                   8860: 
                   8861: =over 4
                   8862: 
                   8863: =item $Title: string, the title of the plot
                   8864: 
                   8865: =item $xlabel: string, text describing the X-axis of the plot
                   8866: 
                   8867: =item $ylabel: string, text describing the Y-axis of the plot
                   8868: 
                   8869: =item $Max: scalar, the maximum Y value to use in the plot
                   8870: If $Max is < any data point, the graph will not be rendered.
                   8871: 
1.140     matthew  8872: =item $colors: array ref holding the colors to be used for the data sets when
1.138     matthew  8873: they are plotted.  If undefined, default values will be used.
                   8874: 
1.178     matthew  8875: =item $labels: array ref holding the labels to use on the x-axis for the bars.
                   8876: 
1.138     matthew  8877: =item @Values: An array of array references.  Each array reference holds data
                   8878: to be plotted in a stacked bar chart.
                   8879: 
1.239     matthew  8880: =item If the final element of @Values is a hash reference the key/value
                   8881: pairs will be added to the graph definition.
                   8882: 
1.138     matthew  8883: =back
                   8884: 
                   8885: Returns:
                   8886: 
                   8887: An <img> tag which references graph.png and the appropriate identifying
                   8888: information for the plot.
                   8889: 
1.127     matthew  8890: =cut
                   8891: 
                   8892: ############################################################
                   8893: ############################################################
1.134     matthew  8894: sub DrawBarGraph {
1.178     matthew  8895:     my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134     matthew  8896:     #
                   8897:     if (! defined($colors)) {
                   8898:         $colors = ['#33ff00', 
                   8899:                   '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
                   8900:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                   8901:                   ]; 
                   8902:     }
1.228     matthew  8903:     my $extra_settings = {};
                   8904:     if (ref($Values[-1]) eq 'HASH') {
                   8905:         $extra_settings = pop(@Values);
                   8906:     }
1.127     matthew  8907:     #
1.136     matthew  8908:     my $identifier = &get_cgi_id();
                   8909:     my $id = 'cgi.'.$identifier;        
1.129     matthew  8910:     if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127     matthew  8911:         return '';
                   8912:     }
1.225     matthew  8913:     #
                   8914:     my @Labels;
                   8915:     if (defined($labels)) {
                   8916:         @Labels = @$labels;
                   8917:     } else {
                   8918:         for (my $i=0;$i<@{$Values[0]};$i++) {
                   8919:             push (@Labels,$i+1);
                   8920:         }
                   8921:     }
                   8922:     #
1.129     matthew  8923:     my $NumBars = scalar(@{$Values[0]});
1.225     matthew  8924:     if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129     matthew  8925:     my %ValuesHash;
                   8926:     my $NumSets=1;
                   8927:     foreach my $array (@Values) {
                   8928:         next if (! ref($array));
1.136     matthew  8929:         $ValuesHash{$id.'.data.'.$NumSets++} = 
1.132     matthew  8930:             join(',',@$array);
1.129     matthew  8931:     }
1.127     matthew  8932:     #
1.136     matthew  8933:     my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225     matthew  8934:     if ($NumBars < 3) {
                   8935:         $width = 120+$NumBars*32;
1.220     matthew  8936:         $xskip = 1;
1.225     matthew  8937:         $bar_width = 30;
                   8938:     } elsif ($NumBars < 5) {
                   8939:         $width = 120+$NumBars*20;
                   8940:         $xskip = 1;
                   8941:         $bar_width = 20;
1.220     matthew  8942:     } elsif ($NumBars < 10) {
1.136     matthew  8943:         $width = 120+$NumBars*15;
                   8944:         $xskip = 1;
                   8945:         $bar_width = 15;
                   8946:     } elsif ($NumBars <= 25) {
                   8947:         $width = 120+$NumBars*11;
                   8948:         $xskip = 5;
                   8949:         $bar_width = 8;
                   8950:     } elsif ($NumBars <= 50) {
                   8951:         $width = 120+$NumBars*8;
                   8952:         $xskip = 5;
                   8953:         $bar_width = 4;
                   8954:     } else {
                   8955:         $width = 120+$NumBars*8;
                   8956:         $xskip = 5;
                   8957:         $bar_width = 4;
                   8958:     }
                   8959:     #
1.137     matthew  8960:     $Max = 1 if ($Max < 1);
                   8961:     if ( int($Max) < $Max ) {
                   8962:         $Max++;
                   8963:         $Max = int($Max);
                   8964:     }
1.127     matthew  8965:     $Title  = '' if (! defined($Title));
                   8966:     $xlabel = '' if (! defined($xlabel));
                   8967:     $ylabel = '' if (! defined($ylabel));
1.369     www      8968:     $ValuesHash{$id.'.title'}    = &escape($Title);
                   8969:     $ValuesHash{$id.'.xlabel'}   = &escape($xlabel);
                   8970:     $ValuesHash{$id.'.ylabel'}   = &escape($ylabel);
1.137     matthew  8971:     $ValuesHash{$id.'.y_max_value'} = $Max;
1.136     matthew  8972:     $ValuesHash{$id.'.NumBars'}  = $NumBars;
                   8973:     $ValuesHash{$id.'.NumSets'}  = $NumSets;
                   8974:     $ValuesHash{$id.'.PlotType'} = 'bar';
                   8975:     $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   8976:     $ValuesHash{$id.'.height'}   = $height;
                   8977:     $ValuesHash{$id.'.width'}    = $width;
                   8978:     $ValuesHash{$id.'.xskip'}    = $xskip;
                   8979:     $ValuesHash{$id.'.bar_width'} = $bar_width;
                   8980:     $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127     matthew  8981:     #
1.228     matthew  8982:     # Deal with other parameters
                   8983:     while (my ($key,$value) = each(%$extra_settings)) {
                   8984:         $ValuesHash{$id.'.'.$key} = $value;
                   8985:     }
                   8986:     #
1.646     raeburn  8987:     &Apache::lonnet::appenv(\%ValuesHash);
1.137     matthew  8988:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   8989: }
                   8990: 
                   8991: ############################################################
                   8992: ############################################################
                   8993: 
                   8994: =pod
                   8995: 
1.648     raeburn  8996: =item * &DrawXYGraph()
1.137     matthew  8997: 
1.138     matthew  8998: Facilitates the plotting of data in an XY graph.
                   8999: Puts plot definition data into the users environment in order for 
                   9000: graph.png to plot it.  Returns an <img> tag for the plot.
                   9001: 
                   9002: Inputs:
                   9003: 
                   9004: =over 4
                   9005: 
                   9006: =item $Title: string, the title of the plot
                   9007: 
                   9008: =item $xlabel: string, text describing the X-axis of the plot
                   9009: 
                   9010: =item $ylabel: string, text describing the Y-axis of the plot
                   9011: 
                   9012: =item $Max: scalar, the maximum Y value to use in the plot
                   9013: If $Max is < any data point, the graph will not be rendered.
                   9014: 
                   9015: =item $colors: Array ref containing the hex color codes for the data to be 
                   9016: plotted in.  If undefined, default values will be used.
                   9017: 
                   9018: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   9019: 
                   9020: =item $Ydata: Array ref containing Array refs.  
1.185     www      9021: Each of the contained arrays will be plotted as a separate curve.
1.138     matthew  9022: 
                   9023: =item %Values: hash indicating or overriding any default values which are 
                   9024: passed to graph.png.  
                   9025: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   9026: 
                   9027: =back
                   9028: 
                   9029: Returns:
                   9030: 
                   9031: An <img> tag which references graph.png and the appropriate identifying
                   9032: information for the plot.
                   9033: 
1.137     matthew  9034: =cut
                   9035: 
                   9036: ############################################################
                   9037: ############################################################
                   9038: sub DrawXYGraph {
                   9039:     my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
                   9040:     #
                   9041:     # Create the identifier for the graph
                   9042:     my $identifier = &get_cgi_id();
                   9043:     my $id = 'cgi.'.$identifier;
                   9044:     #
                   9045:     $Title  = '' if (! defined($Title));
                   9046:     $xlabel = '' if (! defined($xlabel));
                   9047:     $ylabel = '' if (! defined($ylabel));
                   9048:     my %ValuesHash = 
                   9049:         (
1.369     www      9050:          $id.'.title'  => &escape($Title),
                   9051:          $id.'.xlabel' => &escape($xlabel),
                   9052:          $id.'.ylabel' => &escape($ylabel),
1.137     matthew  9053:          $id.'.y_max_value'=> $Max,
                   9054:          $id.'.labels'     => join(',',@$Xlabels),
                   9055:          $id.'.PlotType'   => 'XY',
                   9056:          );
                   9057:     #
                   9058:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   9059:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   9060:     }
                   9061:     #
                   9062:     if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
                   9063:         return '';
                   9064:     }
                   9065:     my $NumSets=1;
1.138     matthew  9066:     foreach my $array (@{$Ydata}){
1.137     matthew  9067:         next if (! ref($array));
                   9068:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
                   9069:     }
1.138     matthew  9070:     $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137     matthew  9071:     #
                   9072:     # Deal with other parameters
                   9073:     while (my ($key,$value) = each(%Values)) {
                   9074:         $ValuesHash{$id.'.'.$key} = $value;
1.127     matthew  9075:     }
                   9076:     #
1.646     raeburn  9077:     &Apache::lonnet::appenv(\%ValuesHash);
1.136     matthew  9078:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   9079: }
                   9080: 
                   9081: ############################################################
                   9082: ############################################################
                   9083: 
                   9084: =pod
                   9085: 
1.648     raeburn  9086: =item * &DrawXYYGraph()
1.138     matthew  9087: 
                   9088: Facilitates the plotting of data in an XY graph with two Y axes.
                   9089: Puts plot definition data into the users environment in order for 
                   9090: graph.png to plot it.  Returns an <img> tag for the plot.
                   9091: 
                   9092: Inputs:
                   9093: 
                   9094: =over 4
                   9095: 
                   9096: =item $Title: string, the title of the plot
                   9097: 
                   9098: =item $xlabel: string, text describing the X-axis of the plot
                   9099: 
                   9100: =item $ylabel: string, text describing the Y-axis of the plot
                   9101: 
                   9102: =item $colors: Array ref containing the hex color codes for the data to be 
                   9103: plotted in.  If undefined, default values will be used.
                   9104: 
                   9105: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   9106: 
                   9107: =item $Ydata1: The first data set
                   9108: 
                   9109: =item $Min1: The minimum value of the left Y-axis
                   9110: 
                   9111: =item $Max1: The maximum value of the left Y-axis
                   9112: 
                   9113: =item $Ydata2: The second data set
                   9114: 
                   9115: =item $Min2: The minimum value of the right Y-axis
                   9116: 
                   9117: =item $Max2: The maximum value of the left Y-axis
                   9118: 
                   9119: =item %Values: hash indicating or overriding any default values which are 
                   9120: passed to graph.png.  
                   9121: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   9122: 
                   9123: =back
                   9124: 
                   9125: Returns:
                   9126: 
                   9127: An <img> tag which references graph.png and the appropriate identifying
                   9128: information for the plot.
1.136     matthew  9129: 
                   9130: =cut
                   9131: 
                   9132: ############################################################
                   9133: ############################################################
1.137     matthew  9134: sub DrawXYYGraph {
                   9135:     my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
                   9136:                                         $Ydata2,$Min2,$Max2,%Values)=@_;
1.136     matthew  9137:     #
                   9138:     # Create the identifier for the graph
                   9139:     my $identifier = &get_cgi_id();
                   9140:     my $id = 'cgi.'.$identifier;
                   9141:     #
                   9142:     $Title  = '' if (! defined($Title));
                   9143:     $xlabel = '' if (! defined($xlabel));
                   9144:     $ylabel = '' if (! defined($ylabel));
                   9145:     my %ValuesHash = 
                   9146:         (
1.369     www      9147:          $id.'.title'  => &escape($Title),
                   9148:          $id.'.xlabel' => &escape($xlabel),
                   9149:          $id.'.ylabel' => &escape($ylabel),
1.136     matthew  9150:          $id.'.labels' => join(',',@$Xlabels),
                   9151:          $id.'.PlotType' => 'XY',
                   9152:          $id.'.NumSets' => 2,
1.137     matthew  9153:          $id.'.two_axes' => 1,
                   9154:          $id.'.y1_max_value' => $Max1,
                   9155:          $id.'.y1_min_value' => $Min1,
                   9156:          $id.'.y2_max_value' => $Max2,
                   9157:          $id.'.y2_min_value' => $Min2,
1.136     matthew  9158:          );
                   9159:     #
1.137     matthew  9160:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   9161:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   9162:     }
                   9163:     #
                   9164:     if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
                   9165:         ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136     matthew  9166:         return '';
                   9167:     }
                   9168:     my $NumSets=1;
1.137     matthew  9169:     foreach my $array ($Ydata1,$Ydata2){
1.136     matthew  9170:         next if (! ref($array));
                   9171:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137     matthew  9172:     }
                   9173:     #
                   9174:     # Deal with other parameters
                   9175:     while (my ($key,$value) = each(%Values)) {
                   9176:         $ValuesHash{$id.'.'.$key} = $value;
1.136     matthew  9177:     }
                   9178:     #
1.646     raeburn  9179:     &Apache::lonnet::appenv(\%ValuesHash);
1.130     albertel 9180:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139     matthew  9181: }
                   9182: 
                   9183: ############################################################
                   9184: ############################################################
                   9185: 
                   9186: =pod
                   9187: 
1.157     matthew  9188: =back 
                   9189: 
1.139     matthew  9190: =head1 Statistics helper routines?  
                   9191: 
                   9192: Bad place for them but what the hell.
                   9193: 
1.157     matthew  9194: =over 4
                   9195: 
1.648     raeburn  9196: =item * &chartlink()
1.139     matthew  9197: 
                   9198: Returns a link to the chart for a specific student.  
                   9199: 
                   9200: Inputs:
                   9201: 
                   9202: =over 4
                   9203: 
                   9204: =item $linktext: The text of the link
                   9205: 
                   9206: =item $sname: The students username
                   9207: 
                   9208: =item $sdomain: The students domain
                   9209: 
                   9210: =back
                   9211: 
1.157     matthew  9212: =back
                   9213: 
1.139     matthew  9214: =cut
                   9215: 
                   9216: ############################################################
                   9217: ############################################################
                   9218: sub chartlink {
                   9219:     my ($linktext, $sname, $sdomain) = @_;
                   9220:     my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369     www      9221:         '&amp;SelectedStudent='.&escape($sname.':'.$sdomain).
1.219     albertel 9222:         '&amp;chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139     matthew  9223:        '">'.$linktext.'</a>';
1.153     matthew  9224: }
                   9225: 
                   9226: #######################################################
                   9227: #######################################################
                   9228: 
                   9229: =pod
                   9230: 
                   9231: =head1 Course Environment Routines
1.157     matthew  9232: 
                   9233: =over 4
1.153     matthew  9234: 
1.648     raeburn  9235: =item * &restore_course_settings()
1.153     matthew  9236: 
1.648     raeburn  9237: =item * &store_course_settings()
1.153     matthew  9238: 
                   9239: Restores/Store indicated form parameters from the course environment.
                   9240: Will not overwrite existing values of the form parameters.
                   9241: 
                   9242: Inputs: 
                   9243: a scalar describing the data (e.g. 'chart', 'problem_analysis')
                   9244: 
                   9245: a hash ref describing the data to be stored.  For example:
                   9246:    
                   9247: %Save_Parameters = ('Status' => 'scalar',
                   9248:     'chartoutputmode' => 'scalar',
                   9249:     'chartoutputdata' => 'scalar',
                   9250:     'Section' => 'array',
1.373     raeburn  9251:     'Group' => 'array',
1.153     matthew  9252:     'StudentData' => 'array',
                   9253:     'Maps' => 'array');
                   9254: 
                   9255: Returns: both routines return nothing
                   9256: 
1.631     raeburn  9257: =back
                   9258: 
1.153     matthew  9259: =cut
                   9260: 
                   9261: #######################################################
                   9262: #######################################################
                   9263: sub store_course_settings {
1.496     albertel 9264:     return &store_settings($env{'request.course.id'},@_);
                   9265: }
                   9266: 
                   9267: sub store_settings {
1.153     matthew  9268:     # save to the environment
                   9269:     # appenv the same items, just to be safe
1.300     albertel 9270:     my $udom  = $env{'user.domain'};
                   9271:     my $uname = $env{'user.name'};
1.496     albertel 9272:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  9273:     my %SaveHash;
                   9274:     my %AppHash;
                   9275:     while (my ($setting,$type) = each(%$Settings)) {
1.496     albertel 9276:         my $basename = join('.','internal',$context,$prefix,$setting);
1.300     albertel 9277:         my $envname = 'environment.'.$basename;
1.258     albertel 9278:         if (exists($env{'form.'.$setting})) {
1.153     matthew  9279:             # Save this value away
                   9280:             if ($type eq 'scalar' &&
1.258     albertel 9281:                 (! exists($env{$envname}) || 
                   9282:                  $env{$envname} ne $env{'form.'.$setting})) {
                   9283:                 $SaveHash{$basename} = $env{'form.'.$setting};
                   9284:                 $AppHash{$envname}   = $env{'form.'.$setting};
1.153     matthew  9285:             } elsif ($type eq 'array') {
                   9286:                 my $stored_form;
1.258     albertel 9287:                 if (ref($env{'form.'.$setting})) {
1.153     matthew  9288:                     $stored_form = join(',',
                   9289:                                         map {
1.369     www      9290:                                             &escape($_);
1.258     albertel 9291:                                         } sort(@{$env{'form.'.$setting}}));
1.153     matthew  9292:                 } else {
                   9293:                     $stored_form = 
1.369     www      9294:                         &escape($env{'form.'.$setting});
1.153     matthew  9295:                 }
                   9296:                 # Determine if the array contents are the same.
1.258     albertel 9297:                 if ($stored_form ne $env{$envname}) {
1.153     matthew  9298:                     $SaveHash{$basename} = $stored_form;
                   9299:                     $AppHash{$envname}   = $stored_form;
                   9300:                 }
                   9301:             }
                   9302:         }
                   9303:     }
                   9304:     my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300     albertel 9305:                                           $udom,$uname);
1.153     matthew  9306:     if ($put_result !~ /^(ok|delayed)/) {
                   9307:         &Apache::lonnet::logthis('unable to save form parameters, '.
                   9308:                                  'got error:'.$put_result);
                   9309:     }
                   9310:     # Make sure these settings stick around in this session, too
1.646     raeburn  9311:     &Apache::lonnet::appenv(\%AppHash);
1.153     matthew  9312:     return;
                   9313: }
                   9314: 
                   9315: sub restore_course_settings {
1.499     albertel 9316:     return &restore_settings($env{'request.course.id'},@_);
1.496     albertel 9317: }
                   9318: 
                   9319: sub restore_settings {
                   9320:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  9321:     while (my ($setting,$type) = each(%$Settings)) {
1.258     albertel 9322:         next if (exists($env{'form.'.$setting}));
1.496     albertel 9323:         my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153     matthew  9324:             '.'.$setting;
1.258     albertel 9325:         if (exists($env{$envname})) {
1.153     matthew  9326:             if ($type eq 'scalar') {
1.258     albertel 9327:                 $env{'form.'.$setting} = $env{$envname};
1.153     matthew  9328:             } elsif ($type eq 'array') {
1.258     albertel 9329:                 $env{'form.'.$setting} = [ 
1.153     matthew  9330:                                            map { 
1.369     www      9331:                                                &unescape($_); 
1.258     albertel 9332:                                            } split(',',$env{$envname})
1.153     matthew  9333:                                            ];
                   9334:             }
                   9335:         }
                   9336:     }
1.127     matthew  9337: }
                   9338: 
1.618     raeburn  9339: #######################################################
                   9340: #######################################################
                   9341: 
                   9342: =pod
                   9343: 
                   9344: =head1 Domain E-mail Routines  
                   9345: 
                   9346: =over 4
                   9347: 
1.648     raeburn  9348: =item * &build_recipient_list()
1.618     raeburn  9349: 
1.884     raeburn  9350: Build recipient lists for five types of e-mail:
1.766     raeburn  9351: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.884     raeburn  9352: (d) Help requests, (e) Course requests needing approval,  generated by
                   9353: lonerrorhandler.pm, CHECKRPMS, loncron, lonsupportreq.pm and
                   9354: loncoursequeueadmin.pm respectively.
1.618     raeburn  9355: 
                   9356: Inputs:
1.619     raeburn  9357: defmail (scalar - email address of default recipient), 
1.618     raeburn  9358: mailing type (scalar - errormail, packagesmail, or helpdeskmail), 
1.619     raeburn  9359: defdom (domain for which to retrieve configuration settings),
                   9360: origmail (scalar - email address of recipient from loncapa.conf, 
                   9361: i.e., predates configuration by DC via domainprefs.pm 
1.618     raeburn  9362: 
1.655     raeburn  9363: Returns: comma separated list of addresses to which to send e-mail.
                   9364: 
                   9365: =back
1.618     raeburn  9366: 
                   9367: =cut
                   9368: 
                   9369: ############################################################
                   9370: ############################################################
                   9371: sub build_recipient_list {
1.619     raeburn  9372:     my ($defmail,$mailing,$defdom,$origmail) = @_;
1.618     raeburn  9373:     my @recipients;
                   9374:     my $otheremails;
                   9375:     my %domconfig =
                   9376:          &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
                   9377:     if (ref($domconfig{'contacts'}) eq 'HASH') {
1.766     raeburn  9378:         if (exists($domconfig{'contacts'}{$mailing})) {
                   9379:             if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
                   9380:                 my @contacts = ('adminemail','supportemail');
                   9381:                 foreach my $item (@contacts) {
                   9382:                     if ($domconfig{'contacts'}{$mailing}{$item}) {
                   9383:                         my $addr = $domconfig{'contacts'}{$item}; 
                   9384:                         if (!grep(/^\Q$addr\E$/,@recipients)) {
                   9385:                             push(@recipients,$addr);
                   9386:                         }
1.619     raeburn  9387:                     }
1.766     raeburn  9388:                     $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
1.618     raeburn  9389:                 }
                   9390:             }
1.766     raeburn  9391:         } elsif ($origmail ne '') {
                   9392:             push(@recipients,$origmail);
1.618     raeburn  9393:         }
1.619     raeburn  9394:     } elsif ($origmail ne '') {
                   9395:         push(@recipients,$origmail);
1.618     raeburn  9396:     }
1.688     raeburn  9397:     if (defined($defmail)) {
                   9398:         if ($defmail ne '') {
                   9399:             push(@recipients,$defmail);
                   9400:         }
1.618     raeburn  9401:     }
                   9402:     if ($otheremails) {
1.619     raeburn  9403:         my @others;
                   9404:         if ($otheremails =~ /,/) {
                   9405:             @others = split(/,/,$otheremails);
1.618     raeburn  9406:         } else {
1.619     raeburn  9407:             push(@others,$otheremails);
                   9408:         }
                   9409:         foreach my $addr (@others) {
                   9410:             if (!grep(/^\Q$addr\E$/,@recipients)) {
                   9411:                 push(@recipients,$addr);
                   9412:             }
1.618     raeburn  9413:         }
                   9414:     }
1.619     raeburn  9415:     my $recipientlist = join(',',@recipients); 
1.618     raeburn  9416:     return $recipientlist;
                   9417: }
                   9418: 
1.127     matthew  9419: ############################################################
                   9420: ############################################################
1.154     albertel 9421: 
1.655     raeburn  9422: =pod
                   9423: 
                   9424: =head1 Course Catalog Routines
                   9425: 
                   9426: =over 4
                   9427: 
                   9428: =item * &gather_categories()
                   9429: 
                   9430: Converts category definitions - keys of categories hash stored in  
                   9431: coursecategories in configuration.db on the primary library server in a 
                   9432: domain - to an array.  Also generates javascript and idx hash used to 
                   9433: generate Domain Coordinator interface for editing Course Categories.
                   9434: 
                   9435: Inputs:
1.663     raeburn  9436: 
1.655     raeburn  9437: categories (reference to hash of category definitions).
1.663     raeburn  9438: 
1.655     raeburn  9439: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   9440:       categories and subcategories).
1.663     raeburn  9441: 
1.655     raeburn  9442: idx (reference to hash of counters used in Domain Coordinator interface for 
                   9443:       editing Course Categories).
1.663     raeburn  9444: 
1.655     raeburn  9445: jsarray (reference to array of categories used to create Javascript arrays for
                   9446:          Domain Coordinator interface for editing Course Categories).
                   9447: 
                   9448: Returns: nothing
                   9449: 
                   9450: Side effects: populates cats, idx and jsarray. 
                   9451: 
                   9452: =cut
                   9453: 
                   9454: sub gather_categories {
                   9455:     my ($categories,$cats,$idx,$jsarray) = @_;
                   9456:     my %counters;
                   9457:     my $num = 0;
                   9458:     foreach my $item (keys(%{$categories})) {
                   9459:         my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
                   9460:         if ($container eq '' && $depth == 0) {
                   9461:             $cats->[$depth][$categories->{$item}] = $cat;
                   9462:         } else {
                   9463:             $cats->[$depth]{$container}[$categories->{$item}] = $cat;
                   9464:         }
                   9465:         my ($escitem,$tail) = split(/:/,$item,2);
                   9466:         if ($counters{$tail} eq '') {
                   9467:             $counters{$tail} = $num;
                   9468:             $num ++;
                   9469:         }
                   9470:         if (ref($idx) eq 'HASH') {
                   9471:             $idx->{$item} = $counters{$tail};
                   9472:         }
                   9473:         if (ref($jsarray) eq 'ARRAY') {
                   9474:             push(@{$jsarray->[$counters{$tail}]},$item);
                   9475:         }
                   9476:     }
                   9477:     return;
                   9478: }
                   9479: 
                   9480: =pod
                   9481: 
                   9482: =item * &extract_categories()
                   9483: 
                   9484: Used to generate breadcrumb trails for course categories.
                   9485: 
                   9486: Inputs:
1.663     raeburn  9487: 
1.655     raeburn  9488: categories (reference to hash of category definitions).
1.663     raeburn  9489: 
1.655     raeburn  9490: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   9491:       categories and subcategories).
1.663     raeburn  9492: 
1.655     raeburn  9493: trails (reference to array of breacrumb trails for each category).
1.663     raeburn  9494: 
1.655     raeburn  9495: allitems (reference to hash - key is category key 
                   9496:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  9497: 
1.655     raeburn  9498: idx (reference to hash of counters used in Domain Coordinator interface for
                   9499:       editing Course Categories).
1.663     raeburn  9500: 
1.655     raeburn  9501: jsarray (reference to array of categories used to create Javascript arrays for
                   9502:          Domain Coordinator interface for editing Course Categories).
                   9503: 
1.665     raeburn  9504: subcats (reference to hash of arrays containing all subcategories within each 
                   9505:          category, -recursive)
                   9506: 
1.655     raeburn  9507: Returns: nothing
                   9508: 
                   9509: Side effects: populates trails and allitems hash references.
                   9510: 
                   9511: =cut
                   9512: 
                   9513: sub extract_categories {
1.665     raeburn  9514:     my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_;
1.655     raeburn  9515:     if (ref($categories) eq 'HASH') {
                   9516:         &gather_categories($categories,$cats,$idx,$jsarray);
                   9517:         if (ref($cats->[0]) eq 'ARRAY') {
                   9518:             for (my $i=0; $i<@{$cats->[0]}; $i++) {
                   9519:                 my $name = $cats->[0][$i];
                   9520:                 my $item = &escape($name).'::0';
                   9521:                 my $trailstr;
                   9522:                 if ($name eq 'instcode') {
                   9523:                     $trailstr = &mt('Official courses (with institutional codes)');
                   9524:                 } else {
                   9525:                     $trailstr = $name;
                   9526:                 }
                   9527:                 if ($allitems->{$item} eq '') {
                   9528:                     push(@{$trails},$trailstr);
                   9529:                     $allitems->{$item} = scalar(@{$trails})-1;
                   9530:                 }
                   9531:                 my @parents = ($name);
                   9532:                 if (ref($cats->[1]{$name}) eq 'ARRAY') {
                   9533:                     for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
                   9534:                         my $category = $cats->[1]{$name}[$j];
1.665     raeburn  9535:                         if (ref($subcats) eq 'HASH') {
                   9536:                             push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
                   9537:                         }
                   9538:                         &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats);
                   9539:                     }
                   9540:                 } else {
                   9541:                     if (ref($subcats) eq 'HASH') {
                   9542:                         $subcats->{$item} = [];
1.655     raeburn  9543:                     }
                   9544:                 }
                   9545:             }
                   9546:         }
                   9547:     }
                   9548:     return;
                   9549: }
                   9550: 
                   9551: =pod
                   9552: 
                   9553: =item *&recurse_categories()
                   9554: 
                   9555: Recursively used to generate breadcrumb trails for course categories.
                   9556: 
                   9557: Inputs:
1.663     raeburn  9558: 
1.655     raeburn  9559: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   9560:       categories and subcategories).
1.663     raeburn  9561: 
1.655     raeburn  9562: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663     raeburn  9563: 
                   9564: category (current course category, for which breadcrumb trail is being generated).
                   9565: 
                   9566: trails (reference to array of breadcrumb trails for each category).
                   9567: 
1.655     raeburn  9568: allitems (reference to hash - key is category key
                   9569:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  9570: 
1.655     raeburn  9571: parents (array containing containers directories for current category, 
                   9572:          back to top level). 
                   9573: 
                   9574: Returns: nothing
                   9575: 
                   9576: Side effects: populates trails and allitems hash references
                   9577: 
                   9578: =cut
                   9579: 
                   9580: sub recurse_categories {
1.665     raeburn  9581:     my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_;
1.655     raeburn  9582:     my $shallower = $depth - 1;
                   9583:     if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
                   9584:         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
                   9585:             my $name = $cats->[$depth]{$category}[$k];
                   9586:             my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   9587:             my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   9588:             if ($allitems->{$item} eq '') {
                   9589:                 push(@{$trails},$trailstr);
                   9590:                 $allitems->{$item} = scalar(@{$trails})-1;
                   9591:             }
                   9592:             my $deeper = $depth+1;
                   9593:             push(@{$parents},$category);
1.665     raeburn  9594:             if (ref($subcats) eq 'HASH') {
                   9595:                 my $subcat = &escape($name).':'.$category.':'.$depth;
                   9596:                 for (my $j=@{$parents}; $j>=0; $j--) {
                   9597:                     my $higher;
                   9598:                     if ($j > 0) {
                   9599:                         $higher = &escape($parents->[$j]).':'.
                   9600:                                   &escape($parents->[$j-1]).':'.$j;
                   9601:                     } else {
                   9602:                         $higher = &escape($parents->[$j]).'::'.$j;
                   9603:                     }
                   9604:                     push(@{$subcats->{$higher}},$subcat);
                   9605:                 }
                   9606:             }
                   9607:             &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
                   9608:                                 $subcats);
1.655     raeburn  9609:             pop(@{$parents});
                   9610:         }
                   9611:     } else {
                   9612:         my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   9613:         my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   9614:         if ($allitems->{$item} eq '') {
                   9615:             push(@{$trails},$trailstr);
                   9616:             $allitems->{$item} = scalar(@{$trails})-1;
                   9617:         }
                   9618:     }
                   9619:     return;
                   9620: }
                   9621: 
1.663     raeburn  9622: =pod
                   9623: 
                   9624: =item *&assign_categories_table()
                   9625: 
                   9626: Create a datatable for display of hierarchical categories in a domain,
                   9627: with checkboxes to allow a course to be categorized. 
                   9628: 
                   9629: Inputs:
                   9630: 
                   9631: cathash - reference to hash of categories defined for the domain (from
                   9632:           configuration.db)
                   9633: 
                   9634: currcat - scalar with an & separated list of categories assigned to a course. 
                   9635: 
                   9636: Returns: $output (markup to be displayed) 
                   9637: 
                   9638: =cut
                   9639: 
                   9640: sub assign_categories_table {
                   9641:     my ($cathash,$currcat) = @_;
                   9642:     my $output;
                   9643:     if (ref($cathash) eq 'HASH') {
                   9644:         my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth);
                   9645:         &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray);
                   9646:         $maxdepth = scalar(@cats);
                   9647:         if (@cats > 0) {
                   9648:             my $itemcount = 0;
                   9649:             if (ref($cats[0]) eq 'ARRAY') {
                   9650:                 $output = &Apache::loncommon::start_data_table();
                   9651:                 my @currcategories;
                   9652:                 if ($currcat ne '') {
                   9653:                     @currcategories = split('&',$currcat);
                   9654:                 }
                   9655:                 for (my $i=0; $i<@{$cats[0]}; $i++) {
                   9656:                     my $parent = $cats[0][$i];
                   9657:                     my $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   9658:                     next if ($parent eq 'instcode');
                   9659:                     my $item = &escape($parent).'::0';
                   9660:                     my $checked = '';
                   9661:                     if (@currcategories > 0) {
                   9662:                         if (grep(/^\Q$item\E$/,@currcategories)) {
1.772     bisitz   9663:                             $checked = ' checked="checked"';
1.663     raeburn  9664:                         }
                   9665:                     }
1.675     raeburn  9666:                     $output .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
                   9667:                                '<input type="checkbox" name="usecategory" value="'.
                   9668:                                $item.'"'.$checked.' />'.$parent.'</span>'.
                   9669:                                '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663     raeburn  9670:                     my $depth = 1;
                   9671:                     push(@path,$parent);
                   9672:                     $output .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories);
                   9673:                     pop(@path);
                   9674:                     $output .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
                   9675:                     $itemcount ++;
                   9676:                 }
                   9677:                 $output .= &Apache::loncommon::end_data_table();
                   9678:             }
                   9679:         }
                   9680:     }
                   9681:     return $output;
                   9682: }
                   9683: 
                   9684: =pod
                   9685: 
                   9686: =item *&assign_category_rows()
                   9687: 
                   9688: Create a datatable row for display of nested categories in a domain,
                   9689: with checkboxes to allow a course to be categorized,called recursively.
                   9690: 
                   9691: Inputs:
                   9692: 
                   9693: itemcount - track row number for alternating colors
                   9694: 
                   9695: cats - reference to array of arrays/hashes which encapsulates hierarchy of
                   9696:       categories and subcategories.
                   9697: 
                   9698: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
                   9699: 
                   9700: parent - parent of current category item
                   9701: 
                   9702: path - Array containing all categories back up through the hierarchy from the
                   9703:        current category to the top level.
                   9704: 
                   9705: currcategories - reference to array of current categories assigned to the course
                   9706: 
                   9707: Returns: $output (markup to be displayed).
                   9708: 
                   9709: =cut
                   9710: 
                   9711: sub assign_category_rows {
                   9712:     my ($itemcount,$cats,$depth,$parent,$path,$currcategories) = @_;
                   9713:     my ($text,$name,$item,$chgstr);
                   9714:     if (ref($cats) eq 'ARRAY') {
                   9715:         my $maxdepth = scalar(@{$cats});
                   9716:         if (ref($cats->[$depth]) eq 'HASH') {
                   9717:             if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
                   9718:                 my $numchildren = @{$cats->[$depth]{$parent}};
                   9719:                 my $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   9720:                 $text .= '<td><table class="LC_datatable">';
                   9721:                 for (my $j=0; $j<$numchildren; $j++) {
                   9722:                     $name = $cats->[$depth]{$parent}[$j];
                   9723:                     $item = &escape($name).':'.&escape($parent).':'.$depth;
                   9724:                     my $deeper = $depth+1;
                   9725:                     my $checked = '';
                   9726:                     if (ref($currcategories) eq 'ARRAY') {
                   9727:                         if (@{$currcategories} > 0) {
                   9728:                             if (grep(/^\Q$item\E$/,@{$currcategories})) {
1.772     bisitz   9729:                                 $checked = ' checked="checked"';
1.663     raeburn  9730:                             }
                   9731:                         }
                   9732:                     }
1.664     raeburn  9733:                     $text .= '<tr><td><span class="LC_nobreak"><label>'.
                   9734:                              '<input type="checkbox" name="usecategory" value="'.
1.675     raeburn  9735:                              $item.'"'.$checked.' />'.$name.'</label></span>'.
                   9736:                              '<input type="hidden" name="catname" value="'.$name.'" />'.
                   9737:                              '</td><td>';
1.663     raeburn  9738:                     if (ref($path) eq 'ARRAY') {
                   9739:                         push(@{$path},$name);
                   9740:                         $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories);
                   9741:                         pop(@{$path});
                   9742:                     }
                   9743:                     $text .= '</td></tr>';
                   9744:                 }
                   9745:                 $text .= '</table></td>';
                   9746:             }
                   9747:         }
                   9748:     }
                   9749:     return $text;
                   9750: }
                   9751: 
1.655     raeburn  9752: ############################################################
                   9753: ############################################################
                   9754: 
                   9755: 
1.443     albertel 9756: sub commit_customrole {
1.664     raeburn  9757:     my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
1.630     raeburn  9758:     my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443     albertel 9759:                          ($start?', '.&mt('starting').' '.localtime($start):'').
                   9760:                          ($end?', ending '.localtime($end):'').': <b>'.
                   9761:               &Apache::lonnet::assigncustomrole(
1.664     raeburn  9762:                  $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
1.443     albertel 9763:                  '</b><br />';
                   9764:     return $output;
                   9765: }
                   9766: 
                   9767: sub commit_standardrole {
1.541     raeburn  9768:     my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
                   9769:     my ($output,$logmsg,$linefeed);
                   9770:     if ($context eq 'auto') {
                   9771:         $linefeed = "\n";
                   9772:     } else {
                   9773:         $linefeed = "<br />\n";
                   9774:     }  
1.443     albertel 9775:     if ($three eq 'st') {
1.541     raeburn  9776:         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
                   9777:                                          $one,$two,$sec,$context);
                   9778:         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
1.626     raeburn  9779:             ($result eq 'unknown_course') || ($result eq 'refused')) {
                   9780:             $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
1.443     albertel 9781:         } else {
1.541     raeburn  9782:             $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443     albertel 9783:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  9784:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
                   9785:             if ($context eq 'auto') {
                   9786:                 $output .= $result.$linefeed.&mt('Add to classlist').': ok';
                   9787:             } else {
                   9788:                $output .= '<b>'.$result.'</b>'.$linefeed.
                   9789:                &mt('Add to classlist').': <b>ok</b>';
                   9790:             }
                   9791:             $output .= $linefeed;
1.443     albertel 9792:         }
                   9793:     } else {
                   9794:         $output = &mt('Assigning').' '.$three.' in '.$url.
                   9795:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  9796:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652     raeburn  9797:         my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541     raeburn  9798:         if ($context eq 'auto') {
                   9799:             $output .= $result.$linefeed;
                   9800:         } else {
                   9801:             $output .= '<b>'.$result.'</b>'.$linefeed;
                   9802:         }
1.443     albertel 9803:     }
                   9804:     return $output;
                   9805: }
                   9806: 
                   9807: sub commit_studentrole {
1.541     raeburn  9808:     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
1.626     raeburn  9809:     my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541     raeburn  9810:     if ($context eq 'auto') {
                   9811:         $linefeed = "\n";
                   9812:     } else {
                   9813:         $linefeed = '<br />'."\n";
                   9814:     }
1.443     albertel 9815:     if (defined($one) && defined($two)) {
                   9816:         my $cid=$one.'_'.$two;
                   9817:         my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
                   9818:         my $secchange = 0;
                   9819:         my $expire_role_result;
                   9820:         my $modify_section_result;
1.628     raeburn  9821:         if ($oldsec ne '-1') { 
                   9822:             if ($oldsec ne $sec) {
1.443     albertel 9823:                 $secchange = 1;
1.628     raeburn  9824:                 my $now = time;
1.443     albertel 9825:                 my $uurl='/'.$cid;
                   9826:                 $uurl=~s/\_/\//g;
                   9827:                 if ($oldsec) {
                   9828:                     $uurl.='/'.$oldsec;
                   9829:                 }
1.626     raeburn  9830:                 $oldsecurl = $uurl;
1.628     raeburn  9831:                 $expire_role_result = 
1.652     raeburn  9832:                     &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628     raeburn  9833:                 if ($env{'request.course.sec'} ne '') { 
                   9834:                     if ($expire_role_result eq 'refused') {
                   9835:                         my @roles = ('st');
                   9836:                         my @statuses = ('previous');
                   9837:                         my @roledoms = ($one);
                   9838:                         my $withsec = 1;
                   9839:                         my %roleshash = 
                   9840:                             &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
                   9841:                                               \@statuses,\@roles,\@roledoms,$withsec);
                   9842:                         if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
                   9843:                             my ($oldstart,$oldend) = 
                   9844:                                 split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
                   9845:                             if ($oldend > 0 && $oldend <= $now) {
                   9846:                                 $expire_role_result = 'ok';
                   9847:                             }
                   9848:                         }
                   9849:                     }
                   9850:                 }
1.443     albertel 9851:                 $result = $expire_role_result;
                   9852:             }
                   9853:         }
                   9854:         if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.652     raeburn  9855:             $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid,'',$context);
1.443     albertel 9856:             if ($modify_section_result =~ /^ok/) {
                   9857:                 if ($secchange == 1) {
1.628     raeburn  9858:                     if ($sec eq '') {
                   9859:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
                   9860:                     } else {
                   9861:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
                   9862:                     }
1.443     albertel 9863:                 } elsif ($oldsec eq '-1') {
1.628     raeburn  9864:                     if ($sec eq '') {
                   9865:                         $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
                   9866:                     } else {
                   9867:                         $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   9868:                     }
1.443     albertel 9869:                 } else {
1.628     raeburn  9870:                     if ($sec eq '') {
                   9871:                         $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
                   9872:                     } else {
                   9873:                         $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   9874:                     }
1.443     albertel 9875:                 }
                   9876:             } else {
1.628     raeburn  9877:                 if ($secchange) {       
                   9878:                     $$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;
                   9879:                 } else {
                   9880:                     $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
                   9881:                 }
1.443     albertel 9882:             }
                   9883:             $result = $modify_section_result;
                   9884:         } elsif ($secchange == 1) {
1.628     raeburn  9885:             if ($oldsec eq '') {
                   9886:                 $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_3] -error: ',$uname,$cid).' '.$expire_role_result.$linefeed;
                   9887:             } else {
                   9888:                 $$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;
                   9889:             }
1.626     raeburn  9890:             if ($expire_role_result eq 'refused') {
                   9891:                 my $newsecurl = '/'.$cid;
                   9892:                 $newsecurl =~ s/\_/\//g;
                   9893:                 if ($sec ne '') {
                   9894:                     $newsecurl.='/'.$sec;
                   9895:                 }
                   9896:                 if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
                   9897:                     if ($sec eq '') {
                   9898:                         $$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;
                   9899:                     } else {
                   9900:                         $$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;
                   9901:                     }
                   9902:                 }
                   9903:             }
1.443     albertel 9904:         }
                   9905:     } else {
1.626     raeburn  9906:         $$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 9907:         $result = "error: incomplete course id\n";
                   9908:     }
                   9909:     return $result;
                   9910: }
                   9911: 
                   9912: ############################################################
                   9913: ############################################################
                   9914: 
1.566     albertel 9915: sub check_clone {
1.578     raeburn  9916:     my ($args,$linefeed) = @_;
1.566     albertel 9917:     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
                   9918:     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
                   9919:     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
                   9920:     my $clonemsg;
                   9921:     my $can_clone = 0;
                   9922: 
                   9923:     if ($clonehome eq 'no_host') {
1.578     raeburn  9924:         $clonemsg = &mt('No new course created.').$linefeed.&mt('A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});     
1.566     albertel 9925:     } else {
                   9926: 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.882     raeburn  9927: 	if (($env{'request.role.domain'} eq $args->{'clonedomain'}) && 
                   9928:             (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.566     albertel 9929: 	    $can_clone = 1;
                   9930: 	} else {
                   9931: 	    my %clonehash = &Apache::lonnet::get('environment',['cloners'],
                   9932: 						 $args->{'clonedomain'},$args->{'clonecourse'});
                   9933: 	    my @cloners = split(/,/,$clonehash{'cloners'});
1.578     raeburn  9934:             if (grep(/^\*$/,@cloners)) {
                   9935:                 $can_clone = 1;
                   9936:             } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
                   9937:                 $can_clone = 1;
                   9938:             } else {
                   9939: 	        my %roleshash =
                   9940: 		    &Apache::lonnet::get_my_roles($args->{'ccuname'},
                   9941: 					 $args->{'ccdomain'},
                   9942:                                          'userroles',['active'],['cc'],
                   9943: 					 [$args->{'clonedomain'}]);
                   9944: 	        if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':cc'}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
                   9945: 		    $can_clone = 1;
                   9946: 	        } else {
                   9947:                     $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'});
                   9948: 	        }
1.566     albertel 9949: 	    }
1.578     raeburn  9950:         }
1.566     albertel 9951:     }
                   9952:     return ($can_clone, $clonemsg, $cloneid, $clonehome);
                   9953: }
                   9954: 
1.444     albertel 9955: sub construct_course {
1.885     raeburn  9956:     my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,$cnum,$category) = @_;
1.444     albertel 9957:     my $outcome;
1.541     raeburn  9958:     my $linefeed =  '<br />'."\n";
                   9959:     if ($context eq 'auto') {
                   9960:         $linefeed = "\n";
                   9961:     }
1.566     albertel 9962: 
                   9963: #
                   9964: # Are we cloning?
                   9965: #
                   9966:     my ($can_clone, $clonemsg, $cloneid, $clonehome);
                   9967:     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578     raeburn  9968: 	($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566     albertel 9969: 	if ($context ne 'auto') {
1.578     raeburn  9970:             if ($clonemsg ne '') {
                   9971: 	        $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
                   9972:             }
1.566     albertel 9973: 	}
                   9974: 	$outcome .= $clonemsg.$linefeed;
                   9975: 
                   9976:         if (!$can_clone) {
                   9977: 	    return (0,$outcome);
                   9978: 	}
                   9979:     }
                   9980: 
1.444     albertel 9981: #
                   9982: # Open course
                   9983: #
                   9984:     my $crstype = lc($args->{'crstype'});
                   9985:     my %cenv=();
                   9986:     $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
                   9987:                                              $args->{'cdescr'},
                   9988:                                              $args->{'curl'},
                   9989:                                              $args->{'course_home'},
                   9990:                                              $args->{'nonstandard'},
                   9991:                                              $args->{'crscode'},
                   9992:                                              $args->{'ccuname'}.':'.
                   9993:                                              $args->{'ccdomain'},
1.882     raeburn  9994:                                              $args->{'crstype'},
1.885     raeburn  9995:                                              $cnum,$context,$category);
1.444     albertel 9996: 
                   9997:     # Note: The testing routines depend on this being output; see 
                   9998:     # Utils::Course. This needs to at least be output as a comment
                   9999:     # if anyone ever decides to not show this, and Utils::Course::new
                   10000:     # will need to be suitably modified.
1.541     raeburn  10001:     $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
1.444     albertel 10002: #
                   10003: # Check if created correctly
                   10004: #
1.479     albertel 10005:     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444     albertel 10006:     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.541     raeburn  10007:     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566     albertel 10008: 
1.444     albertel 10009: #
1.566     albertel 10010: # Do the cloning
                   10011: #   
                   10012:     if ($can_clone && $cloneid) {
                   10013: 	$clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
                   10014: 	if ($context ne 'auto') {
                   10015: 	    $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
                   10016: 	}
                   10017: 	$outcome .= $clonemsg.$linefeed;
                   10018: 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444     albertel 10019: # Copy all files
1.637     www      10020: 	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
1.444     albertel 10021: # Restore URL
1.566     albertel 10022: 	$cenv{'url'}=$oldcenv{'url'};
1.444     albertel 10023: # Restore title
1.566     albertel 10024: 	$cenv{'description'}=$oldcenv{'description'};
1.444     albertel 10025: # Mark as cloned
1.566     albertel 10026: 	$cenv{'clonedfrom'}=$cloneid;
1.638     www      10027: # Need to clone grading mode
                   10028:         my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
                   10029:         $cenv{'grading'}=$newenv{'grading'};
                   10030: # Do not clone these environment entries
                   10031:         &Apache::lonnet::del('environment',
                   10032:                   ['default_enrollment_start_date',
                   10033:                    'default_enrollment_end_date',
                   10034:                    'question.email',
                   10035:                    'policy.email',
                   10036:                    'comment.email',
                   10037:                    'pch.users.denied',
1.725     raeburn  10038:                    'plc.users.denied',
                   10039:                    'hidefromcat',
                   10040:                    'categories'],
1.638     www      10041:                    $$crsudom,$$crsunum);
1.444     albertel 10042:     }
1.566     albertel 10043: 
1.444     albertel 10044: #
                   10045: # Set environment (will override cloned, if existing)
                   10046: #
                   10047:     my @sections = ();
                   10048:     my @xlists = ();
                   10049:     if ($args->{'crstype'}) {
                   10050:         $cenv{'type'}=$args->{'crstype'};
                   10051:     }
                   10052:     if ($args->{'crsid'}) {
                   10053:         $cenv{'courseid'}=$args->{'crsid'};
                   10054:     }
                   10055:     if ($args->{'crscode'}) {
                   10056:         $cenv{'internal.coursecode'}=$args->{'crscode'};
                   10057:     }
                   10058:     if ($args->{'crsquota'} ne '') {
                   10059:         $cenv{'internal.coursequota'}=$args->{'crsquota'};
                   10060:     } else {
                   10061:         $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
                   10062:     }
                   10063:     if ($args->{'ccuname'}) {
                   10064:         $cenv{'internal.courseowner'} = $args->{'ccuname'}.
                   10065:                                         ':'.$args->{'ccdomain'};
                   10066:     } else {
                   10067:         $cenv{'internal.courseowner'} = $args->{'curruser'};
                   10068:     }
                   10069:     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
                   10070:     if ($args->{'crssections'}) {
                   10071:         $cenv{'internal.sectionnums'} = '';
                   10072:         if ($args->{'crssections'} =~ m/,/) {
                   10073:             @sections = split/,/,$args->{'crssections'};
                   10074:         } else {
                   10075:             $sections[0] = $args->{'crssections'};
                   10076:         }
                   10077:         if (@sections > 0) {
                   10078:             foreach my $item (@sections) {
                   10079:                 my ($sec,$gp) = split/:/,$item;
                   10080:                 my $class = $args->{'crscode'}.$sec;
                   10081:                 my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
                   10082:                 $cenv{'internal.sectionnums'} .= $item.',';
                   10083:                 unless ($addcheck eq 'ok') {
                   10084:                     push @badclasses, $class;
                   10085:                 }
                   10086:             }
                   10087:             $cenv{'internal.sectionnums'} =~ s/,$//;
                   10088:         }
                   10089:     }
                   10090: # do not hide course coordinator from staff listing, 
                   10091: # even if privileged
                   10092:     $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   10093: # add crosslistings
                   10094:     if ($args->{'crsxlist'}) {
                   10095:         $cenv{'internal.crosslistings'}='';
                   10096:         if ($args->{'crsxlist'} =~ m/,/) {
                   10097:             @xlists = split/,/,$args->{'crsxlist'};
                   10098:         } else {
                   10099:             $xlists[0] = $args->{'crsxlist'};
                   10100:         }
                   10101:         if (@xlists > 0) {
                   10102:             foreach my $item (@xlists) {
                   10103:                 my ($xl,$gp) = split/:/,$item;
                   10104:                 my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
                   10105:                 $cenv{'internal.crosslistings'} .= $item.',';
                   10106:                 unless ($addcheck eq 'ok') {
                   10107:                     push @badclasses, $xl;
                   10108:                 }
                   10109:             }
                   10110:             $cenv{'internal.crosslistings'} =~ s/,$//;
                   10111:         }
                   10112:     }
                   10113:     if ($args->{'autoadds'}) {
                   10114:         $cenv{'internal.autoadds'}=$args->{'autoadds'};
                   10115:     }
                   10116:     if ($args->{'autodrops'}) {
                   10117:         $cenv{'internal.autodrops'}=$args->{'autodrops'};
                   10118:     }
                   10119: # check for notification of enrollment changes
                   10120:     my @notified = ();
                   10121:     if ($args->{'notify_owner'}) {
                   10122:         if ($args->{'ccuname'} ne '') {
                   10123:             push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
                   10124:         }
                   10125:     }
                   10126:     if ($args->{'notify_dc'}) {
                   10127:         if ($uname ne '') { 
1.630     raeburn  10128:             push(@notified,$uname.':'.$udom);
1.444     albertel 10129:         }
                   10130:     }
                   10131:     if (@notified > 0) {
                   10132:         my $notifylist;
                   10133:         if (@notified > 1) {
                   10134:             $notifylist = join(',',@notified);
                   10135:         } else {
                   10136:             $notifylist = $notified[0];
                   10137:         }
                   10138:         $cenv{'internal.notifylist'} = $notifylist;
                   10139:     }
                   10140:     if (@badclasses > 0) {
                   10141:         my %lt=&Apache::lonlocal::texthash(
                   10142:                 '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',
                   10143:                 'dnhr' => 'does not have rights to access enrollment in these classes',
                   10144:                 'adby' => 'as determined by the policies of your institution on access to official classlists'
                   10145:         );
1.541     raeburn  10146:         my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
                   10147:                            ' ('.$lt{'adby'}.')';
                   10148:         if ($context eq 'auto') {
                   10149:             $outcome .= $badclass_msg.$linefeed;
1.566     albertel 10150:             $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.541     raeburn  10151:             foreach my $item (@badclasses) {
                   10152:                 if ($context eq 'auto') {
                   10153:                     $outcome .= " - $item\n";
                   10154:                 } else {
                   10155:                     $outcome .= "<li>$item</li>\n";
                   10156:                 }
                   10157:             }
                   10158:             if ($context eq 'auto') {
                   10159:                 $outcome .= $linefeed;
                   10160:             } else {
1.566     albertel 10161:                 $outcome .= "</ul><br /><br /></div>\n";
1.541     raeburn  10162:             }
                   10163:         } 
1.444     albertel 10164:     }
                   10165:     if ($args->{'no_end_date'}) {
                   10166:         $args->{'endaccess'} = 0;
                   10167:     }
                   10168:     $cenv{'internal.autostart'}=$args->{'enrollstart'};
                   10169:     $cenv{'internal.autoend'}=$args->{'enrollend'};
                   10170:     $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
                   10171:     $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
                   10172:     if ($args->{'showphotos'}) {
                   10173:       $cenv{'internal.showphotos'}=$args->{'showphotos'};
                   10174:     }
                   10175:     $cenv{'internal.authtype'} = $args->{'authtype'};
                   10176:     $cenv{'internal.autharg'} = $args->{'autharg'}; 
                   10177:     if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
                   10178:         if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'}  eq '') {
1.541     raeburn  10179:             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'); 
                   10180:             if ($context eq 'auto') {
                   10181:                 $outcome .= $krb_msg;
                   10182:             } else {
1.566     albertel 10183:                 $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541     raeburn  10184:             }
                   10185:             $outcome .= $linefeed;
1.444     albertel 10186:         }
                   10187:     }
                   10188:     if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
                   10189:        if ($args->{'setpolicy'}) {
                   10190:            $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   10191:        }
                   10192:        if ($args->{'setcontent'}) {
                   10193:            $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   10194:        }
                   10195:     }
                   10196:     if ($args->{'reshome'}) {
                   10197: 	$cenv{'reshome'}=$args->{'reshome'}.'/';
                   10198: 	$cenv{'reshome'}=~s/\/+$/\//;
                   10199:     }
                   10200: #
                   10201: # course has keyed access
                   10202: #
                   10203:     if ($args->{'setkeys'}) {
                   10204:        $cenv{'keyaccess'}='yes';
                   10205:     }
                   10206: # if specified, key authority is not course, but user
                   10207: # only active if keyaccess is yes
                   10208:     if ($args->{'keyauth'}) {
1.487     albertel 10209: 	my ($user,$domain) = split(':',$args->{'keyauth'});
                   10210: 	$user = &LONCAPA::clean_username($user);
                   10211: 	$domain = &LONCAPA::clean_username($domain);
1.488     foxr     10212: 	if ($user ne '' && $domain ne '') {
1.487     albertel 10213: 	    $cenv{'keyauth'}=$user.':'.$domain;
1.444     albertel 10214: 	}
                   10215:     }
                   10216: 
                   10217:     if ($args->{'disresdis'}) {
                   10218:         $cenv{'pch.roles.denied'}='st';
                   10219:     }
                   10220:     if ($args->{'disablechat'}) {
                   10221:         $cenv{'plc.roles.denied'}='st';
                   10222:     }
                   10223: 
                   10224:     # Record we've not yet viewed the Course Initialization Helper for this 
                   10225:     # course
                   10226:     $cenv{'course.helper.not.run'} = 1;
                   10227:     #
                   10228:     # Use new Randomseed
                   10229:     #
                   10230:     $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
                   10231:     $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
                   10232:     #
                   10233:     # The encryption code and receipt prefix for this course
                   10234:     #
                   10235:     $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
                   10236:     $cenv{'internal.encpref'}=100+int(9*rand(99));
                   10237:     #
                   10238:     # By default, use standard grading
                   10239:     if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
                   10240: 
1.541     raeburn  10241:     $outcome .= $linefeed.&mt('Setting environment').': '.                 
                   10242:           &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 10243: #
                   10244: # Open all assignments
                   10245: #
                   10246:     if ($args->{'openall'}) {
                   10247:        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
                   10248:        my %storecontent = ($storeunder         => time,
                   10249:                            $storeunder.'.type' => 'date_start');
                   10250:        
                   10251:        $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541     raeburn  10252:                  ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 10253:    }
                   10254: #
                   10255: # Set first page
                   10256: #
                   10257:     unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
                   10258: 	    || ($cloneid)) {
1.445     albertel 10259: 	use LONCAPA::map;
1.444     albertel 10260: 	$outcome .= &mt('Setting first resource').': ';
1.445     albertel 10261: 
                   10262: 	my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
                   10263:         my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
                   10264: 
1.444     albertel 10265:         $outcome .= ($fatal?$errtext:'read ok').' - ';
                   10266:         my $title; my $url;
                   10267:         if ($args->{'firstres'} eq 'syl') {
1.690     bisitz   10268: 	    $title=&mt('Syllabus');
1.444     albertel 10269:             $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
                   10270:         } else {
1.690     bisitz   10271:             $title=&mt('Navigate Contents');
1.444     albertel 10272:             $url='/adm/navmaps';
                   10273:         }
1.445     albertel 10274: 
                   10275:         $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
                   10276: 	(my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
                   10277: 
                   10278: 	if ($errtext) { $fatal=2; }
1.541     raeburn  10279:         $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444     albertel 10280:     }
1.566     albertel 10281: 
                   10282:     return (1,$outcome);
1.444     albertel 10283: }
                   10284: 
                   10285: ############################################################
                   10286: ############################################################
                   10287: 
1.378     raeburn  10288: sub course_type {
                   10289:     my ($cid) = @_;
                   10290:     if (!defined($cid)) {
                   10291:         $cid = $env{'request.course.id'};
                   10292:     }
1.404     albertel 10293:     if (defined($env{'course.'.$cid.'.type'})) {
                   10294:         return $env{'course.'.$cid.'.type'};
1.378     raeburn  10295:     } else {
                   10296:         return 'Course';
1.377     raeburn  10297:     }
                   10298: }
1.156     albertel 10299: 
1.406     raeburn  10300: sub group_term {
                   10301:     my $crstype = &course_type();
                   10302:     my %names = (
                   10303:                   'Course' => 'group',
1.865     raeburn  10304:                   'Community' => 'group',
1.406     raeburn  10305:                 );
                   10306:     return $names{$crstype};
                   10307: }
                   10308: 
1.902     raeburn  10309: sub course_types {
                   10310:     my @types = ('official','unofficial','community');
                   10311:     my %typename = (
                   10312:                          official   => 'Official course',
                   10313:                          unofficial => 'Unofficial course',
                   10314:                          community  => 'Community',
                   10315:                    );
                   10316:     return (\@types,\%typename);
                   10317: }
                   10318: 
1.156     albertel 10319: sub icon {
                   10320:     my ($file)=@_;
1.505     albertel 10321:     my $curfext = lc((split(/\./,$file))[-1]);
1.168     albertel 10322:     my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156     albertel 10323:     my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168     albertel 10324:     if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
                   10325: 	if (-e  $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                   10326: 	          $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   10327: 	            $curfext.".gif") {
                   10328: 	    $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   10329: 		$curfext.".gif";
                   10330: 	}
                   10331:     }
1.249     albertel 10332:     return &lonhttpdurl($iconname);
1.154     albertel 10333: } 
1.84      albertel 10334: 
1.575     albertel 10335: sub lonhttpdurl {
1.692     www      10336: #
                   10337: # Had been used for "small fry" static images on separate port 8080.
                   10338: # Modify here if lightweight http functionality desired again.
                   10339: # Currently eliminated due to increasing firewall issues.
                   10340: #
1.575     albertel 10341:     my ($url)=@_;
1.692     www      10342:     return $url;
1.215     albertel 10343: }
                   10344: 
1.213     albertel 10345: sub connection_aborted {
                   10346:     my ($r)=@_;
                   10347:     $r->print(" ");$r->rflush();
                   10348:     my $c = $r->connection;
                   10349:     return $c->aborted();
                   10350: }
                   10351: 
1.221     foxr     10352: #    Escapes strings that may have embedded 's that will be put into
1.222     foxr     10353: #    strings as 'strings'.
                   10354: sub escape_single {
1.221     foxr     10355:     my ($input) = @_;
1.223     albertel 10356:     $input =~ s/\\/\\\\/g;	# Escape the \'s..(must be first)>
1.221     foxr     10357:     $input =~ s/\'/\\\'/g;	# Esacpe the 's....
                   10358:     return $input;
                   10359: }
1.223     albertel 10360: 
1.222     foxr     10361: #  Same as escape_single, but escape's "'s  This 
                   10362: #  can be used for  "strings"
                   10363: sub escape_double {
                   10364:     my ($input) = @_;
                   10365:     $input =~ s/\\/\\\\/g;	# Escape the /'s..(must be first)>
                   10366:     $input =~ s/\"/\\\"/g;	# Esacpe the "s....
                   10367:     return $input;
                   10368: }
1.223     albertel 10369:  
1.222     foxr     10370: #   Escapes the last element of a full URL.
                   10371: sub escape_url {
                   10372:     my ($url)   = @_;
1.238     raeburn  10373:     my @urlslices = split(/\//, $url,-1);
1.369     www      10374:     my $lastitem = &escape(pop(@urlslices));
1.223     albertel 10375:     return join('/',@urlslices).'/'.$lastitem;
1.222     foxr     10376: }
1.462     albertel 10377: 
1.820     raeburn  10378: sub compare_arrays {
                   10379:     my ($arrayref1,$arrayref2) = @_;
                   10380:     my (@difference,%count);
                   10381:     @difference = ();
                   10382:     %count = ();
                   10383:     if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
                   10384:         foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
                   10385:         foreach my $element (keys(%count)) {
                   10386:             if ($count{$element} == 1) {
                   10387:                 push(@difference,$element);
                   10388:             }
                   10389:         }
                   10390:     }
                   10391:     return @difference;
                   10392: }
                   10393: 
1.817     bisitz   10394: # -------------------------------------------------------- Initialize user login
1.462     albertel 10395: sub init_user_environment {
1.463     albertel 10396:     my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462     albertel 10397:     my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
                   10398: 
                   10399:     my $public=($username eq 'public' && $domain eq 'public');
                   10400: 
                   10401: # See if old ID present, if so, remove
                   10402: 
                   10403:     my ($filename,$cookie,$userroles);
                   10404:     my $now=time;
                   10405: 
                   10406:     if ($public) {
                   10407: 	my $max_public=100;
                   10408: 	my $oldest;
                   10409: 	my $oldest_time=0;
                   10410: 	for(my $next=1;$next<=$max_public;$next++) {
                   10411: 	    if (-e $lonids."/publicuser_$next.id") {
                   10412: 		my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
                   10413: 		if ($mtime<$oldest_time || !$oldest_time) {
                   10414: 		    $oldest_time=$mtime;
                   10415: 		    $oldest=$next;
                   10416: 		}
                   10417: 	    } else {
                   10418: 		$cookie="publicuser_$next";
                   10419: 		last;
                   10420: 	    }
                   10421: 	}
                   10422: 	if (!$cookie) { $cookie="publicuser_$oldest"; }
                   10423:     } else {
1.463     albertel 10424: 	# if this isn't a robot, kill any existing non-robot sessions
                   10425: 	if (!$args->{'robot'}) {
                   10426: 	    opendir(DIR,$lonids);
                   10427: 	    while ($filename=readdir(DIR)) {
                   10428: 		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
                   10429: 		    unlink($lonids.'/'.$filename);
                   10430: 		}
1.462     albertel 10431: 	    }
1.463     albertel 10432: 	    closedir(DIR);
1.462     albertel 10433: 	}
                   10434: # Give them a new cookie
1.463     albertel 10435: 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684     www      10436: 		                   : $now.$$.int(rand(10000)));
1.463     albertel 10437: 	$cookie="$username\_$id\_$domain\_$authhost";
1.462     albertel 10438:     
                   10439: # Initialize roles
                   10440: 
                   10441: 	$userroles=&Apache::lonnet::rolesinit($domain,$username,$authhost);
                   10442:     }
                   10443: # ------------------------------------ Check browser type and MathML capability
                   10444: 
                   10445:     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
                   10446:         $clientunicode,$clientos) = &decode_user_agent($r);
                   10447: 
                   10448: # ------------------------------------------------------------- Get environment
                   10449: 
                   10450:     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
                   10451:     my ($tmp) = keys(%userenv);
                   10452:     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   10453: 	# default remote control to off
                   10454: 	if ($userenv{'remote'} ne 'on') { $userenv{'remote'} = 'off'; }
                   10455:     } else {
                   10456: 	undef(%userenv);
                   10457:     }
                   10458:     if (($userenv{'interface'}) && (!$form->{'interface'})) {
                   10459: 	$form->{'interface'}=$userenv{'interface'};
                   10460:     }
                   10461:     $env{'environment.remote'}=$userenv{'remote'};
                   10462:     if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
                   10463: 
                   10464: # --------------- Do not trust query string to be put directly into environment
1.817     bisitz   10465:     foreach my $option ('interface','localpath','localres') {
                   10466:         $form->{$option}=~s/[\n\r\=]//gs;
1.462     albertel 10467:     }
                   10468: # --------------------------------------------------------- Write first profile
                   10469: 
                   10470:     {
                   10471: 	my %initial_env = 
                   10472: 	    ("user.name"          => $username,
                   10473: 	     "user.domain"        => $domain,
                   10474: 	     "user.home"          => $authhost,
                   10475: 	     "browser.type"       => $clientbrowser,
                   10476: 	     "browser.version"    => $clientversion,
                   10477: 	     "browser.mathml"     => $clientmathml,
                   10478: 	     "browser.unicode"    => $clientunicode,
                   10479: 	     "browser.os"         => $clientos,
                   10480: 	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
                   10481: 	     "request.course.fn"  => '',
                   10482: 	     "request.course.uri" => '',
                   10483: 	     "request.course.sec" => '',
                   10484: 	     "request.role"       => 'cm',
                   10485: 	     "request.role.adv"   => $env{'user.adv'},
                   10486: 	     "request.host"       => $ENV{'REMOTE_ADDR'},);
                   10487: 
                   10488:         if ($form->{'localpath'}) {
                   10489: 	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
                   10490: 	    $initial_env{"browser.localres"}   = $form->{'localres'};
                   10491:         }
                   10492: 	
                   10493: 	if ($public) {
                   10494: 	    $initial_env{"environment.remote"} = "off";
                   10495: 	}
                   10496: 	if ($form->{'interface'}) {
                   10497: 	    $form->{'interface'}=~s/\W//gs;
                   10498: 	    $initial_env{"browser.interface"} = $form->{'interface'};
                   10499: 	    $env{'browser.interface'}=$form->{'interface'};
                   10500: 	}
                   10501: 
1.724     raeburn  10502:         foreach my $tool ('aboutme','blog','portfolio') {
                   10503:             $userenv{'availabletools.'.$tool} = 
                   10504:                 &Apache::lonnet::usertools_access($username,$domain,$tool,'reload');
                   10505:         }
                   10506: 
1.864     raeburn  10507:         foreach my $crstype ('official','unofficial','community') {
1.765     raeburn  10508:             $userenv{'canrequest.'.$crstype} =
                   10509:                 &Apache::lonnet::usertools_access($username,$domain,$crstype,
                   10510:                                                   'reload','requestcourses');
                   10511:         }
                   10512: 
1.462     albertel 10513: 	$env{'user.environment'} = "$lonids/$cookie.id";
                   10514: 	
                   10515: 	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
                   10516: 		 &GDBM_WRCREAT(),0640)) {
                   10517: 	    &_add_to_env(\%disk_env,\%initial_env);
                   10518: 	    &_add_to_env(\%disk_env,\%userenv,'environment.');
                   10519: 	    &_add_to_env(\%disk_env,$userroles);
1.463     albertel 10520: 	    if (ref($args->{'extra_env'})) {
                   10521: 		&_add_to_env(\%disk_env,$args->{'extra_env'});
                   10522: 	    }
1.462     albertel 10523: 	    untie(%disk_env);
                   10524: 	} else {
1.705     tempelho 10525: 	    &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
                   10526: 			   'Could not create environment storage in lonauth: '.$!.'</span>');
1.462     albertel 10527: 	    return 'error: '.$!;
                   10528: 	}
                   10529:     }
                   10530:     $env{'request.role'}='cm';
                   10531:     $env{'request.role.adv'}=$env{'user.adv'};
                   10532:     $env{'browser.type'}=$clientbrowser;
                   10533: 
                   10534:     return $cookie;
                   10535: 
                   10536: }
                   10537: 
                   10538: sub _add_to_env {
                   10539:     my ($idf,$env_data,$prefix) = @_;
1.676     raeburn  10540:     if (ref($env_data) eq 'HASH') {
                   10541:         while (my ($key,$value) = each(%$env_data)) {
                   10542: 	    $idf->{$prefix.$key} = $value;
                   10543: 	    $env{$prefix.$key}   = $value;
                   10544:         }
1.462     albertel 10545:     }
                   10546: }
                   10547: 
1.685     tempelho 10548: # --- Get the symbolic name of a problem and the url
                   10549: sub get_symb {
                   10550:     my ($request,$silent) = @_;
1.726     raeburn  10551:     (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685     tempelho 10552:     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
                   10553:     if ($symb eq '') {
                   10554:         if (!$silent) {
                   10555:             $request->print("Unable to handle ambiguous references:$url:.");
                   10556:             return ();
                   10557:         }
                   10558:     }
                   10559:     &Apache::lonenc::check_decrypt(\$symb);
                   10560:     return ($symb);
                   10561: }
                   10562: 
                   10563: # --------------------------------------------------------------Get annotation
                   10564: 
                   10565: sub get_annotation {
                   10566:     my ($symb,$enc) = @_;
                   10567: 
                   10568:     my $key = $symb;
                   10569:     if (!$enc) {
                   10570:         $key =
                   10571:             &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
                   10572:     }
                   10573:     my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
                   10574:     return $annotation{$key};
                   10575: }
                   10576: 
                   10577: sub clean_symb {
1.731     raeburn  10578:     my ($symb,$delete_enc) = @_;
1.685     tempelho 10579: 
                   10580:     &Apache::lonenc::check_decrypt(\$symb);
                   10581:     my $enc = $env{'request.enc'};
1.731     raeburn  10582:     if ($delete_enc) {
1.730     raeburn  10583:         delete($env{'request.enc'});
                   10584:     }
1.685     tempelho 10585: 
                   10586:     return ($symb,$enc);
                   10587: }
1.462     albertel 10588: 
1.41      ng       10589: =pod
                   10590: 
                   10591: =back
                   10592: 
1.112     bowersj2 10593: =cut
1.41      ng       10594: 
1.112     bowersj2 10595: 1;
                   10596: __END__;
1.41      ng       10597: 

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