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

1.10      albertel    1: # The LearningOnline Network with CAPA
1.1       albertel    2: # a pile of common routines
1.10      albertel    3: #
1.590   ! raeburn     4: # $Id: loncommon.pm,v 1.589 2007/09/26 12:34:19 raeburn Exp $
1.10      albertel    5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
1.1       albertel   28: 
                     29: # Makes a table out of the previous attempts
1.2       albertel   30: # Inputs result_from_symbread, user, domain, course_id
1.16      harris41   31: # Reads in non-network-related .tab files
1.1       albertel   32: 
1.35      matthew    33: # POD header:
                     34: 
1.45      matthew    35: =pod
                     36: 
1.35      matthew    37: =head1 NAME
                     38: 
                     39: Apache::loncommon - pile of common routines
                     40: 
                     41: =head1 SYNOPSIS
                     42: 
1.112     bowersj2   43: Common routines for manipulating connections, student answers,
                     44:     domains, common Javascript fragments, etc.
1.35      matthew    45: 
1.112     bowersj2   46: =head1 OVERVIEW
1.35      matthew    47: 
1.112     bowersj2   48: A collection of commonly used subroutines that don't have a natural
                     49: home anywhere else. This collection helps remove
1.35      matthew    50: redundancy from other modules and increase efficiency of memory usage.
                     51: 
                     52: =cut 
                     53: 
                     54: # End of POD header
1.1       albertel   55: package Apache::loncommon;
                     56: 
                     57: use strict;
1.258     albertel   58: use Apache::lonnet;
1.46      matthew    59: use GDBM_File;
1.51      www        60: use POSIX qw(strftime mktime);
1.82      www        61: use Apache::lonmenu();
1.498     albertel   62: use Apache::lonenc();
1.117     www        63: use Apache::lonlocal;
1.139     matthew    64: use HTML::Entities;
1.334     albertel   65: use Apache::lonhtmlcommon();
                     66: use Apache::loncoursedata();
1.344     albertel   67: use Apache::lontexconvert();
1.444     albertel   68: use Apache::lonclonecourse();
1.479     albertel   69: use LONCAPA qw(:DEFAULT :match);
1.117     www        70: 
1.517     raeburn    71: # ---------------------------------------------- Designs
                     72: use vars qw(%defaultdesign);
                     73: 
1.22      www        74: my $readit;
                     75: 
1.517     raeburn    76: 
1.157     matthew    77: ##
                     78: ## Global Variables
                     79: ##
1.46      matthew    80: 
1.20      www        81: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12      harris41   82: my %language;
1.124     www        83: my %supported_language;
1.12      harris41   84: my %cprtag;
1.192     taceyjo1   85: my %scprtag;
1.351     www        86: my %fe; my %fd; my %fm;
1.41      ng         87: my %category_extensions;
1.12      harris41   88: 
1.46      matthew    89: # ---------------------------------------------- Thesaurus variables
1.144     matthew    90: #
                     91: # %Keywords:
                     92: #      A hash used by &keyword to determine if a word is considered a keyword.
                     93: # $thesaurus_db_file 
                     94: #      Scalar containing the full path to the thesaurus database.
1.46      matthew    95: 
                     96: my %Keywords;
                     97: my $thesaurus_db_file;
                     98: 
1.144     matthew    99: #
                    100: # Initialize values from language.tab, copyright.tab, filetypes.tab,
                    101: # thesaurus.tab, and filecategories.tab.
                    102: #
1.18      www       103: BEGIN {
1.46      matthew   104:     # Variable initialization
                    105:     $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
                    106:     #
1.22      www       107:     unless ($readit) {
1.12      harris41  108: # ------------------------------------------------------------------- languages
                    109:     {
1.158     raeburn   110:         my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    111:                                    '/language.tab';
                    112:         if ( open(my $fh,"<$langtabfile") ) {
1.356     albertel  113:             while (my $line = <$fh>) {
                    114:                 next if ($line=~/^\#/);
                    115:                 chomp($line);
                    116:                 my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$line));
1.158     raeburn   117:                 $language{$key}=$val.' - '.$enc;
                    118:                 if ($sup) {
                    119:                     $supported_language{$key}=$sup;
                    120:                 }
                    121:             }
                    122:             close($fh);
                    123:         }
1.12      harris41  124:     }
                    125: # ------------------------------------------------------------------ copyrights
                    126:     {
1.158     raeburn   127:         my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    128:                                   '/copyright.tab';
                    129:         if ( open (my $fh,"<$copyrightfile") ) {
1.356     albertel  130:             while (my $line = <$fh>) {
                    131:                 next if ($line=~/^\#/);
                    132:                 chomp($line);
                    133:                 my ($key,$val)=(split(/\s+/,$line,2));
1.158     raeburn   134:                 $cprtag{$key}=$val;
                    135:             }
                    136:             close($fh);
                    137:         }
1.12      harris41  138:     }
1.351     www       139: # ----------------------------------------------------------- source copyrights
1.192     taceyjo1  140:     {
                    141:         my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    142:                                   '/source_copyright.tab';
                    143:         if ( open (my $fh,"<$sourcecopyrightfile") ) {
1.356     albertel  144:             while (my $line = <$fh>) {
                    145:                 next if ($line =~ /^\#/);
                    146:                 chomp($line);
                    147:                 my ($key,$val)=(split(/\s+/,$line,2));
1.192     taceyjo1  148:                 $scprtag{$key}=$val;
                    149:             }
                    150:             close($fh);
                    151:         }
                    152:     }
1.63      www       153: 
1.517     raeburn   154: # -------------------------------------------------------------- default domain designs
1.63      www       155:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517     raeburn   156:     my $designfile = $designdir.'/default.tab';
                    157:     if ( open (my $fh,"<$designfile") ) {
                    158:         while (my $line = <$fh>) {
                    159:             next if ($line =~ /^\#/);
                    160:             chomp($line);
                    161:             my ($key,$val)=(split(/\=/,$line));
                    162:             if ($val) { $defaultdesign{$key}=$val; }
                    163:         }
                    164:         close($fh);
1.63      www       165:     }
                    166: 
1.15      harris41  167: # ------------------------------------------------------------- file categories
                    168:     {
1.158     raeburn   169:         my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    170:                                   '/filecategories.tab';
                    171:         if ( open (my $fh,"<$categoryfile") ) {
1.356     albertel  172: 	    while (my $line = <$fh>) {
                    173: 		next if ($line =~ /^\#/);
                    174: 		chomp($line);
                    175:                 my ($extension,$category)=(split(/\s+/,$line,2));
1.158     raeburn   176:                 push @{$category_extensions{lc($category)}},$extension;
                    177:             }
                    178:             close($fh);
                    179:         }
                    180: 
1.15      harris41  181:     }
1.12      harris41  182: # ------------------------------------------------------------------ file types
                    183:     {
1.158     raeburn   184:         my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    185:                '/filetypes.tab';
                    186:         if ( open (my $fh,"<$typesfile") ) {
1.356     albertel  187:             while (my $line = <$fh>) {
                    188: 		next if ($line =~ /^\#/);
                    189: 		chomp($line);
                    190:                 my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158     raeburn   191:                 if ($descr ne '') {
                    192:                     $fe{$ending}=lc($emb);
                    193:                     $fd{$ending}=$descr;
1.351     www       194:                     if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158     raeburn   195:                 }
                    196:             }
                    197:             close($fh);
                    198:         }
1.12      harris41  199:     }
1.22      www       200:     &Apache::lonnet::logthis(
1.46      matthew   201:               "<font color=yellow>INFO: Read file types</font>");
1.22      www       202:     $readit=1;
1.46      matthew   203:     }  # end of unless($readit) 
1.32      matthew   204:     
                    205: }
1.112     bowersj2  206: 
1.42      matthew   207: ###############################################################
                    208: ##           HTML and Javascript Helper Functions            ##
                    209: ###############################################################
                    210: 
                    211: =pod 
                    212: 
1.112     bowersj2  213: =head1 HTML and Javascript Functions
1.42      matthew   214: 
1.112     bowersj2  215: =over 4
                    216: 
                    217: =item * browser_and_searcher_javascript ()
                    218: 
                    219: X<browsing, javascript>X<searching, javascript>Returns a string
                    220: containing javascript with two functions, C<openbrowser> and
                    221: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
                    222: tags.
1.42      matthew   223: 
1.112     bowersj2  224: =item * openbrowser(formname,elementname,only,omit) [javascript]
1.42      matthew   225: 
                    226: inputs: formname, elementname, only, omit
                    227: 
                    228: formname and elementname indicate the name of the html form and name of
                    229: the element that the results of the browsing selection are to be placed in. 
                    230: 
                    231: Specifying 'only' will restrict the browser to displaying only files
1.185     www       232: with the given extension.  Can be a comma separated list.
1.42      matthew   233: 
                    234: Specifying 'omit' will restrict the browser to NOT displaying files
1.185     www       235: with the given extension.  Can be a comma separated list.
1.42      matthew   236: 
1.112     bowersj2  237: =item * opensearcher(formname, elementname) [javascript]
1.42      matthew   238: 
                    239: Inputs: formname, elementname
                    240: 
                    241: formname and elementname specify the name of the html form and the name
                    242: of the element the selection from the search results will be placed in.
1.542     raeburn   243: 
1.42      matthew   244: =cut
                    245: 
                    246: sub browser_and_searcher_javascript {
1.199     albertel  247:     my ($mode)=@_;
                    248:     if (!defined($mode)) { $mode='edit'; }
1.453     albertel  249:     my $resurl=&escape_single(&lastresurl());
1.42      matthew   250:     return <<END;
1.219     albertel  251: // <!-- BEGIN LON-CAPA Internal
1.50      matthew   252:     var editbrowser = null;
1.135     albertel  253:     function openbrowser(formname,elementname,only,omit,titleelement) {
1.170     www       254:         var url = '$resurl/?';
1.42      matthew   255:         if (editbrowser == null) {
                    256:             url += 'launch=1&';
                    257:         }
                    258:         url += 'catalogmode=interactive&';
1.199     albertel  259:         url += 'mode=$mode&';
1.42      matthew   260:         url += 'form=' + formname + '&';
                    261:         if (only != null) {
                    262:             url += 'only=' + only + '&';
1.217     albertel  263:         } else {
                    264:             url += 'only=&';
                    265: 	}
1.42      matthew   266:         if (omit != null) {
                    267:             url += 'omit=' + omit + '&';
1.217     albertel  268:         } else {
                    269:             url += 'omit=&';
                    270: 	}
1.135     albertel  271:         if (titleelement != null) {
                    272:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  273:         } else {
                    274: 	    url += 'titleelement=&';
                    275: 	}
1.42      matthew   276:         url += 'element=' + elementname + '';
                    277:         var title = 'Browser';
1.435     albertel  278:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   279:         options += ',width=700,height=600';
                    280:         editbrowser = open(url,title,options,'1');
                    281:         editbrowser.focus();
                    282:     }
                    283:     var editsearcher;
1.135     albertel  284:     function opensearcher(formname,elementname,titleelement) {
1.42      matthew   285:         var url = '/adm/searchcat?';
                    286:         if (editsearcher == null) {
                    287:             url += 'launch=1&';
                    288:         }
                    289:         url += 'catalogmode=interactive&';
1.199     albertel  290:         url += 'mode=$mode&';
1.42      matthew   291:         url += 'form=' + formname + '&';
1.135     albertel  292:         if (titleelement != null) {
                    293:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  294:         } else {
                    295: 	    url += 'titleelement=&';
                    296: 	}
1.42      matthew   297:         url += 'element=' + elementname + '';
                    298:         var title = 'Search';
1.435     albertel  299:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   300:         options += ',width=700,height=600';
                    301:         editsearcher = open(url,title,options,'1');
                    302:         editsearcher.focus();
                    303:     }
1.219     albertel  304: // END LON-CAPA Internal -->
1.42      matthew   305: END
1.170     www       306: }
                    307: 
                    308: sub lastresurl {
1.258     albertel  309:     if ($env{'environment.lastresurl'}) {
                    310: 	return $env{'environment.lastresurl'}
1.170     www       311:     } else {
                    312: 	return '/res';
                    313:     }
                    314: }
                    315: 
                    316: sub storeresurl {
                    317:     my $resurl=&Apache::lonnet::clutter(shift);
                    318:     unless ($resurl=~/^\/res/) { return 0; }
                    319:     $resurl=~s/\/$//;
                    320:     &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
                    321:     &Apache::lonnet::appenv('environment.lastresurl' => $resurl);
                    322:     return 1;
1.42      matthew   323: }
                    324: 
1.74      www       325: sub studentbrowser_javascript {
1.111     www       326:    unless (
1.258     albertel  327:             (($env{'request.course.id'}) && 
1.302     albertel  328:              (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    329: 	      || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    330: 					  '/'.$env{'request.course.sec'})
                    331: 	      ))
1.258     albertel  332:          || ($env{'request.role'}=~/^(au|dc|su)/)
1.111     www       333:           ) { return ''; }  
1.74      www       334:    return (<<'ENDSTDBRW');
                    335: <script type="text/javascript" language="Javascript" >
                    336:     var stdeditbrowser;
1.558     albertel  337:     function openstdbrowser(formname,uname,udom,roleflag,ignorefilter) {
1.74      www       338:         var url = '/adm/pickstudent?';
                    339:         var filter;
1.558     albertel  340: 	if (!ignorefilter) {
                    341: 	    eval('filter=document.'+formname+'.'+uname+'.value;');
                    342: 	}
1.74      www       343:         if (filter != null) {
                    344:            if (filter != '') {
                    345:                url += 'filter='+filter+'&';
                    346: 	   }
                    347:         }
                    348:         url += 'form=' + formname + '&unameelement='+uname+
                    349:                                     '&udomelement='+udom;
1.111     www       350: 	if (roleflag) { url+="&roles=1"; }
1.102     www       351:         var title = 'Student_Browser';
1.74      www       352:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    353:         options += ',width=700,height=600';
                    354:         stdeditbrowser = open(url,title,options,'1');
                    355:         stdeditbrowser.focus();
                    356:     }
                    357: </script>
                    358: ENDSTDBRW
                    359: }
1.42      matthew   360: 
1.74      www       361: sub selectstudent_link {
1.111     www       362:    my ($form,$unameele,$udomele)=@_;
1.258     albertel  363:    if ($env{'request.course.id'}) {  
1.302     albertel  364:        if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    365: 	   && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    366: 					'/'.$env{'request.course.sec'})) {
1.111     www       367: 	   return '';
                    368:        }
                    369:        return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele.
1.558     albertel  370:         '","'.$udomele.'","","1");'."'>".&mt('Select User')."</a>";
1.74      www       371:    }
1.258     albertel  372:    if ($env{'request.role'}=~/^(au|dc|su)/) {
1.111     www       373:        return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele.
1.119     www       374:         '","'.$udomele.'",1);'."'>".&mt('Select User')."</a>";
1.111     www       375:    }
                    376:    return '';
1.91      www       377: }
                    378: 
                    379: sub coursebrowser_javascript {
1.468     raeburn   380:     my ($domainfilter,$sec_element,$formname)=@_;
1.377     raeburn   381:     my $crs_or_grp_alert = &mt('Please select the type of LON-CAPA entity - Course or Group - for which you wish to add/modify a user role');
1.468     raeburn   382:    my $output = '
1.538     albertel  383: <script type="text/javascript">
1.468     raeburn   384:     var stdeditbrowser;'."\n";
                    385:    $output .= <<"ENDSTDBRW";
1.377     raeburn   386:     function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,crstype) {
1.91      www       387:         var url = '/adm/pickcourse?';
1.468     raeburn   388:         var domainfilter = '';
                    389:         var formid = getFormIdByName(formname);
                    390:         if (formid > -1) {
                    391:             var domid = getIndexByName(formid,udom);
                    392:             if (domid > -1) {
                    393:                 if (document.forms[formid].elements[domid].type == 'select-one') {
                    394:                     domainfilter=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
                    395:                 }
                    396:                 if (document.forms[formid].elements[domid].type == 'hidden') {
                    397:                     domainfilter=document.forms[formid].elements[domid].value;
                    398:                 }
                    399:             }
1.91      www       400:         }
1.128     albertel  401:         if (domainfilter != null) {
                    402:            if (domainfilter != '') {
                    403:                url += 'domainfilter='+domainfilter+'&';
                    404: 	   }
                    405:         }
1.91      www       406:         url += 'form=' + formname + '&cnumelement='+uname+
1.187     albertel  407: 	                            '&cdomelement='+udom+
                    408:                                     '&cnameelement='+desc;
1.468     raeburn   409:         if (extra_element !=null && extra_element != '') {
                    410:             if (formname == 'rolechoice') {
                    411:                 url += '&roleelement='+extra_element;
                    412:                 if (domainfilter == null || domainfilter == '') {
                    413:                     url += '&domainfilter='+extra_element;
                    414:                 }
1.234     raeburn   415:             }
1.468     raeburn   416:             else {
                    417:                 if (formname == 'portform') {
                    418:                     url += '&setroles='+extra_element;
                    419:                 }
                    420:             }     
1.230     raeburn   421:         }
1.293     raeburn   422:         if (multflag !=null && multflag != '') {
                    423:             url += '&multiple='+multflag;
                    424:         }
1.377     raeburn   425:         if (crstype == 'Course/Group') {
                    426:             if (formname == 'cu') {
                    427:                 crstype = document.cu.crstype.options[document.cu.crstype.selectedIndex].value; 
                    428:                 if (crstype == "") {
                    429:                     alert("$crs_or_grp_alert");
                    430:                     return;
                    431:                 }
                    432:             }
                    433:         }
                    434:         if (crstype !=null && crstype != '') {
                    435:             url += '&type='+crstype;
                    436:         }
1.102     www       437:         var title = 'Course_Browser';
1.91      www       438:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    439:         options += ',width=700,height=600';
                    440:         stdeditbrowser = open(url,title,options,'1');
                    441:         stdeditbrowser.focus();
                    442:     }
1.468     raeburn   443: 
                    444:     function getFormIdByName(formname) {
                    445:         for (var i=0;i<document.forms.length;i++) {
                    446:             if (document.forms[i].name == formname) {
                    447:                 return i;
                    448:             }
                    449:         }
                    450:         return -1; 
                    451:     }
                    452: 
                    453:     function getIndexByName(formid,item) {
                    454:         for (var i=0;i<document.forms[formid].elements.length;i++) {
                    455:             if (document.forms[formid].elements[i].name == item) {
                    456:                 return i;
                    457:             }
                    458:         }
                    459:         return -1;
                    460:     }
1.91      www       461: ENDSTDBRW
1.468     raeburn   462:     if ($sec_element ne '') {
                    463:         $output .= &setsec_javascript($sec_element,$formname);
                    464:     }
                    465:     $output .= '
                    466: </script>';
                    467:     return $output;
                    468: }
                    469: 
                    470: sub setsec_javascript {
                    471:     my ($sec_element,$formname) = @_;
                    472:     my $setsections = qq|
                    473: function setSect(sectionlist) {
                    474:     var sectionsArray = sectionlist.split(",");
                    475:     var numSections = sectionsArray.length;
                    476:     document.$formname.$sec_element.length = 0;
                    477:     if (numSections == 0) {
                    478:         document.$formname.$sec_element.multiple=false;
                    479:         document.$formname.$sec_element.size=1;
                    480:         document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
                    481:     } else {
                    482:         if (numSections == 1) {
                    483:             document.$formname.$sec_element.multiple=false;
                    484:             document.$formname.$sec_element.size=1;
                    485:             document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
                    486:             document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
                    487:             document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
                    488:         } else {
                    489:             for (var i=0; i<numSections; i++) {
                    490:                 document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
                    491:             }
                    492:             document.$formname.$sec_element.multiple=true
                    493:             if (numSections < 3) {
                    494:                 document.$formname.$sec_element.size=numSections;
                    495:             } else {
                    496:                 document.$formname.$sec_element.size=3;
                    497:             }
                    498:             document.$formname.$sec_element.options[0].selected = false
                    499:         }
                    500:     }
1.91      www       501: }
1.468     raeburn   502: |;
                    503:     return $setsections;
                    504: }
                    505: 
1.91      www       506: 
                    507: sub selectcourse_link {
1.377     raeburn   508:    my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype)=@_;
1.492     albertel  509:    return "<a href='".'javascript:opencrsbrowser("'.$form.'","'.$unameele.
                    510:         '","'.$udomele.'","'.$desc.'","'.$extra_element.'","'.$multflag.'","'.$selecttype.'");'."'>".&mt('Select Course')."</a>";
1.74      www       511: }
1.42      matthew   512: 
1.273     raeburn   513: sub check_uncheck_jscript {
                    514:     my $jscript = <<"ENDSCRT";
                    515: function checkAll(field) {
                    516:     if (field.length > 0) {
                    517:         for (i = 0; i < field.length; i++) {
                    518:             field[i].checked = true ;
                    519:         }
                    520:     } else {
                    521:         field.checked = true
                    522:     }
                    523: }
                    524:  
                    525: function uncheckAll(field) {
                    526:     if (field.length > 0) {
                    527:         for (i = 0; i < field.length; i++) {
                    528:             field[i].checked = false ;
1.543     albertel  529:         }
                    530:     } else {
1.273     raeburn   531:         field.checked = false ;
                    532:     }
                    533: }
                    534: ENDSCRT
                    535:     return $jscript;
                    536: }
                    537: 
                    538: 
1.42      matthew   539: =pod
1.36      matthew   540: 
1.112     bowersj2  541: =item * linked_select_forms(...)
1.36      matthew   542: 
                    543: linked_select_forms returns a string containing a <script></script> block
                    544: and html for two <select> menus.  The select menus will be linked in that
                    545: changing the value of the first menu will result in new values being placed
                    546: in the second menu.  The values in the select menu will appear in alphabetical
                    547: order.
                    548: 
                    549: linked_select_forms takes the following ordered inputs:
                    550: 
                    551: =over 4
                    552: 
1.112     bowersj2  553: =item * $formname, the name of the <form> tag
1.36      matthew   554: 
1.112     bowersj2  555: =item * $middletext, the text which appears between the <select> tags
1.36      matthew   556: 
1.112     bowersj2  557: =item * $firstdefault, the default value for the first menu
1.36      matthew   558: 
1.112     bowersj2  559: =item * $firstselectname, the name of the first <select> tag
1.36      matthew   560: 
1.112     bowersj2  561: =item * $secondselectname, the name of the second <select> tag
1.36      matthew   562: 
1.112     bowersj2  563: =item * $hashref, a reference to a hash containing the data for the menus.
1.36      matthew   564: 
1.41      ng        565: =back 
                    566: 
1.36      matthew   567: Below is an example of such a hash.  Only the 'text', 'default', and 
                    568: 'select2' keys must appear as stated.  keys(%menu) are the possible 
                    569: values for the first select menu.  The text that coincides with the 
1.41      ng        570: first menu value is given in $menu{$choice1}->{'text'}.  The values 
1.36      matthew   571: and text for the second menu are given in the hash pointed to by 
                    572: $menu{$choice1}->{'select2'}.  
                    573: 
1.112     bowersj2  574:  my %menu = ( A1 => { text =>"Choice A1" ,
                    575:                        default => "B3",
                    576:                        select2 => { 
                    577:                            B1 => "Choice B1",
                    578:                            B2 => "Choice B2",
                    579:                            B3 => "Choice B3",
                    580:                            B4 => "Choice B4"
                    581:                            }
                    582:                    },
                    583:                A2 => { text =>"Choice A2" ,
                    584:                        default => "C2",
                    585:                        select2 => { 
                    586:                            C1 => "Choice C1",
                    587:                            C2 => "Choice C2",
                    588:                            C3 => "Choice C3"
                    589:                            }
                    590:                    },
                    591:                A3 => { text =>"Choice A3" ,
                    592:                        default => "D6",
                    593:                        select2 => { 
                    594:                            D1 => "Choice D1",
                    595:                            D2 => "Choice D2",
                    596:                            D3 => "Choice D3",
                    597:                            D4 => "Choice D4",
                    598:                            D5 => "Choice D5",
                    599:                            D6 => "Choice D6",
                    600:                            D7 => "Choice D7"
                    601:                            }
                    602:                    }
                    603:                );
1.36      matthew   604: 
                    605: =cut
                    606: 
                    607: sub linked_select_forms {
                    608:     my ($formname,
                    609:         $middletext,
                    610:         $firstdefault,
                    611:         $firstselectname,
                    612:         $secondselectname, 
                    613:         $hashref
                    614:         ) = @_;
                    615:     my $second = "document.$formname.$secondselectname";
                    616:     my $first = "document.$formname.$firstselectname";
                    617:     # output the javascript to do the changing
                    618:     my $result = '';
1.219     albertel  619:     $result.="<script type=\"text/javascript\">\n";
1.36      matthew   620:     $result.="var select2data = new Object();\n";
                    621:     $" = '","';
                    622:     my $debug = '';
                    623:     foreach my $s1 (sort(keys(%$hashref))) {
                    624:         $result.="select2data.d_$s1 = new Object();\n";        
                    625:         $result.="select2data.d_$s1.def = new String('".
                    626:             $hashref->{$s1}->{'default'}."');\n";
                    627:         $result.="select2data.d_$s1.values = new Array(";        
                    628:         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
                    629:         $result.="\"@s2values\");\n";
                    630:         $result.="select2data.d_$s1.texts = new Array(";        
                    631:         my @s2texts;
                    632:         foreach my $value (@s2values) {
                    633:             push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
                    634:         }
                    635:         $result.="\"@s2texts\");\n";
                    636:     }
                    637:     $"=' ';
                    638:     $result.= <<"END";
                    639: 
                    640: function select1_changed() {
                    641:     // Determine new choice
                    642:     var newvalue = "d_" + $first.value;
                    643:     // update select2
                    644:     var values     = select2data[newvalue].values;
                    645:     var texts      = select2data[newvalue].texts;
                    646:     var select2def = select2data[newvalue].def;
                    647:     var i;
                    648:     // out with the old
                    649:     for (i = 0; i < $second.options.length; i++) {
                    650:         $second.options[i] = null;
                    651:     }
                    652:     // in with the nuclear
                    653:     for (i=0;i<values.length; i++) {
                    654:         $second.options[i] = new Option(values[i]);
1.143     matthew   655:         $second.options[i].value = values[i];
1.36      matthew   656:         $second.options[i].text = texts[i];
                    657:         if (values[i] == select2def) {
                    658:             $second.options[i].selected = true;
                    659:         }
                    660:     }
                    661: }
                    662: </script>
                    663: END
                    664:     # output the initial values for the selection lists
                    665:     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed()\">\n";
                    666:     foreach my $value (sort(keys(%$hashref))) {
                    667:         $result.="    <option value=\"$value\" ";
1.253     albertel  668:         $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119     www       669:         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36      matthew   670:     }
                    671:     $result .= "</select>\n";
                    672:     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
                    673:     $result .= $middletext;
                    674:     $result .= "<select size=\"1\" name=\"$secondselectname\">\n";
                    675:     my $seconddefault = $hashref->{$firstdefault}->{'default'};
                    676:     foreach my $value (sort(keys(%select2))) {
                    677:         $result.="    <option value=\"$value\" ";        
1.253     albertel  678:         $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119     www       679:         $result.=">".&mt($select2{$value})."</option>\n";
1.36      matthew   680:     }
                    681:     $result .= "</select>\n";
                    682:     #    return $debug;
                    683:     return $result;
                    684: }   #  end of sub linked_select_forms {
                    685: 
1.45      matthew   686: =pod
1.44      bowersj2  687: 
1.112     bowersj2  688: =item * help_open_topic($topic, $text, $stayOnPage, $width, $height)
1.44      bowersj2  689: 
1.112     bowersj2  690: Returns a string corresponding to an HTML link to the given help
                    691: $topic, where $topic corresponds to the name of a .tex file in
                    692: /home/httpd/html/adm/help/tex, with underscores replaced by
                    693: spaces. 
                    694: 
                    695: $text will optionally be linked to the same topic, allowing you to
                    696: link text in addition to the graphic. If you do not want to link
                    697: text, but wish to specify one of the later parameters, pass an
                    698: empty string. 
                    699: 
                    700: $stayOnPage is a value that will be interpreted as a boolean. If true,
                    701: the link will not open a new window. If false, the link will open
                    702: a new window using Javascript. (Default is false.) 
                    703: 
                    704: $width and $height are optional numerical parameters that will
                    705: override the width and height of the popped up window, which may
                    706: be useful for certain help topics with big pictures included. 
1.44      bowersj2  707: 
                    708: =cut
                    709: 
                    710: sub help_open_topic {
1.48      bowersj2  711:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
                    712:     $text = "" if (not defined $text);
1.44      bowersj2  713:     $stayOnPage = 0 if (not defined $stayOnPage);
1.552     banghart  714:     if ($env{'browser.interface'} eq 'textual') {
1.79      www       715: 	$stayOnPage=1;
                    716:     }
1.44      bowersj2  717:     $width = 350 if (not defined $width);
                    718:     $height = 400 if (not defined $height);
                    719:     my $filename = $topic;
                    720:     $filename =~ s/ /_/g;
                    721: 
1.48      bowersj2  722:     my $template = "";
                    723:     my $link;
1.572     banghart  724:     
1.159     www       725:     $topic=~s/\W/\_/g;
1.44      bowersj2  726: 
1.572     banghart  727:     if (!$stayOnPage) {
1.72      bowersj2  728: 	$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  729:     } else {
1.48      bowersj2  730: 	$link = "/adm/help/${filename}.hlp";
                    731:     }
                    732: 
                    733:     # Add the text
1.572     banghart  734:     if ($text ne "") {
1.77      www       735: 	$template .= 
1.572     banghart  736:             "<table bgcolor='#3333AA' cellspacing='1' cellpadding='1' border='0'><tr>".
                    737:             "<td bgcolor='#5555FF'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.48      bowersj2  738:     }
                    739: 
                    740:     # Add the graphic
1.179     matthew   741:     my $title = &mt('Online Help');
1.215     albertel  742:     my $helpicon=&lonhttpdurl("/adm/help/gif/smallHelp.gif");
1.48      bowersj2  743:     $template .= <<"ENDTEMPLATE";
1.436     albertel  744:  <a target="_top" href="$link" title="$title"><img src="$helpicon" border="0" alt="(Help: $topic)" /></a>
1.44      bowersj2  745: ENDTEMPLATE
1.78      www       746:     if ($text ne '') { $template.='</td></tr></table>' };
1.44      bowersj2  747:     return $template;
                    748: 
1.106     bowersj2  749: }
                    750: 
                    751: # This is a quicky function for Latex cheatsheet editing, since it 
                    752: # appears in at least four places
                    753: sub helpLatexCheatsheet {
                    754:     my $other = shift;
                    755:     my $addOther = '';
                    756:     if ($other) {
                    757: 	$addOther = Apache::loncommon::help_open_topic($other, shift,
                    758: 						       undef, undef, 600) .
                    759: 							   '</td><td>';
                    760:     }
                    761:     return '<table><tr><td>'.
                    762: 	$addOther .
                    763: 	&Apache::loncommon::help_open_topic("Greek_Symbols",'Greek Symbols',
                    764: 					    undef,undef,600)
                    765: 	.'</td><td>'.
                    766: 	&Apache::loncommon::help_open_topic("Other_Symbols",'Other Symbols',
                    767: 					    undef,undef,600)
                    768: 	.'</td></tr></table>';
1.172     www       769: }
                    770: 
1.430     albertel  771: sub general_help {
                    772:     my $helptopic='Student_Intro';
                    773:     if ($env{'request.role'}=~/^(ca|au)/) {
                    774: 	$helptopic='Authoring_Intro';
                    775:     } elsif ($env{'request.role'}=~/^cc/) {
                    776: 	$helptopic='Course_Coordination_Intro';
                    777:     }
                    778:     return $helptopic;
                    779: }
                    780: 
                    781: sub update_help_link {
                    782:     my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
                    783:     my $origurl = $ENV{'REQUEST_URI'};
                    784:     $origurl=~s|^/~|/priv/|;
                    785:     my $timestamp = time;
                    786:     foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
                    787:         $$datum = &escape($$datum);
                    788:     }
                    789: 
                    790:     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";
                    791:     my $output .= <<"ENDOUTPUT";
                    792: <script type="text/javascript">
                    793: banner_link = '$banner_link';
                    794: </script>
                    795: ENDOUTPUT
                    796:     return $output;
                    797: }
                    798: 
                    799: # now just updates the help link and generates a blue icon
1.193     raeburn   800: sub help_open_menu {
1.430     albertel  801:     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text) 
1.552     banghart  802: 	= @_;    
1.430     albertel  803:     $stayOnPage = 0 if (not defined $stayOnPage);
1.572     banghart  804:     # only use pop-up help (stayOnPage == 0)
1.552     banghart  805:     # if environment.remote is on (using remote control UI)
1.572     banghart  806:     if ($env{'browser.interface'} eq 'textual' ||
                    807:     	$env{'environment.remote'} eq 'off' ) {
1.552     banghart  808:         $stayOnPage=1;
1.430     albertel  809:     }
                    810:     my $output;
                    811:     if ($component_help) {
                    812: 	if (!$text) {
                    813: 	    $output=&help_open_topic($component_help,undef,$stayOnPage,
                    814: 				       $width,$height);
                    815: 	} else {
                    816: 	    my $help_text;
                    817: 	    $help_text=&unescape($topic);
                    818: 	    $output='<table><tr><td>'.
                    819: 		&help_open_topic($component_help,$help_text,$stayOnPage,
                    820: 				 $width,$height).'</td></tr></table>';
                    821: 	}
                    822:     }
                    823:     my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
                    824:     return $output.$banner_link;
                    825: }
                    826: 
                    827: sub top_nav_help {
                    828:     my ($text) = @_;
1.436     albertel  829:     $text = &mt($text);
1.572     banghart  830:     my $stay_on_page = 
1.436     albertel  831: 	($env{'browser.interface'}  eq 'textual' ||
                    832: 	 $env{'environment.remote'} eq 'off' );
1.572     banghart  833:     my $link = ($stay_on_page) ? "javascript:helpMenu('display')"
1.436     albertel  834: 	                     : "javascript:helpMenu('open')";
1.572     banghart  835:     my $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
1.436     albertel  836: 
1.201     raeburn   837:     my $title = &mt('Get help');
1.436     albertel  838: 
                    839:     return <<"END";
                    840: $banner_link
                    841:  <a href="$link" title="$title">$text</a>
                    842: END
                    843: }
                    844: 
                    845: sub help_menu_js {
                    846:     my ($text) = @_;
                    847: 
                    848:     my $stayOnPage = 
                    849: 	($env{'browser.interface'}  eq 'textual' ||
                    850: 	 $env{'environment.remote'} eq 'off' );
                    851: 
                    852:     my $width = 620;
                    853:     my $height = 600;
1.430     albertel  854:     my $helptopic=&general_help();
                    855:     my $details_link = '/adm/help/'.$helptopic.'.hlp';
1.261     albertel  856:     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331     albertel  857:     my $start_page =
                    858:         &Apache::loncommon::start_page('Help Menu', undef,
                    859: 				       {'frameset'    => 1,
                    860: 					'js_ready'    => 1,
                    861: 					'add_entries' => {
                    862: 					    'border' => '0',
1.579     raeburn   863: 					    'rows'   => "110,*",},});
1.331     albertel  864:     my $end_page =
                    865:         &Apache::loncommon::end_page({'frameset' => 1,
                    866: 				      'js_ready' => 1,});
                    867: 
1.436     albertel  868:     my $template .= <<"ENDTEMPLATE";
                    869: <script type="text/javascript">
1.253     albertel  870: // <!-- BEGIN LON-CAPA Internal
                    871: // <![CDATA[
1.430     albertel  872: var banner_link = '';
1.243     raeburn   873: function helpMenu(target) {
                    874:     var caller = this;
                    875:     if (target == 'open') {
                    876:         var newWindow = null;
                    877:         try {
1.262     albertel  878:             newWindow =  window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243     raeburn   879:         }
                    880:         catch(error) {
                    881:             writeHelp(caller);
                    882:             return;
                    883:         }
                    884:         if (newWindow) {
                    885:             caller = newWindow;
                    886:         }
1.193     raeburn   887:     }
1.243     raeburn   888:     writeHelp(caller);
                    889:     return;
                    890: }
                    891: function writeHelp(caller) {
1.430     albertel  892:     caller.document.writeln('$start_page<frame name="bannerframe"  src="'+banner_link+'" /><frame name="bodyframe" src="$details_link" /> $end_page')
1.243     raeburn   893:     caller.document.close()
                    894:     caller.focus()
1.193     raeburn   895: }
1.253     albertel  896: // ]]>
1.219     albertel  897: // END LON-CAPA Internal -->
1.436     albertel  898: </script>
1.193     raeburn   899: ENDTEMPLATE
                    900:     return $template;
                    901: }
                    902: 
1.172     www       903: sub help_open_bug {
                    904:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel  905:     unless ($env{'user.adv'}) { return ''; }
1.172     www       906:     unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
                    907:     $text = "" if (not defined $text);
                    908:     $stayOnPage = 0 if (not defined $stayOnPage);
1.258     albertel  909:     if ($env{'browser.interface'} eq 'textual' ||
                    910: 	$env{'environment.remote'} eq 'off' ) {
1.172     www       911: 	$stayOnPage=1;
                    912:     }
1.184     albertel  913:     $width = 600 if (not defined $width);
                    914:     $height = 600 if (not defined $height);
1.172     www       915: 
                    916:     $topic=~s/\W+/\+/g;
                    917:     my $link='';
                    918:     my $template='';
1.379     albertel  919:     my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&amp;bug_file_loc='.
                    920: 	&escape($ENV{'REQUEST_URI'}).'&amp;component='.$topic;
1.172     www       921:     if (!$stayOnPage)
                    922:     {
                    923: 	$link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                    924:     }
                    925:     else
                    926:     {
                    927: 	$link = $url;
                    928:     }
                    929:     # Add the text
                    930:     if ($text ne "")
                    931:     {
                    932: 	$template .= 
                    933:   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436     albertel  934:   "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.172     www       935:     }
                    936: 
                    937:     # Add the graphic
1.179     matthew   938:     my $title = &mt('Report a Bug');
1.215     albertel  939:     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172     www       940:     $template .= <<"ENDTEMPLATE";
1.436     albertel  941:  <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172     www       942: ENDTEMPLATE
                    943:     if ($text ne '') { $template.='</td></tr></table>' };
                    944:     return $template;
                    945: 
                    946: }
                    947: 
                    948: sub help_open_faq {
                    949:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel  950:     unless ($env{'user.adv'}) { return ''; }
1.172     www       951:     unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
                    952:     $text = "" if (not defined $text);
                    953:     $stayOnPage = 0 if (not defined $stayOnPage);
1.258     albertel  954:     if ($env{'browser.interface'} eq 'textual' ||
                    955: 	$env{'environment.remote'} eq 'off' ) {
1.172     www       956: 	$stayOnPage=1;
                    957:     }
                    958:     $width = 350 if (not defined $width);
                    959:     $height = 400 if (not defined $height);
                    960: 
                    961:     $topic=~s/\W+/\+/g;
                    962:     my $link='';
                    963:     my $template='';
                    964:     my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
                    965:     if (!$stayOnPage)
                    966:     {
                    967: 	$link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                    968:     }
                    969:     else
                    970:     {
                    971: 	$link = $url;
                    972:     }
                    973: 
                    974:     # Add the text
                    975:     if ($text ne "")
                    976:     {
                    977: 	$template .= 
1.173     www       978:   "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.436     albertel  979:   "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
1.172     www       980:     }
                    981: 
                    982:     # Add the graphic
1.179     matthew   983:     my $title = &mt('View the FAQ');
1.215     albertel  984:     my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172     www       985:     $template .= <<"ENDTEMPLATE";
1.436     albertel  986:  <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172     www       987: ENDTEMPLATE
                    988:     if ($text ne '') { $template.='</td></tr></table>' };
                    989:     return $template;
                    990: 
1.44      bowersj2  991: }
1.37      matthew   992: 
1.180     matthew   993: ###############################################################
                    994: ###############################################################
                    995: 
1.45      matthew   996: =pod
                    997: 
1.256     matthew   998: =item * change_content_javascript():
                    999: 
                   1000: This and the next function allow you to create small sections of an
                   1001: otherwise static HTML page that you can update on the fly with
                   1002: Javascript, even in Netscape 4.
                   1003: 
                   1004: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
                   1005: must be written to the HTML page once. It will prove the Javascript
                   1006: function "change(name, content)". Calling the change function with the
                   1007: name of the section 
                   1008: you want to update, matching the name passed to C<changable_area>, and
                   1009: the new content you want to put in there, will put the content into
                   1010: that area.
                   1011: 
                   1012: B<Note>: Netscape 4 only reserves enough space for the changable area
                   1013: to contain room for the original contents. You need to "make space"
                   1014: for whatever changes you wish to make, and be B<sure> to check your
                   1015: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
                   1016: it's adequate for updating a one-line status display, but little more.
                   1017: This script will set the space to 100% width, so you only need to
                   1018: worry about height in Netscape 4.
                   1019: 
                   1020: Modern browsers are much less limiting, and if you can commit to the
                   1021: user not using Netscape 4, this feature may be used freely with
                   1022: pretty much any HTML.
                   1023: 
                   1024: =cut
                   1025: 
                   1026: sub change_content_javascript {
                   1027:     # If we're on Netscape 4, we need to use Layer-based code
1.258     albertel 1028:     if ($env{'browser.type'} eq 'netscape' &&
                   1029: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1030: 	return (<<NETSCAPE4);
                   1031: 	function change(name, content) {
                   1032: 	    doc = document.layers[name+"___escape"].layers[0].document;
                   1033: 	    doc.open();
                   1034: 	    doc.write(content);
                   1035: 	    doc.close();
                   1036: 	}
                   1037: NETSCAPE4
                   1038:     } else {
                   1039: 	# Otherwise, we need to use semi-standards-compliant code
                   1040: 	# (technically, "innerHTML" isn't standard but the equivalent
                   1041: 	# is really scary, and every useful browser supports it
                   1042: 	return (<<DOMBASED);
                   1043: 	function change(name, content) {
                   1044: 	    element = document.getElementById(name);
                   1045: 	    element.innerHTML = content;
                   1046: 	}
                   1047: DOMBASED
                   1048:     }
                   1049: }
                   1050: 
                   1051: =pod
                   1052: 
                   1053: =item * changable_area($name, $origContent):
                   1054: 
                   1055: This provides a "changable area" that can be modified on the fly via
                   1056: the Javascript code provided in C<change_content_javascript>. $name is
                   1057: the name you will use to reference the area later; do not repeat the
                   1058: same name on a given HTML page more then once. $origContent is what
                   1059: the area will originally contain, which can be left blank.
                   1060: 
                   1061: =cut
                   1062: 
                   1063: sub changable_area {
                   1064:     my ($name, $origContent) = @_;
                   1065: 
1.258     albertel 1066:     if ($env{'browser.type'} eq 'netscape' &&
                   1067: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1068: 	# If this is netscape 4, we need to use the Layer tag
                   1069: 	return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
                   1070:     } else {
                   1071: 	return "<span id='$name'>$origContent</span>";
                   1072:     }
                   1073: }
                   1074: 
                   1075: =pod
                   1076: 
1.590   ! raeburn  1077: =item * viewport_geometry_js {
        !          1078: 
        !          1079: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
        !          1080: 
        !          1081: =cut
        !          1082: 
        !          1083: 
        !          1084: sub viewport_geometry_js { 
        !          1085:     return <<"GEOMETRY";
        !          1086: var Geometry = {};
        !          1087: function init_geometry() {
        !          1088:     if (Geometry.init) { return };
        !          1089:     Geometry.init=1;
        !          1090:     if (window.innerHeight) {
        !          1091:         Geometry.getViewportHeight   = function() { return window.innerHeight; };
        !          1092:         Geometry.getViewportWidth   = function() { return window.innerWidth; };
        !          1093:         Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
        !          1094:         Geometry.getVerticalScroll   = function() { return window.pageYOffset; };
        !          1095:     }
        !          1096:     else if (document.documentElement && document.documentElement.clientHeight) {
        !          1097:         Geometry.getViewportHeight =
        !          1098:             function() { return document.documentElement.clientHeight; };
        !          1099:         Geometry.getViewportWidth =
        !          1100:             function() { return document.documentElement.clientWidth; };
        !          1101: 
        !          1102:         Geometry.getHorizontalScroll =
        !          1103:             function() { return document.documentElement.scrollLeft; };
        !          1104:         Geometry.getVerticalScroll =
        !          1105:             function() { return document.documentElement.scrollTop; };
        !          1106:     }
        !          1107:     else if (document.body.clientHeight) {
        !          1108:         Geometry.getViewportHeight =
        !          1109:             function() { return document.body.clientHeight; };
        !          1110:         Geometry.getViewportWidth =
        !          1111:             function() { return document.body.clientWidth; };
        !          1112:         Geometry.getHorizontalScroll =
        !          1113:             function() { return document.body.scrollLeft; };
        !          1114:         Geometry.getVerticalScroll =
        !          1115:             function() { return document.body.scrollTop; };
        !          1116:     }
        !          1117: }
        !          1118: 
        !          1119: GEOMETRY
        !          1120: }
        !          1121: 
        !          1122: =pod
        !          1123: 
        !          1124: =item * viewport_size_js {
        !          1125: 
        !          1126: 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. 
        !          1127: 
        !          1128: =cut
        !          1129: 
        !          1130: sub viewport_size_js {
        !          1131:     my $geometry = &viewport_geometry_js();
        !          1132:     return <<"DIMS";
        !          1133: 
        !          1134: $geometry
        !          1135: 
        !          1136: function getViewportDims(width,height) {
        !          1137:     init_geometry();
        !          1138:     width.value = Geometry.getViewportWidth();
        !          1139:     height.value = Geometry.getViewportHeight();
        !          1140:     return;
        !          1141: }
        !          1142: 
        !          1143: DIMS
        !          1144: }
        !          1145: 
        !          1146: =pod
        !          1147: 
1.565     albertel 1148: =item * resize_textarea_js
                   1149: 
                   1150: emits the needed javascript to resize a textarea to be as big as possible
                   1151: 
                   1152: creates a function resize_textrea that takes two IDs first should be
                   1153: the id of the element to resize, second should be the id of a div that
                   1154: surrounds everything that comes after the textarea, this routine needs
                   1155: to be attached to the <body> for the onload and onresize events.
                   1156: 
                   1157: 
                   1158: =cut
                   1159: 
                   1160: sub resize_textarea_js {
1.590   ! raeburn  1161:     my $geometry = &viewport_geometry_js();
1.565     albertel 1162:     return <<"RESIZE";
                   1163:     <script type="text/javascript">
1.590   ! raeburn  1164: $geometry
1.565     albertel 1165: 
1.588     albertel 1166: function getX(element) {
                   1167:     var x = 0;
                   1168:     while (element) {
                   1169: 	x += element.offsetLeft;
                   1170: 	element = element.offsetParent;
                   1171:     }
                   1172:     return x;
                   1173: }
                   1174: function getY(element) {
                   1175:     var y = 0;
                   1176:     while (element) {
                   1177: 	y += element.offsetTop;
                   1178: 	element = element.offsetParent;
                   1179:     }
                   1180:     return y;
                   1181: }
                   1182: 
                   1183: 
1.565     albertel 1184: function resize_textarea(textarea_id,bottom_id) {
                   1185:     init_geometry();
                   1186:     var textarea        = document.getElementById(textarea_id);
                   1187:     //alert(textarea);
                   1188: 
1.588     albertel 1189:     var textarea_top    = getY(textarea);
1.565     albertel 1190:     var textarea_height = textarea.offsetHeight;
                   1191:     var bottom          = document.getElementById(bottom_id);
1.588     albertel 1192:     var bottom_top      = getY(bottom);
1.565     albertel 1193:     var bottom_height   = bottom.offsetHeight;
                   1194:     var window_height   = Geometry.getViewportHeight();
1.588     albertel 1195:     var fudge           = 23;
1.565     albertel 1196:     var new_height      = window_height-fudge-textarea_top-bottom_height;
                   1197:     if (new_height < 300) {
                   1198: 	new_height = 300;
                   1199:     }
                   1200:     textarea.style.height=new_height+'px';
                   1201: }
                   1202: </script>
                   1203: RESIZE
                   1204: 
                   1205: }
                   1206: 
                   1207: =pod
                   1208: 
1.256     matthew  1209: =back
1.542     raeburn  1210:  
1.256     matthew  1211: =head1 Excel and CSV file utility routines
                   1212: 
                   1213: =over 4
                   1214: 
                   1215: =cut
                   1216: 
                   1217: ###############################################################
                   1218: ###############################################################
                   1219: 
                   1220: =pod
                   1221: 
1.112     bowersj2 1222: =item * csv_translate($text) 
1.37      matthew  1223: 
1.185     www      1224: Translate $text to allow it to be output as a 'comma separated values' 
1.37      matthew  1225: format.
                   1226: 
                   1227: =cut
                   1228: 
1.180     matthew  1229: ###############################################################
                   1230: ###############################################################
1.37      matthew  1231: sub csv_translate {
                   1232:     my $text = shift;
                   1233:     $text =~ s/\"/\"\"/g;
1.209     albertel 1234:     $text =~ s/\n/ /g;
1.37      matthew  1235:     return $text;
                   1236: }
1.180     matthew  1237: 
                   1238: ###############################################################
                   1239: ###############################################################
                   1240: 
                   1241: =pod
                   1242: 
                   1243: =item * define_excel_formats
                   1244: 
                   1245: Define some commonly used Excel cell formats.
                   1246: 
                   1247: Currently supported formats:
                   1248: 
                   1249: =over 4
                   1250: 
                   1251: =item header
                   1252: 
                   1253: =item bold
                   1254: 
                   1255: =item h1
                   1256: 
                   1257: =item h2
                   1258: 
                   1259: =item h3
                   1260: 
1.256     matthew  1261: =item h4
                   1262: 
                   1263: =item i
                   1264: 
1.180     matthew  1265: =item date
                   1266: 
                   1267: =back
                   1268: 
                   1269: Inputs: $workbook
                   1270: 
                   1271: Returns: $format, a hash reference.
                   1272: 
                   1273: =cut
                   1274: 
                   1275: ###############################################################
                   1276: ###############################################################
                   1277: sub define_excel_formats {
                   1278:     my ($workbook) = @_;
                   1279:     my $format;
                   1280:     $format->{'header'} = $workbook->add_format(bold      => 1, 
                   1281:                                                 bottom    => 1,
                   1282:                                                 align     => 'center');
                   1283:     $format->{'bold'} = $workbook->add_format(bold=>1);
                   1284:     $format->{'h1'}   = $workbook->add_format(bold=>1, size=>18);
                   1285:     $format->{'h2'}   = $workbook->add_format(bold=>1, size=>16);
                   1286:     $format->{'h3'}   = $workbook->add_format(bold=>1, size=>14);
1.255     matthew  1287:     $format->{'h4'}   = $workbook->add_format(bold=>1, size=>12);
1.246     matthew  1288:     $format->{'i'}    = $workbook->add_format(italic=>1);
1.180     matthew  1289:     $format->{'date'} = $workbook->add_format(num_format=>
1.207     matthew  1290:                                             'mm/dd/yyyy hh:mm:ss');
1.180     matthew  1291:     return $format;
                   1292: }
                   1293: 
                   1294: ###############################################################
                   1295: ###############################################################
1.113     bowersj2 1296: 
                   1297: =pod
                   1298: 
1.256     matthew  1299: =item * create_workbook
1.255     matthew  1300: 
                   1301: Create an Excel worksheet.  If it fails, output message on the
                   1302: request object and return undefs.
                   1303: 
                   1304: Inputs: Apache request object
                   1305: 
                   1306: Returns (undef) on failure, 
                   1307:     Excel worksheet object, scalar with filename, and formats 
                   1308:     from &Apache::loncommon::define_excel_formats on success
                   1309: 
                   1310: =cut
                   1311: 
                   1312: ###############################################################
                   1313: ###############################################################
                   1314: sub create_workbook {
                   1315:     my ($r) = @_;
                   1316:         #
                   1317:     # Create the excel spreadsheet
                   1318:     my $filename = '/prtspool/'.
1.258     albertel 1319:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255     matthew  1320:         time.'_'.rand(1000000000).'.xls';
                   1321:     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
                   1322:     if (! defined($workbook)) {
                   1323:         $r->log_error("Error creating excel spreadsheet $filename: $!");
                   1324:         $r->print('<p>'.&mt("Unable to create new Excel file.  ".
                   1325:                             "This error has been logged.  ".
                   1326:                             "Please alert your LON-CAPA administrator").
                   1327:                   '</p>');
                   1328:         return (undef);
                   1329:     }
                   1330:     #
                   1331:     $workbook->set_tempdir('/home/httpd/perl/tmp');
                   1332:     #
                   1333:     my $format = &Apache::loncommon::define_excel_formats($workbook);
                   1334:     return ($workbook,$filename,$format);
                   1335: }
                   1336: 
                   1337: ###############################################################
                   1338: ###############################################################
                   1339: 
                   1340: =pod
                   1341: 
1.256     matthew  1342: =item * create_text_file
1.113     bowersj2 1343: 
1.542     raeburn  1344: Create a file to write to and eventually make available to the user.
1.256     matthew  1345: If file creation fails, outputs an error message on the request object and 
                   1346: return undefs.
1.113     bowersj2 1347: 
1.256     matthew  1348: Inputs: Apache request object, and file suffix
1.113     bowersj2 1349: 
1.256     matthew  1350: Returns (undef) on failure, 
                   1351:     Filehandle and filename on success.
1.113     bowersj2 1352: 
                   1353: =cut
                   1354: 
1.256     matthew  1355: ###############################################################
                   1356: ###############################################################
                   1357: sub create_text_file {
                   1358:     my ($r,$suffix) = @_;
                   1359:     if (! defined($suffix)) { $suffix = 'txt'; };
                   1360:     my $fh;
                   1361:     my $filename = '/prtspool/'.
1.258     albertel 1362:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256     matthew  1363:         time.'_'.rand(1000000000).'.'.$suffix;
                   1364:     $fh = Apache::File->new('>/home/httpd'.$filename);
                   1365:     if (! defined($fh)) {
                   1366:         $r->log_error("Couldn't open $filename for output $!");
                   1367:         $r->print("Problems occured in creating the output file.  ".
                   1368:                   "This error has been logged.  ".
                   1369:                   "Please alert your LON-CAPA administrator.");
1.113     bowersj2 1370:     }
1.256     matthew  1371:     return ($fh,$filename)
1.113     bowersj2 1372: }
                   1373: 
                   1374: 
1.256     matthew  1375: =pod 
1.113     bowersj2 1376: 
                   1377: =back
                   1378: 
                   1379: =cut
1.37      matthew  1380: 
                   1381: ###############################################################
1.33      matthew  1382: ##        Home server <option> list generating code          ##
                   1383: ###############################################################
1.35      matthew  1384: 
1.169     www      1385: # ------------------------------------------
                   1386: 
                   1387: sub domain_select {
                   1388:     my ($name,$value,$multiple)=@_;
                   1389:     my %domains=map { 
1.514     albertel 1390: 	$_ => $_.' '. &Apache::lonnet::domain($_,'description') 
1.512     albertel 1391:     } &Apache::lonnet::all_domains();
1.169     www      1392:     if ($multiple) {
                   1393: 	$domains{''}=&mt('Any domain');
1.550     albertel 1394: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287     albertel 1395: 	return &multiple_select_form($name,$value,4,\%domains);
1.169     www      1396:     } else {
1.550     albertel 1397: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.169     www      1398: 	return &select_form($name,$value,%domains);
                   1399:     }
                   1400: }
                   1401: 
1.282     albertel 1402: #-------------------------------------------
                   1403: 
                   1404: =pod
                   1405: 
1.519     raeburn  1406: =head1 Routines for form select boxes
                   1407: 
                   1408: =over 4
                   1409: 
1.287     albertel 1410: =item * multiple_select_form($name,$value,$size,$hash,$order)
1.282     albertel 1411: 
                   1412: Returns a string containing a <select> element int multiple mode
                   1413: 
                   1414: 
                   1415: Args:
                   1416:   $name - name of the <select> element
1.506     raeburn  1417:   $value - scalar or array ref of values that should already be selected
1.282     albertel 1418:   $size - number of rows long the select element is
1.283     albertel 1419:   $hash - the elements should be 'option' => 'shown text'
1.282     albertel 1420:           (shown text should already have been &mt())
1.506     raeburn  1421:   $order - (optional) array ref of the order to show the elements in
1.283     albertel 1422: 
1.282     albertel 1423: =cut
                   1424: 
                   1425: #-------------------------------------------
1.169     www      1426: sub multiple_select_form {
1.284     albertel 1427:     my ($name,$value,$size,$hash,$order)=@_;
1.169     www      1428:     my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
                   1429:     my $output='';
1.191     matthew  1430:     if (! defined($size)) {
                   1431:         $size = 4;
1.283     albertel 1432:         if (scalar(keys(%$hash))<4) {
                   1433:             $size = scalar(keys(%$hash));
1.191     matthew  1434:         }
                   1435:     }
1.169     www      1436:     $output.="\n<select name='$name' size='$size' multiple='1'>";
1.501     banghart 1437:     my @order;
1.506     raeburn  1438:     if (ref($order) eq 'ARRAY')  {
                   1439:         @order = @{$order};
                   1440:     } else {
                   1441:         @order = sort(keys(%$hash));
1.501     banghart 1442:     }
                   1443:     if (exists($$hash{'select_form_order'})) {
                   1444:         @order = @{$$hash{'select_form_order'}};
                   1445:     }
                   1446:         
1.284     albertel 1447:     foreach my $key (@order) {
1.356     albertel 1448:         $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284     albertel 1449:         $output.='selected="selected" ' if ($selected{$key});
                   1450:         $output.='>'.$hash->{$key}."</option>\n";
1.169     www      1451:     }
                   1452:     $output.="</select>\n";
                   1453:     return $output;
                   1454: }
                   1455: 
1.88      www      1456: #-------------------------------------------
                   1457: 
                   1458: =pod
                   1459: 
1.112     bowersj2 1460: =item * select_form($defdom,$name,%hash)
1.88      www      1461: 
                   1462: Returns a string containing a <select name='$name' size='1'> form to 
                   1463: allow a user to select options from a hash option_name => displayed text.  
                   1464: See lonrights.pm for an example invocation and use.
                   1465: 
                   1466: =cut
                   1467: 
                   1468: #-------------------------------------------
                   1469: sub select_form {
                   1470:     my ($def,$name,%hash) = @_;
                   1471:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
1.128     albertel 1472:     my @keys;
                   1473:     if (exists($hash{'select_form_order'})) {
                   1474: 	@keys=@{$hash{'select_form_order'}};
                   1475:     } else {
                   1476: 	@keys=sort(keys(%hash));
                   1477:     }
1.356     albertel 1478:     foreach my $key (@keys) {
                   1479:         $selectform.=
                   1480: 	    '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
                   1481:             ($key eq $def ? 'selected="selected" ' : '').
                   1482:                 ">".&mt($hash{$key})."</option>\n";
1.88      www      1483:     }
                   1484:     $selectform.="</select>";
                   1485:     return $selectform;
                   1486: }
                   1487: 
1.475     www      1488: # For display filters
                   1489: 
                   1490: sub display_filter {
                   1491:     if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477     www      1492:     if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.475     www      1493:     return '<nobr><label>'.&mt('Records [_1]',
                   1494: 			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
                   1495: 							   (&mt('all'),10,20,50,100,1000,10000))).
1.478     www      1496: 	   '</label></nobr> <nobr>'.
1.475     www      1497:            &mt('Filter [_1]',
1.477     www      1498: 	   &select_form($env{'form.displayfilter'},
                   1499: 			'displayfilter',
                   1500: 			('currentfolder' => 'Current folder/page',
                   1501: 			 'containing' => 'Containing phrase',
                   1502: 			 'none' => 'None'))).
1.478     www      1503: 			 '<input type="text" name="containingphrase" size="30" value="'.&HTML::Entities::encode($env{'form.containingphrase'}).'" /></nobr>';
1.475     www      1504: }
                   1505: 
1.167     www      1506: sub gradeleveldescription {
                   1507:     my $gradelevel=shift;
                   1508:     my %gradelevels=(0 => 'Not specified',
                   1509: 		     1 => 'Grade 1',
                   1510: 		     2 => 'Grade 2',
                   1511: 		     3 => 'Grade 3',
                   1512: 		     4 => 'Grade 4',
                   1513: 		     5 => 'Grade 5',
                   1514: 		     6 => 'Grade 6',
                   1515: 		     7 => 'Grade 7',
                   1516: 		     8 => 'Grade 8',
                   1517: 		     9 => 'Grade 9',
                   1518: 		     10 => 'Grade 10',
                   1519: 		     11 => 'Grade 11',
                   1520: 		     12 => 'Grade 12',
                   1521: 		     13 => 'Grade 13',
                   1522: 		     14 => '100 Level',
                   1523: 		     15 => '200 Level',
                   1524: 		     16 => '300 Level',
                   1525: 		     17 => '400 Level',
                   1526: 		     18 => 'Graduate Level');
                   1527:     return &mt($gradelevels{$gradelevel});
                   1528: }
                   1529: 
1.163     www      1530: sub select_level_form {
                   1531:     my ($deflevel,$name)=@_;
                   1532:     unless ($deflevel) { $deflevel=0; }
1.167     www      1533:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
                   1534:     for (my $i=0; $i<=18; $i++) {
                   1535:         $selectform.="<option value=\"$i\" ".
1.253     albertel 1536:             ($i==$deflevel ? 'selected="selected" ' : '').
1.167     www      1537:                 ">".&gradeleveldescription($i)."</option>\n";
                   1538:     }
                   1539:     $selectform.="</select>";
                   1540:     return $selectform;
1.163     www      1541: }
1.167     www      1542: 
1.35      matthew  1543: #-------------------------------------------
                   1544: 
1.45      matthew  1545: =pod
                   1546: 
1.563     raeburn  1547: =item * select_dom_form($defdom,$name,$includeempty,$showdomdesc)
1.35      matthew  1548: 
                   1549: Returns a string containing a <select name='$name' size='1'> form to 
                   1550: allow a user to select the domain to preform an operation in.  
                   1551: See loncreateuser.pm for an example invocation and use.
                   1552: 
1.90      www      1553: If the $includeempty flag is set, it also includes an empty choice ("no domain
                   1554: selected");
                   1555: 
1.563     raeburn  1556: If the $showdomdesc flag is set, the domain name is followed by the domain description. 
                   1557: 
1.35      matthew  1558: =cut
                   1559: 
                   1560: #-------------------------------------------
1.34      matthew  1561: sub select_dom_form {
1.563     raeburn  1562:     my ($defdom,$name,$includeempty,$showdomdesc) = @_;
1.550     albertel 1563:     my @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
1.90      www      1564:     if ($includeempty) { @domains=('',@domains); }
1.34      matthew  1565:     my $selectdomain = "<select name=\"$name\" size=\"1\">\n";
1.356     albertel 1566:     foreach my $dom (@domains) {
                   1567:         $selectdomain.="<option value=\"$dom\" ".
1.563     raeburn  1568:             ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
                   1569:         if ($showdomdesc) {
                   1570:             if ($dom ne '') {
                   1571:                 my $domdesc = &Apache::lonnet::domain($dom,'description');
                   1572:                 if ($domdesc ne '') {
                   1573:                     $selectdomain .= ' ('.$domdesc.')';
                   1574:                 }
                   1575:             } 
                   1576:         }
                   1577:         $selectdomain .= "</option>\n";
1.34      matthew  1578:     }
                   1579:     $selectdomain.="</select>";
                   1580:     return $selectdomain;
                   1581: }
                   1582: 
1.35      matthew  1583: #-------------------------------------------
                   1584: 
1.45      matthew  1585: =pod
                   1586: 
1.586     raeburn  1587: =item * home_server_form_item($domain,$name,$defaultflag)
1.35      matthew  1588: 
1.586     raeburn  1589: input: 4 arguments (two required, two optional) - 
                   1590:     $domain - domain of new user
                   1591:     $name - name of form element
                   1592:     $default - Value of 'default' causes a default item to be first 
                   1593:                             option, and selected by default. 
                   1594:     $hide - Value of 'hide' causes hiding of the name of the server, 
                   1595:                             if 1 server found, or default, if 0 found.
                   1596: output: returns 1 items: 
                   1597: (a) form element which contains either:
                   1598:    (i) <select name="$name">
                   1599:         <option value="$hostid1">$hostid $servers{$hostid}</option>
                   1600:         <option value="$hostid2">$hostid $servers{$hostid}</option>       
                   1601:        </select>
                   1602:        form item if there are multiple library servers in $domain, or
                   1603:    (ii) an <input type="hidden" name="$name" value="$hostid" /> form item 
                   1604:        if there is only one library server in $domain.
                   1605: 
                   1606: (b) number of library servers found.
                   1607: 
                   1608: See loncreateuser.pm for example of use.
1.35      matthew  1609: 
                   1610: =cut
                   1611: 
                   1612: #-------------------------------------------
1.586     raeburn  1613: sub home_server_form_item {
                   1614:     my ($domain,$name,$default,$hide) = @_;
1.513     albertel 1615:     my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586     raeburn  1616:     my $result;
                   1617:     my $numlib = keys(%servers);
                   1618:     if ($numlib > 1) {
                   1619:         $result .= '<select name="'.$name.'" />'."\n";
                   1620:         if ($default) {
                   1621:             $result .= '<option value="default" selected>'.&mt('default').
                   1622:                        '</option>'."\n";
                   1623:         }
                   1624:         foreach my $hostid (sort(keys(%servers))) {
                   1625:             $result.= '<option value="'.$hostid.'">'.
                   1626: 	              $hostid.' '.$servers{$hostid}."</option>\n";
                   1627:         }
                   1628:         $result .= '</select>'."\n";
                   1629:     } elsif ($numlib == 1) {
                   1630:         my $hostid;
                   1631:         foreach my $item (keys(%servers)) {
                   1632:             $hostid = $item;
                   1633:         }
                   1634:         $result .= '<input type="hidden" name="'.$name.'" value="'.
                   1635:                    $hostid.'" />';
                   1636:                    if (!$hide) {
                   1637:                        $result .= $hostid.' '.$servers{$hostid};
                   1638:                    }
                   1639:                    $result .= "\n";
                   1640:     } elsif ($default) {
                   1641:         $result .= '<input type="hidden" name="'.$name.
                   1642:                    '" value="default" />';
                   1643:                    if (!$hide) {
                   1644:                        $result .= &mt('default');
                   1645:                    }
                   1646:                    $result .= "\n";
1.33      matthew  1647:     }
1.586     raeburn  1648:     return ($result,$numlib);
1.33      matthew  1649: }
1.112     bowersj2 1650: 
                   1651: =pod
                   1652: 
1.534     albertel 1653: =back 
                   1654: 
1.112     bowersj2 1655: =cut
1.87      matthew  1656: 
                   1657: ###############################################################
1.112     bowersj2 1658: ##                  Decoding User Agent                      ##
1.87      matthew  1659: ###############################################################
                   1660: 
                   1661: =pod
                   1662: 
1.112     bowersj2 1663: =head1 Decoding the User Agent
                   1664: 
                   1665: =over 4
                   1666: 
                   1667: =item * &decode_user_agent()
1.87      matthew  1668: 
                   1669: Inputs: $r
                   1670: 
                   1671: Outputs:
                   1672: 
                   1673: =over 4
                   1674: 
1.112     bowersj2 1675: =item * $httpbrowser
1.87      matthew  1676: 
1.112     bowersj2 1677: =item * $clientbrowser
1.87      matthew  1678: 
1.112     bowersj2 1679: =item * $clientversion
1.87      matthew  1680: 
1.112     bowersj2 1681: =item * $clientmathml
1.87      matthew  1682: 
1.112     bowersj2 1683: =item * $clientunicode
1.87      matthew  1684: 
1.112     bowersj2 1685: =item * $clientos
1.87      matthew  1686: 
                   1687: =back
                   1688: 
1.157     matthew  1689: =back 
                   1690: 
1.87      matthew  1691: =cut
                   1692: 
                   1693: ###############################################################
                   1694: ###############################################################
                   1695: sub decode_user_agent {
1.247     albertel 1696:     my ($r)=@_;
1.87      matthew  1697:     my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
                   1698:     my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
                   1699:     my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247     albertel 1700:     if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87      matthew  1701:     my $clientbrowser='unknown';
                   1702:     my $clientversion='0';
                   1703:     my $clientmathml='';
                   1704:     my $clientunicode='0';
                   1705:     for (my $i=0;$i<=$#browsertype;$i++) {
                   1706:         my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\:/,$browsertype[$i]);
                   1707: 	if (($httpbrowser=~/$match/i)  && ($httpbrowser!~/$notmatch/i)) {
                   1708: 	    $clientbrowser=$bname;
                   1709:             $httpbrowser=~/$vreg/i;
                   1710: 	    $clientversion=$1;
                   1711:             $clientmathml=($clientversion>=$minv);
                   1712:             $clientunicode=($clientversion>=$univ);
                   1713: 	}
                   1714:     }
                   1715:     my $clientos='unknown';
                   1716:     if (($httpbrowser=~/linux/i) ||
                   1717:         ($httpbrowser=~/unix/i) ||
                   1718:         ($httpbrowser=~/ux/i) ||
                   1719:         ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
                   1720:     if (($httpbrowser=~/vax/i) ||
                   1721:         ($httpbrowser=~/vms/i)) { $clientos='vms'; }
                   1722:     if ($httpbrowser=~/next/i) { $clientos='next'; }
                   1723:     if (($httpbrowser=~/mac/i) ||
                   1724:         ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
                   1725:     if ($httpbrowser=~/win/i) { $clientos='win'; }
                   1726:     if ($httpbrowser=~/embed/i) { $clientos='pda'; }
                   1727:     return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
                   1728:             $clientunicode,$clientos,);
                   1729: }
                   1730: 
1.32      matthew  1731: ###############################################################
                   1732: ##    Authentication changing form generation subroutines    ##
                   1733: ###############################################################
                   1734: ##
                   1735: ## All of the authform_xxxxxxx subroutines take their inputs in a
                   1736: ## hash, and have reasonable default values.
                   1737: ##
                   1738: ##    formname = the name given in the <form> tag.
1.35      matthew  1739: #-------------------------------------------
                   1740: 
1.45      matthew  1741: =pod
                   1742: 
1.112     bowersj2 1743: =head1 Authentication Routines
                   1744: 
                   1745: =over 4
                   1746: 
                   1747: =item * authform_xxxxxx
1.35      matthew  1748: 
                   1749: The authform_xxxxxx subroutines provide javascript and html forms which 
                   1750: handle some of the conveniences required for authentication forms.  
                   1751: This is not an optimal method, but it works.  
                   1752: 
                   1753: See loncreateuser.pm for invocation and use examples.
                   1754: 
                   1755: =over 4
                   1756: 
1.112     bowersj2 1757: =item * authform_header
1.35      matthew  1758: 
1.112     bowersj2 1759: =item * authform_authorwarning
1.35      matthew  1760: 
1.112     bowersj2 1761: =item * authform_nochange
1.35      matthew  1762: 
1.112     bowersj2 1763: =item * authform_kerberos
1.35      matthew  1764: 
1.112     bowersj2 1765: =item * authform_internal
1.35      matthew  1766: 
1.112     bowersj2 1767: =item * authform_filesystem
1.35      matthew  1768: 
                   1769: =back
                   1770: 
1.157     matthew  1771: =back 
                   1772: 
1.35      matthew  1773: =cut
                   1774: 
                   1775: #-------------------------------------------
1.32      matthew  1776: sub authform_header{  
                   1777:     my %in = (
                   1778:         formname => 'cu',
1.80      albertel 1779:         kerb_def_dom => '',
1.32      matthew  1780:         @_,
                   1781:     );
                   1782:     $in{'formname'} = 'document.' . $in{'formname'};
                   1783:     my $result='';
1.80      albertel 1784: 
                   1785: #---------------------------------------------- Code for upper case translation
                   1786:     my $Javascript_toUpperCase;
                   1787:     unless ($in{kerb_def_dom}) {
                   1788:         $Javascript_toUpperCase =<<"END";
                   1789:         switch (choice) {
                   1790:            case 'krb': currentform.elements[choicearg].value =
                   1791:                currentform.elements[choicearg].value.toUpperCase();
                   1792:                break;
                   1793:            default:
                   1794:         }
                   1795: END
                   1796:     } else {
                   1797:         $Javascript_toUpperCase = "";
                   1798:     }
                   1799: 
1.165     raeburn  1800:     my $radioval = "'nochange'";
1.174     matthew  1801:     if (exists($in{'curr_authtype'}) &&
                   1802:         defined($in{'curr_authtype'}) &&
                   1803:         $in{'curr_authtype'} ne '') {
                   1804:         $radioval = "'$in{'curr_authtype'}arg'";
                   1805:     }
1.165     raeburn  1806:     my $argfield = 'null';
                   1807:     if ( grep/^mode$/,(keys %in) ) {
                   1808:         if ($in{'mode'} eq 'modifycourse')  {
                   1809:             if ( grep/^curr_authtype$/,(keys %in) ) {
                   1810:                 $radioval = "'$in{'curr_authtype'}'";
                   1811:             }
                   1812:             if ( grep/^curr_autharg$/,(keys %in) ) {
                   1813:                 unless ($in{'curr_autharg'} eq '') {
                   1814:                     $argfield = "'$in{'curr_autharg'}'";
                   1815:                 }
                   1816:             }
                   1817:         }
                   1818:     }
                   1819: 
1.32      matthew  1820:     $result.=<<"END";
                   1821: var current = new Object();
1.165     raeburn  1822: current.radiovalue = $radioval;
                   1823: current.argfield = $argfield;
1.32      matthew  1824: 
                   1825: function changed_radio(choice,currentform) {
                   1826:     var choicearg = choice + 'arg';
                   1827:     // If a radio button in changed, we need to change the argfield
                   1828:     if (current.radiovalue != choice) {
                   1829:         current.radiovalue = choice;
                   1830:         if (current.argfield != null) {
                   1831:             currentform.elements[current.argfield].value = '';
                   1832:         }
                   1833:         if (choice == 'nochange') {
                   1834:             current.argfield = null;
                   1835:         } else {
                   1836:             current.argfield = choicearg;
                   1837:             switch(choice) {
                   1838:                 case 'krb': 
                   1839:                     currentform.elements[current.argfield].value = 
                   1840:                         "$in{'kerb_def_dom'}";
                   1841:                 break;
                   1842:               default:
                   1843:                 break;
                   1844:             }
                   1845:         }
                   1846:     }
                   1847:     return;
                   1848: }
1.22      www      1849: 
1.32      matthew  1850: function changed_text(choice,currentform) {
                   1851:     var choicearg = choice + 'arg';
                   1852:     if (currentform.elements[choicearg].value !='') {
1.80      albertel 1853:         $Javascript_toUpperCase
1.32      matthew  1854:         // clear old field
                   1855:         if ((current.argfield != choicearg) && (current.argfield != null)) {
                   1856:             currentform.elements[current.argfield].value = '';
                   1857:         }
                   1858:         current.argfield = choicearg;
                   1859:     }
                   1860:     set_auth_radio_buttons(choice,currentform);
                   1861:     return;
1.20      www      1862: }
1.32      matthew  1863: 
                   1864: function set_auth_radio_buttons(newvalue,currentform) {
                   1865:     var i=0;
                   1866:     while (i < currentform.login.length) {
                   1867:         if (currentform.login[i].value == newvalue) { break; }
                   1868:         i++;
                   1869:     }
                   1870:     if (i == currentform.login.length) {
                   1871:         return;
                   1872:     }
                   1873:     current.radiovalue = newvalue;
                   1874:     currentform.login[i].checked = true;
                   1875:     return;
                   1876: }
                   1877: END
                   1878:     return $result;
                   1879: }
                   1880: 
                   1881: sub authform_authorwarning{
                   1882:     my $result='';
1.144     matthew  1883:     $result='<i>'.
                   1884:         &mt('As a general rule, only authors or co-authors should be '.
                   1885:             'filesystem authenticated '.
                   1886:             '(which allows access to the server filesystem).')."</i>\n";
1.32      matthew  1887:     return $result;
                   1888: }
                   1889: 
                   1890: sub authform_nochange{  
                   1891:     my %in = (
                   1892:               formname => 'document.cu',
                   1893:               kerb_def_dom => 'MSU.EDU',
                   1894:               @_,
                   1895:           );
1.586     raeburn  1896:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'}); 
                   1897:     my $result;
                   1898:     if (keys(%can_assign) == 0) {
                   1899:         $result = &mt('Under you current role you are not permitted to change login settings for this user');  
                   1900:     } else {
                   1901:         $result = '<label>'.&mt('[_1] Do not change login data',
                   1902:                   '<input type="radio" name="login" value="nochange" '.
                   1903:                   'checked="checked" onclick="'.
1.281     albertel 1904:             "javascript:changed_radio('nochange',$in{'formname'});".'" />').
                   1905: 	    '</label>';
1.586     raeburn  1906:     }
1.32      matthew  1907:     return $result;
                   1908: }
                   1909: 
                   1910: sub authform_kerberos{  
                   1911:     my %in = (
                   1912:               formname => 'document.cu',
                   1913:               kerb_def_dom => 'MSU.EDU',
1.80      albertel 1914:               kerb_def_auth => 'krb4',
1.32      matthew  1915:               @_,
                   1916:               );
1.586     raeburn  1917:     my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
                   1918:         $autharg,$jscall);
                   1919:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
1.80      albertel 1920:     if ($in{'kerb_def_auth'} eq 'krb5') {
1.586     raeburn  1921:        $check5 = ' checked="on"';
1.80      albertel 1922:     } else {
1.586     raeburn  1923:        $check4 = ' checked="on"';
1.80      albertel 1924:     }
1.165     raeburn  1925:     $krbarg = $in{'kerb_def_dom'};
1.586     raeburn  1926:     if (grep(/^curr_authtype$/,(keys(%in)))) {
                   1927:         if ($in{'curr_authtype'} =~ m/^krb(\d+)$/) {
                   1928:             $krbver = $1;
                   1929:             $krbcheck = ' checked="on"';
                   1930:             if ($krbver eq '5') {
                   1931:                 $check5 = ' checked="on"';
                   1932:                 $check4 = '';
                   1933:             } else {
                   1934:                 $check4 = ' checked="on"';
                   1935:                 $check5 = '';
                   1936:             }
                   1937:             if (grep(/^curr_autharg$/,(keys(%in)))) {
1.165     raeburn  1938:                 $krbarg = $in{'curr_autharg'};
                   1939:             }
1.586     raeburn  1940:             if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
                   1941:                 if (grep(/^curr_autharg$/,(keys(%in)))) {
                   1942:                     $result = 
                   1943:     &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
                   1944:         $in{'curr_autharg'},$krbver);
                   1945:                 } else {
                   1946:                     $result =
                   1947:     &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
                   1948:                 }
                   1949:                 return $result; 
                   1950:             }
                   1951:         }
                   1952:     } else {
                   1953:         if ($authnum == 1) {
                   1954:             $authtype = '<input type="hidden" name="login" value="krb">';
1.165     raeburn  1955:         }
                   1956:     }
1.586     raeburn  1957:     if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
                   1958:         return;
1.587     raeburn  1959:     } elsif ($authtype eq '') {
                   1960:         if (grep(/^mode$/,(keys(%in)))) {
                   1961:             if ($in{'mode'} eq 'modifycourse') {
                   1962:                 if ($authnum == 1) {
                   1963:                     $authtype = '<input type="hidden" name="login" value="krb">';
                   1964:                 }
                   1965:             }
                   1966:         }
1.586     raeburn  1967:     }
                   1968:     $jscall = "javascript:changed_radio('krb',$in{'formname'});";
                   1969:     if ($authtype eq '') {
                   1970:         $authtype = '<input type="radio" name="login" value="krb" '.
                   1971:                     'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
                   1972:                     $krbcheck.' />';
                   1973:     }
                   1974:     if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
                   1975:         ($can_assign{'krb4'} && !$can_assign{'krb5'} && 
                   1976:          $in{'curr_authtype'} eq 'krb5') ||
                   1977:         (!$can_assign{'krb4'} && $can_assign{'krb5'} && 
                   1978:          $in{'curr_authtype'} eq 'krb4')) {
                   1979:         $result .= &mt
1.144     matthew  1980:         ('[_1] Kerberos authenticated with domain [_2] '.
1.281     albertel 1981:          '[_3] Version 4 [_4] Version 5 [_5]',
1.586     raeburn  1982:          '<label>'.$authtype,
1.281     albertel 1983:          '</label><input type="text" size="10" name="krbarg" '.
1.165     raeburn  1984:              'value="'.$krbarg.'" '.
1.144     matthew  1985:              'onchange="'.$jscall.'" />',
1.281     albertel 1986:          '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
                   1987:          '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
                   1988: 	 '</label>');
1.586     raeburn  1989:     } elsif ($can_assign{'krb4'}) {
                   1990:         $result .= &mt
                   1991:         ('[_1] Kerberos authenticated with domain [_2] '.
                   1992:          '[_3] Version 4 [_4]',
                   1993:          '<label>'.$authtype,
                   1994:          '</label><input type="text" size="10" name="krbarg" '.
                   1995:              'value="'.$krbarg.'" '.
                   1996:              'onchange="'.$jscall.'" />',
                   1997:          '<label><input type="hidden" name="krbver" value="4" />',
                   1998:          '</label>');
                   1999:     } elsif ($can_assign{'krb5'}) {
                   2000:         $result .= &mt
                   2001:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2002:          '[_3] Version 5 [_4]',
                   2003:          '<label>'.$authtype,
                   2004:          '</label><input type="text" size="10" name="krbarg" '.
                   2005:              'value="'.$krbarg.'" '.
                   2006:              'onchange="'.$jscall.'" />',
                   2007:          '<label><input type="hidden" name="krbver" value="5" />',
                   2008:          '</label>');
                   2009:     }
1.32      matthew  2010:     return $result;
                   2011: }
                   2012: 
                   2013: sub authform_internal{  
1.586     raeburn  2014:     my %in = (
1.32      matthew  2015:                 formname => 'document.cu',
                   2016:                 kerb_def_dom => 'MSU.EDU',
                   2017:                 @_,
                   2018:                 );
1.586     raeburn  2019:     my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
                   2020:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
                   2021:     if (grep(/^curr_authtype$/,(keys(%in)))) {
                   2022:         if ($in{'curr_authtype'} eq 'internal:') {
                   2023:             if ($can_assign{'int'}) {
                   2024:                 $intcheck = 'checked="on" ';
                   2025:                 if (grep(/^curr_autharg$/,(keys(%in)))) {
                   2026:                     $intarg = $in{'curr_autharg'};
                   2027:                 }
                   2028:             } else {
                   2029:                 $result = &mt('Currently internally authenticated.');
                   2030:                 return $result;
1.165     raeburn  2031:             }
                   2032:         }
1.586     raeburn  2033:     } else {
                   2034:         if ($authnum == 1) {
                   2035:             $authtype = '<input type="hidden" name="login" value="int">';
                   2036:         }
                   2037:     }
                   2038:     if (!$can_assign{'int'}) {
                   2039:         return;
1.587     raeburn  2040:     } elsif ($authtype eq '') {
                   2041:         if (grep(/^mode$/,(keys(%in)))) {
                   2042:             if ($in{'mode'} eq 'modifycourse') {
                   2043:                 if ($authnum == 1) {
                   2044:                     $authtype = '<input type="hidden" name="login" value="int">';
                   2045:                 }
                   2046:             }
                   2047:         }
1.165     raeburn  2048:     }
1.586     raeburn  2049:     $jscall = "javascript:changed_radio('int',$in{'formname'});";
                   2050:     if ($authtype eq '') {
                   2051:         $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
                   2052:                     ' onchange="'.$jscall.'" onclick="'.$jscall.'" />';
                   2053:     }
                   2054:     $autharg = '<input type="text" size="10" name="intarg" value="'.
                   2055:                $intarg.'" onchange="'.$jscall.'" />';
                   2056:     $result = &mt
1.144     matthew  2057:         ('[_1] Internally authenticated (with initial password [_2])',
1.586     raeburn  2058:          '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  2059:     return $result;
                   2060: }
                   2061: 
                   2062: sub authform_local{  
                   2063:     my %in = (
                   2064:               formname => 'document.cu',
                   2065:               kerb_def_dom => 'MSU.EDU',
                   2066:               @_,
                   2067:               );
1.586     raeburn  2068:     my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
                   2069:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
                   2070:     if (grep(/^curr_authtype$/,(keys(%in)))) {
                   2071:         if ($in{'curr_authtype'} eq 'localauth:') {
                   2072:             if ($can_assign{'loc'}) {
                   2073:                 $loccheck = 'checked="on" ';
                   2074:                 if (grep(/^curr_autharg$/,(keys(%in)))) {
                   2075:                     $locarg = $in{'curr_autharg'};
                   2076:                 }
                   2077:             } else {
                   2078:                 $result = &mt('Currently using local (institutional) authentication.');
                   2079:                 return $result;
1.165     raeburn  2080:             }
                   2081:         }
1.586     raeburn  2082:     } else {
                   2083:         if ($authnum == 1) {
                   2084:             $authtype = '<input type="hidden" name="login" value="loc">';
                   2085:         }
                   2086:     }
                   2087:     if (!$can_assign{'loc'}) {
                   2088:         return;
1.587     raeburn  2089:     } elsif ($authtype eq '') {
                   2090:         if (grep(/^mode$/,(keys(%in)))) {
                   2091:             if ($in{'mode'} eq 'modifycourse') {
                   2092:                 if ($authnum == 1) {
                   2093:                     $authtype = '<input type="hidden" name="login" value="loc">';
                   2094:                 }
                   2095:             }
                   2096:         }
1.165     raeburn  2097:     }
1.586     raeburn  2098:     $jscall = "javascript:changed_radio('loc',$in{'formname'});";
                   2099:     if ($authtype eq '') {
                   2100:         $authtype = '<input type="radio" name="login" value="loc" '.
                   2101:                     $loccheck.' onchange="'.$jscall.'" onclick="'.
                   2102:                     $jscall.'" />';
                   2103:     }
                   2104:     $autharg = '<input type="text" size="10" name="locarg" value="'.
                   2105:                $locarg.'" onchange="'.$jscall.'" />';
                   2106:     $result = &mt('[_1] Local Authentication with argument [_2]',
                   2107:                   '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  2108:     return $result;
                   2109: }
                   2110: 
                   2111: sub authform_filesystem{  
                   2112:     my %in = (
                   2113:               formname => 'document.cu',
                   2114:               kerb_def_dom => 'MSU.EDU',
                   2115:               @_,
                   2116:               );
1.586     raeburn  2117:     my ($fsyscheck,$result,$authtype,$autharg,$jscall);
                   2118:     my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
                   2119:     if (grep(/^curr_authtype$/,(keys(%in)))) {
                   2120:         if ($in{'curr_authtype'} eq 'unix:') {
                   2121:             if ($can_assign{'fsys'}) {
                   2122:                 $fsyscheck = 'checked="on" ';
                   2123:             } else {
                   2124:                 $result = &mt('Currently Filesystem Authenticated.');
                   2125:                 return $result;
                   2126:             }           
                   2127:         }
                   2128:     } else {
                   2129:         if ($authnum == 1) {
                   2130:             $authtype = '<input type="hidden" name="login" value="fsys">';
                   2131:         }
                   2132:     }
                   2133:     if (!$can_assign{'fsys'}) {
                   2134:         return;
1.587     raeburn  2135:     } elsif ($authtype eq '') {
                   2136:         if (grep(/^mode$/,(keys(%in)))) {
                   2137:             if ($in{'mode'} eq 'modifycourse') {
                   2138:                 if ($authnum == 1) {
                   2139:                     $authtype = '<input type="hidden" name="login" value="fsys">';
                   2140:                 }
                   2141:             }
                   2142:         }
1.586     raeburn  2143:     }
                   2144:     $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
                   2145:     if ($authtype eq '') {
                   2146:         $authtype = '<input type="radio" name="login" value="fsys" '.
                   2147:                     $fsyscheck.' onchange="'.$jscall.'" onclick="'.
                   2148:                     $jscall.'" />';
                   2149:     }
                   2150:     $autharg = '<input type="text" size="10" name="fsysarg" value=""'.
                   2151:                ' onchange="'.$jscall.'" />';
                   2152:     $result = &mt
1.144     matthew  2153:         ('[_1] Filesystem Authenticated (with initial password [_2])',
1.281     albertel 2154:          '<label><input type="radio" name="login" value="fsys" '.
1.586     raeburn  2155:          $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.281     albertel 2156:          '</label><input type="text" size="10" name="fsysarg" value="" '.
1.144     matthew  2157:                   'onchange="'.$jscall.'" />');
1.32      matthew  2158:     return $result;
                   2159: }
                   2160: 
1.586     raeburn  2161: sub get_assignable_auth {
                   2162:     my ($dom) = @_;
                   2163:     if ($dom eq '') {
                   2164:         $dom = $env{'request.role.domain'};
                   2165:     }
                   2166:     my %can_assign = (
                   2167:                           krb4 => 1,
                   2168:                           krb5 => 1,
                   2169:                           int  => 1,
                   2170:                           loc  => 1,
                   2171:                      );
                   2172:     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
                   2173:     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   2174:         if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
                   2175:             my $authhash = $domconfig{'usercreation'}{'authtypes'};
                   2176:             my $context;
                   2177:             if ($env{'request.role'} =~ /^au/) {
                   2178:                 $context = 'author';
                   2179:             } elsif ($env{'request.role'} =~ /^dc/) {
                   2180:                 $context = 'domain';
                   2181:             } elsif ($env{'request.course.id'}) {
                   2182:                 $context = 'course';
                   2183:             }
                   2184:             if ($context) {
                   2185:                 if (ref($authhash->{$context}) eq 'HASH') {
                   2186:                    %can_assign = %{$authhash->{$context}}; 
                   2187:                 }
                   2188:             }
                   2189:         }
                   2190:     }
                   2191:     my $authnum = 0;
                   2192:     foreach my $key (keys(%can_assign)) {
                   2193:         if ($can_assign{$key}) {
                   2194:             $authnum ++;
                   2195:         }
                   2196:     }
                   2197:     if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
                   2198:         $authnum --;
                   2199:     }
                   2200:     return ($authnum,%can_assign);
                   2201: }
                   2202: 
1.80      albertel 2203: ###############################################################
                   2204: ##    Get Authentication Defaults for Domain                 ##
                   2205: ###############################################################
                   2206: 
                   2207: =pod
                   2208: 
1.112     bowersj2 2209: =head1 Domains and Authentication
                   2210: 
                   2211: Returns default authentication type and an associated argument as
                   2212: listed in file 'domain.tab'.
                   2213: 
                   2214: =over 4
                   2215: 
                   2216: =item * get_auth_defaults
1.80      albertel 2217: 
                   2218: get_auth_defaults($target_domain) returns the default authentication
                   2219: type and an associated argument (initial password or a kerberos domain).
                   2220: These values are stored in lonTabs/domain.tab
                   2221: 
                   2222: ($def_auth, $def_arg) = &get_auth_defaults($target_domain);
                   2223: 
                   2224: If target_domain is not found in domain.tab, returns nothing ('').
                   2225: 
                   2226: =cut
                   2227: 
                   2228: #-------------------------------------------
                   2229: sub get_auth_defaults {
                   2230:     my $domain=shift;
1.514     albertel 2231:     return (&Apache::lonnet::domain($domain,'auth_def'),
                   2232: 	    &Apache::lonnet::domain($domain,'auth_arg_def'));
                   2233: 	    
1.80      albertel 2234: }
                   2235: ###############################################################
                   2236: ##   End Get Authentication Defaults for Domain              ##
                   2237: ###############################################################
                   2238: 
                   2239: ###############################################################
                   2240: ##    Get Kerberos Defaults for Domain                 ##
                   2241: ###############################################################
                   2242: ##
                   2243: ## Returns default kerberos version and an associated argument
                   2244: ## as listed in file domain.tab. If not listed, provides
                   2245: ## appropriate default domain and kerberos version.
                   2246: ##
                   2247: #-------------------------------------------
                   2248: 
                   2249: =pod
                   2250: 
1.112     bowersj2 2251: =item * get_kerberos_defaults
1.80      albertel 2252: 
                   2253: get_kerberos_defaults($target_domain) returns the default kerberos
                   2254: version and domain. If not found in domain.tabs, it defaults to
                   2255: version 4 and the domain of the server.
                   2256: 
                   2257: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
                   2258: 
                   2259: =cut
                   2260: 
                   2261: #-------------------------------------------
                   2262: sub get_kerberos_defaults {
                   2263:     my $domain=shift;
                   2264:     my ($krbdef,$krbdefdom) =
                   2265:         &Apache::loncommon::get_auth_defaults($domain);
                   2266:     unless ($krbdef =~/^krb/ && $krbdefdom) {
                   2267:         $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
                   2268:         my $krbdefdom=$1;
                   2269:         $krbdefdom=~tr/a-z/A-Z/;
                   2270:         $krbdef = "krb4";
                   2271:     }
                   2272:     return ($krbdef,$krbdefdom);
                   2273: }
1.112     bowersj2 2274: 
                   2275: =pod
                   2276: 
                   2277: =back
                   2278: 
                   2279: =cut
1.32      matthew  2280: 
1.46      matthew  2281: ###############################################################
                   2282: ##                Thesaurus Functions                        ##
                   2283: ###############################################################
1.20      www      2284: 
1.46      matthew  2285: =pod
1.20      www      2286: 
1.112     bowersj2 2287: =head1 Thesaurus Functions
                   2288: 
                   2289: =over 4
                   2290: 
                   2291: =item * initialize_keywords
1.46      matthew  2292: 
                   2293: Initializes the package variable %Keywords if it is empty.  Uses the
                   2294: package variable $thesaurus_db_file.
                   2295: 
                   2296: =cut
                   2297: 
                   2298: ###################################################
                   2299: 
                   2300: sub initialize_keywords {
                   2301:     return 1 if (scalar keys(%Keywords));
                   2302:     # If we are here, %Keywords is empty, so fill it up
                   2303:     #   Make sure the file we need exists...
                   2304:     if (! -e $thesaurus_db_file) {
                   2305:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
                   2306:                                  " failed because it does not exist");
                   2307:         return 0;
                   2308:     }
                   2309:     #   Set up the hash as a database
                   2310:     my %thesaurus_db;
                   2311:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 2312:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  2313:         &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
                   2314:                                  $thesaurus_db_file);
                   2315:         return 0;
                   2316:     } 
                   2317:     #  Get the average number of appearances of a word.
                   2318:     my $avecount = $thesaurus_db{'average.count'};
                   2319:     #  Put keywords (those that appear > average) into %Keywords
                   2320:     while (my ($word,$data)=each (%thesaurus_db)) {
                   2321:         my ($count,undef) = split /:/,$data;
                   2322:         $Keywords{$word}++ if ($count > $avecount);
                   2323:     }
                   2324:     untie %thesaurus_db;
                   2325:     # Remove special values from %Keywords.
1.356     albertel 2326:     foreach my $value ('total.count','average.count') {
                   2327:         delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586     raeburn  2328:   }
1.46      matthew  2329:     return 1;
                   2330: }
                   2331: 
                   2332: ###################################################
                   2333: 
                   2334: =pod
                   2335: 
1.112     bowersj2 2336: =item * keyword($word)
1.46      matthew  2337: 
                   2338: Returns true if $word is a keyword.  A keyword is a word that appears more 
                   2339: than the average number of times in the thesaurus database.  Calls 
                   2340: &initialize_keywords
                   2341: 
                   2342: =cut
                   2343: 
                   2344: ###################################################
1.20      www      2345: 
                   2346: sub keyword {
1.46      matthew  2347:     return if (!&initialize_keywords());
                   2348:     my $word=lc(shift());
                   2349:     $word=~s/\W//g;
                   2350:     return exists($Keywords{$word});
1.20      www      2351: }
1.46      matthew  2352: 
                   2353: ###############################################################
                   2354: 
                   2355: =pod 
1.20      www      2356: 
1.112     bowersj2 2357: =item * get_related_words
1.46      matthew  2358: 
1.160     matthew  2359: Look up a word in the thesaurus.  Takes a scalar argument and returns
1.46      matthew  2360: an array of words.  If the keyword is not in the thesaurus, an empty array
                   2361: will be returned.  The order of the words returned is determined by the
                   2362: database which holds them.
                   2363: 
                   2364: Uses global $thesaurus_db_file.
                   2365: 
                   2366: =cut
                   2367: 
                   2368: ###############################################################
                   2369: sub get_related_words {
                   2370:     my $keyword = shift;
                   2371:     my %thesaurus_db;
                   2372:     if (! -e $thesaurus_db_file) {
                   2373:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
                   2374:                                  "failed because the file does not exist");
                   2375:         return ();
                   2376:     }
                   2377:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 2378:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  2379:         return ();
                   2380:     } 
                   2381:     my @Words=();
1.429     www      2382:     my $count=0;
1.46      matthew  2383:     if (exists($thesaurus_db{$keyword})) {
1.356     albertel 2384: 	# The first element is the number of times
                   2385: 	# the word appears.  We do not need it now.
1.429     www      2386: 	my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
                   2387: 	my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
                   2388: 	my $threshold=$mostfrequentcount/10;
                   2389:         foreach my $possibleword (@RelatedWords) {
                   2390:             my ($word,$wordcount)=split(/\,/,$possibleword);
                   2391:             if ($wordcount>$threshold) {
                   2392: 		push(@Words,$word);
                   2393:                 $count++;
                   2394:                 if ($count>10) { last; }
                   2395: 	    }
1.20      www      2396:         }
                   2397:     }
1.46      matthew  2398:     untie %thesaurus_db;
                   2399:     return @Words;
1.14      harris41 2400: }
1.46      matthew  2401: 
1.112     bowersj2 2402: =pod
                   2403: 
                   2404: =back
                   2405: 
                   2406: =cut
1.61      www      2407: 
                   2408: # -------------------------------------------------------------- Plaintext name
1.81      albertel 2409: =pod
                   2410: 
1.112     bowersj2 2411: =head1 User Name Functions
                   2412: 
                   2413: =over 4
                   2414: 
1.226     albertel 2415: =item * plainname($uname,$udom,$first)
1.81      albertel 2416: 
1.112     bowersj2 2417: Takes a users logon name and returns it as a string in
1.226     albertel 2418: "first middle last generation" form 
                   2419: if $first is set to 'lastname' then it returns it as
                   2420: 'lastname generation, firstname middlename' if their is a lastname
1.81      albertel 2421: 
                   2422: =cut
1.61      www      2423: 
1.295     www      2424: 
1.81      albertel 2425: ###############################################################
1.61      www      2426: sub plainname {
1.226     albertel 2427:     my ($uname,$udom,$first)=@_;
1.537     albertel 2428:     return if (!defined($uname) || !defined($udom));
1.295     www      2429:     my %names=&getnames($uname,$udom);
1.226     albertel 2430:     my $name=&Apache::lonnet::format_name($names{'firstname'},
                   2431: 					  $names{'middlename'},
                   2432: 					  $names{'lastname'},
                   2433: 					  $names{'generation'},$first);
                   2434:     $name=~s/^\s+//;
1.62      www      2435:     $name=~s/\s+$//;
                   2436:     $name=~s/\s+/ /g;
1.353     albertel 2437:     if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62      www      2438:     return $name;
1.61      www      2439: }
1.66      www      2440: 
                   2441: # -------------------------------------------------------------------- Nickname
1.81      albertel 2442: =pod
                   2443: 
1.112     bowersj2 2444: =item * nickname($uname,$udom)
1.81      albertel 2445: 
                   2446: Gets a users name and returns it as a string as
                   2447: 
                   2448: "&quot;nickname&quot;"
1.66      www      2449: 
1.81      albertel 2450: if the user has a nickname or
                   2451: 
                   2452: "first middle last generation"
                   2453: 
                   2454: if the user does not
                   2455: 
                   2456: =cut
1.66      www      2457: 
                   2458: sub nickname {
                   2459:     my ($uname,$udom)=@_;
1.537     albertel 2460:     return if (!defined($uname) || !defined($udom));
1.295     www      2461:     my %names=&getnames($uname,$udom);
1.68      albertel 2462:     my $name=$names{'nickname'};
1.66      www      2463:     if ($name) {
                   2464:        $name='&quot;'.$name.'&quot;'; 
                   2465:     } else {
                   2466:        $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
                   2467: 	     $names{'lastname'}.' '.$names{'generation'};
                   2468:        $name=~s/\s+$//;
                   2469:        $name=~s/\s+/ /g;
                   2470:     }
                   2471:     return $name;
                   2472: }
                   2473: 
1.295     www      2474: sub getnames {
                   2475:     my ($uname,$udom)=@_;
1.537     albertel 2476:     return if (!defined($uname) || !defined($udom));
1.433     albertel 2477:     if ($udom eq 'public' && $uname eq 'public') {
                   2478: 	return ('lastname' => &mt('Public'));
                   2479:     }
1.295     www      2480:     my $id=$uname.':'.$udom;
                   2481:     my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
                   2482:     if ($cached) {
                   2483: 	return %{$names};
                   2484:     } else {
                   2485: 	my %loadnames=&Apache::lonnet::get('environment',
                   2486:                     ['firstname','middlename','lastname','generation','nickname'],
                   2487: 					 $udom,$uname);
                   2488: 	&Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
                   2489: 	return %loadnames;
                   2490:     }
                   2491: }
1.61      www      2492: 
1.542     raeburn  2493: # -------------------------------------------------------------------- getemails
                   2494: =pod
                   2495: 
                   2496: =item * getemails($uname,$udom)
                   2497: 
                   2498: Gets a user's email information and returns it as a hash with keys:
                   2499: notification, critnotification, permanentemail
                   2500: 
                   2501: For notification and critnotification, values are comma-separated lists 
                   2502: of e-mail address(es); for permanentemail, value is a single e-mail address.
                   2503:  
                   2504: =cut
                   2505: 
1.466     albertel 2506: sub getemails {
                   2507:     my ($uname,$udom)=@_;
                   2508:     if ($udom eq 'public' && $uname eq 'public') {
                   2509: 	return;
                   2510:     }
1.467     www      2511:     if (!$udom) { $udom=$env{'user.domain'}; }
                   2512:     if (!$uname) { $uname=$env{'user.name'}; }
1.466     albertel 2513:     my $id=$uname.':'.$udom;
                   2514:     my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
                   2515:     if ($cached) {
                   2516: 	return %{$names};
                   2517:     } else {
                   2518: 	my %loadnames=&Apache::lonnet::get('environment',
                   2519:                     			   ['notification','critnotification',
                   2520: 					    'permanentemail'],
                   2521: 					   $udom,$uname);
                   2522: 	&Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
                   2523: 	return %loadnames;
                   2524:     }
                   2525: }
                   2526: 
1.551     albertel 2527: sub flush_email_cache {
                   2528:     my ($uname,$udom)=@_;
                   2529:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   2530:     if (!$uname) { $uname=$env{'user.name'};   }
                   2531:     return if ($udom eq 'public' && $uname eq 'public');
                   2532:     my $id=$uname.':'.$udom;
                   2533:     &Apache::lonnet::devalidate_cache_new('emailscache',$id);
                   2534: }
                   2535: 
1.61      www      2536: # ------------------------------------------------------------------ Screenname
1.81      albertel 2537: 
                   2538: =pod
                   2539: 
1.112     bowersj2 2540: =item * screenname($uname,$udom)
1.81      albertel 2541: 
                   2542: Gets a users screenname and returns it as a string
                   2543: 
                   2544: =cut
1.61      www      2545: 
                   2546: sub screenname {
                   2547:     my ($uname,$udom)=@_;
1.258     albertel 2548:     if ($uname eq $env{'user.name'} &&
                   2549: 	$udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212     albertel 2550:     my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68      albertel 2551:     return $names{'screenname'};
1.62      www      2552: }
                   2553: 
1.212     albertel 2554: 
1.62      www      2555: # ------------------------------------------------------------- Message Wrapper
                   2556: 
                   2557: sub messagewrapper {
1.369     www      2558:     my ($link,$username,$domain,$subject,$text)=@_;
1.62      www      2559:     return 
1.441     albertel 2560:         '<a href="/adm/email?compose=individual&amp;'.
                   2561:         'recname='.$username.'&amp;recdom='.$domain.
                   2562: 	'&amp;subject='.&escape($subject).'&amp;text='.&escape($text).'" '.
1.200     matthew  2563:         'title="'.&mt('Send message').'">'.$link.'</a>';
1.74      www      2564: }
                   2565: # --------------------------------------------------------------- Notes Wrapper
                   2566: 
                   2567: sub noteswrapper {
                   2568:     my ($link,$un,$do)=@_;
                   2569:     return 
                   2570: "<a href='/adm/email?recordftf=retrieve&recname=$un&recdom=$do'>$link</a>";
1.62      www      2571: }
                   2572: # ------------------------------------------------------------- Aboutme Wrapper
                   2573: 
                   2574: sub aboutmewrapper {
1.166     www      2575:     my ($link,$username,$domain,$target)=@_;
1.447     raeburn  2576:     if (!defined($username)  && !defined($domain)) {
                   2577:         return;
                   2578:     }
1.205     www      2579:     return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.454     banghart 2580: 	($target?' target="$target"':'').' title="'.&mt("View this user's personal page").'">'.$link.'</a>';
1.62      www      2581: }
                   2582: 
                   2583: # ------------------------------------------------------------ Syllabus Wrapper
                   2584: 
                   2585: 
                   2586: sub syllabuswrapper {
1.109     matthew  2587:     my ($linktext,$coursedir,$domain,$fontcolor)=@_;
                   2588:     if ($fontcolor) { 
                   2589:         $linktext='<font color="'.$fontcolor.'">'.$linktext.'</font>'; 
                   2590:     }
1.208     matthew  2591:     return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61      www      2592: }
1.14      harris41 2593: 
1.208     matthew  2594: sub track_student_link {
1.268     albertel 2595:     my ($linktext,$sname,$sdom,$target,$start) = @_;
                   2596:     my $link ="/adm/trackstudent?";
1.208     matthew  2597:     my $title = 'View recent activity';
                   2598:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   2599:         defined($sdom)  && $sdom  !~ /^\s*$/) {
1.268     albertel 2600:         $link .= "selected_student=$sname:$sdom";
1.208     matthew  2601:         $title .= ' of this student';
1.268     albertel 2602:     } 
1.208     matthew  2603:     if (defined($target) && $target !~ /^\s*$/) {
                   2604:         $target = qq{target="$target"};
                   2605:     } else {
                   2606:         $target = '';
                   2607:     }
1.268     albertel 2608:     if ($start) { $link.='&amp;start='.$start; }
1.554     albertel 2609:     $title = &mt($title);
                   2610:     $linktext = &mt($linktext);
1.448     albertel 2611:     return qq{<a href="$link" title="$title" $target>$linktext</a>}.
                   2612: 	&help_open_topic('View_recent_activity');
1.208     matthew  2613: }
                   2614: 
1.508     www      2615: # ===================================================== Display a student photo
                   2616: 
                   2617: 
1.509     albertel 2618: sub student_image_tag {
1.508     www      2619:     my ($domain,$user)=@_;
                   2620:     my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
                   2621:     if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
                   2622: 	return '<img src="'.$imgsrc.'" align="right" />';
                   2623:     } else {
                   2624: 	return '';
                   2625:     }
                   2626: }
                   2627: 
1.112     bowersj2 2628: =pod
                   2629: 
                   2630: =back
                   2631: 
                   2632: =head1 Access .tab File Data
                   2633: 
                   2634: =over 4
                   2635: 
                   2636: =item * languageids() 
                   2637: 
                   2638: returns list of all language ids
                   2639: 
                   2640: =cut
                   2641: 
1.14      harris41 2642: sub languageids {
1.16      harris41 2643:     return sort(keys(%language));
1.14      harris41 2644: }
                   2645: 
1.112     bowersj2 2646: =pod
                   2647: 
                   2648: =item * languagedescription() 
                   2649: 
                   2650: returns description of a specified language id
                   2651: 
                   2652: =cut
                   2653: 
1.14      harris41 2654: sub languagedescription {
1.125     www      2655:     my $code=shift;
                   2656:     return  ($supported_language{$code}?'* ':'').
                   2657:             $language{$code}.
1.126     www      2658: 	    ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145     www      2659: }
                   2660: 
                   2661: sub plainlanguagedescription {
                   2662:     my $code=shift;
                   2663:     return $language{$code};
                   2664: }
                   2665: 
                   2666: sub supportedlanguagecode {
                   2667:     my $code=shift;
                   2668:     return $supported_language{$code};
1.97      www      2669: }
                   2670: 
1.112     bowersj2 2671: =pod
                   2672: 
                   2673: =item * copyrightids() 
                   2674: 
                   2675: returns list of all copyrights
                   2676: 
                   2677: =cut
                   2678: 
                   2679: sub copyrightids {
                   2680:     return sort(keys(%cprtag));
                   2681: }
                   2682: 
                   2683: =pod
                   2684: 
                   2685: =item * copyrightdescription() 
                   2686: 
                   2687: returns description of a specified copyright id
                   2688: 
                   2689: =cut
                   2690: 
                   2691: sub copyrightdescription {
1.166     www      2692:     return &mt($cprtag{shift(@_)});
1.112     bowersj2 2693: }
1.197     matthew  2694: 
                   2695: =pod
                   2696: 
1.192     taceyjo1 2697: =item * source_copyrightids() 
                   2698: 
                   2699: returns list of all source copyrights
                   2700: 
                   2701: =cut
                   2702: 
                   2703: sub source_copyrightids {
                   2704:     return sort(keys(%scprtag));
                   2705: }
                   2706: 
                   2707: =pod
                   2708: 
                   2709: =item * source_copyrightdescription() 
                   2710: 
                   2711: returns description of a specified source copyright id
                   2712: 
                   2713: =cut
                   2714: 
                   2715: sub source_copyrightdescription {
                   2716:     return &mt($scprtag{shift(@_)});
                   2717: }
1.112     bowersj2 2718: 
                   2719: =pod
                   2720: 
                   2721: =item * filecategories() 
                   2722: 
                   2723: returns list of all file categories
                   2724: 
                   2725: =cut
                   2726: 
                   2727: sub filecategories {
                   2728:     return sort(keys(%category_extensions));
                   2729: }
                   2730: 
                   2731: =pod
                   2732: 
                   2733: =item * filecategorytypes() 
                   2734: 
                   2735: returns list of file types belonging to a given file
                   2736: category
                   2737: 
                   2738: =cut
                   2739: 
                   2740: sub filecategorytypes {
1.356     albertel 2741:     my ($cat) = @_;
                   2742:     return @{$category_extensions{lc($cat)}};
1.112     bowersj2 2743: }
                   2744: 
                   2745: =pod
                   2746: 
                   2747: =item * fileembstyle() 
                   2748: 
                   2749: returns embedding style for a specified file type
                   2750: 
                   2751: =cut
                   2752: 
                   2753: sub fileembstyle {
                   2754:     return $fe{lc(shift(@_))};
1.169     www      2755: }
                   2756: 
1.351     www      2757: sub filemimetype {
                   2758:     return $fm{lc(shift(@_))};
                   2759: }
                   2760: 
1.169     www      2761: 
                   2762: sub filecategoryselect {
                   2763:     my ($name,$value)=@_;
1.189     matthew  2764:     return &select_form($value,$name,
1.169     www      2765: 			'' => &mt('Any category'),
                   2766: 			map { $_,$_ } sort(keys(%category_extensions)));
1.112     bowersj2 2767: }
                   2768: 
                   2769: =pod
                   2770: 
                   2771: =item * filedescription() 
                   2772: 
                   2773: returns description for a specified file type
                   2774: 
                   2775: =cut
                   2776: 
                   2777: sub filedescription {
1.188     matthew  2778:     my $file_description = $fd{lc(shift())};
                   2779:     $file_description =~ s:([\[\]]):~$1:g;
                   2780:     return &mt($file_description);
1.112     bowersj2 2781: }
                   2782: 
                   2783: =pod
                   2784: 
                   2785: =item * filedescriptionex() 
                   2786: 
                   2787: returns description for a specified file type with
                   2788: extra formatting
                   2789: 
                   2790: =cut
                   2791: 
                   2792: sub filedescriptionex {
                   2793:     my $ex=shift;
1.188     matthew  2794:     my $file_description = $fd{lc($ex)};
                   2795:     $file_description =~ s:([\[\]]):~$1:g;
                   2796:     return '.'.$ex.' '.&mt($file_description);
1.112     bowersj2 2797: }
                   2798: 
                   2799: # End of .tab access
                   2800: =pod
                   2801: 
                   2802: =back
                   2803: 
                   2804: =cut
                   2805: 
                   2806: # ------------------------------------------------------------------ File Types
                   2807: sub fileextensions {
                   2808:     return sort(keys(%fe));
                   2809: }
                   2810: 
1.97      www      2811: # ----------------------------------------------------------- Display Languages
                   2812: # returns a hash with all desired display languages
                   2813: #
                   2814: 
                   2815: sub display_languages {
                   2816:     my %languages=();
1.356     albertel 2817:     foreach my $lang (&preferred_languages()) {
                   2818: 	$languages{$lang}=1;
1.97      www      2819:     }
                   2820:     &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258     albertel 2821:     if ($env{'form.displaylanguage'}) {
1.356     albertel 2822: 	foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
                   2823: 	    $languages{$lang}=1;
1.97      www      2824:         }
                   2825:     }
                   2826:     return %languages;
1.14      harris41 2827: }
                   2828: 
1.117     www      2829: sub preferred_languages {
                   2830:     my @languages=();
1.258     albertel 2831:     if ($env{'course.'.$env{'request.course.id'}.'.languages'}) {
1.117     www      2832: 	@languages=(@languages,split(/\s*(\,|\;|\:)\s*/,
1.258     albertel 2833: 	         $env{'course.'.$env{'request.course.id'}.'.languages'}));
1.177     www      2834:     }
1.258     albertel 2835:     if ($env{'environment.languages'}) {
1.459     albertel 2836: 	@languages=(@languages,
                   2837: 		    split(/\s*(\,|\;|\:)\s*/,$env{'environment.languages'}));
1.118     www      2838:     }
1.583     albertel 2839:     my $browser=$ENV{'HTTP_ACCEPT_LANGUAGE'};
1.162     www      2840:     if ($browser) {
1.583     albertel 2841: 	my @browser = 
                   2842: 	    map { (split(/\s*;\s*/,$_))[0] } (split(/\s*,\s*/,$browser));
                   2843: 	push(@languages,@browser);
1.162     www      2844:     }
1.514     albertel 2845:     if (&Apache::lonnet::domain($env{'user.domain'},'lang_def')) {
1.118     www      2846: 	@languages=(@languages,
1.514     albertel 2847: 		    &Apache::lonnet::domain($env{'user.domain'},
                   2848: 					    'lang_def'));
1.118     www      2849:     }
1.514     albertel 2850:     if (&Apache::lonnet::domain($env{'request.role.domain'},'lang_def')) {
1.118     www      2851: 	@languages=(@languages,
1.514     albertel 2852: 		    &Apache::lonnet::domain($env{'request.role.domain'},
                   2853: 					    'lang_def'));
1.118     www      2854:     }
1.514     albertel 2855:     if (&Apache::lonnet::domain($Apache::lonnet::perlvar{'lonDefDomain'},
                   2856: 				'lang_def')) {
1.118     www      2857: 	@languages=(@languages,
1.514     albertel 2858: 		    &Apache::lonnet::domain($Apache::lonnet::perlvar{'lonDefDomain'},
                   2859: 					    'lang_def'));
1.118     www      2860:     }
                   2861: # turn "en-ca" into "en-ca,en"
                   2862:     my @genlanguages;
1.356     albertel 2863:     foreach my $lang (@languages) {
                   2864: 	unless ($lang=~/\w/) { next; }
1.583     albertel 2865: 	push(@genlanguages,$lang);
1.356     albertel 2866: 	if ($lang=~/(\-|\_)/) {
                   2867: 	    push(@genlanguages,(split(/(\-|\_)/,$lang))[0]);
1.118     www      2868: 	}
                   2869:     }
1.583     albertel 2870:     #uniqueify the languages list
                   2871:     my %count;
                   2872:     @genlanguages = map { $count{$_}++ == 0 ? $_ : () } @genlanguages;
1.118     www      2873:     return @genlanguages;
1.117     www      2874: }
                   2875: 
1.582     albertel 2876: sub languages {
                   2877:     my ($possible_langs) = @_;
                   2878:     my @preferred_langs = &preferred_languages();
                   2879:     if (!ref($possible_langs)) {
                   2880: 	if( wantarray ) {
                   2881: 	    return @preferred_langs;
                   2882: 	} else {
                   2883: 	    return $preferred_langs[0];
                   2884: 	}
                   2885:     }
                   2886:     my %possibilities = map { $_ => 1 } (@$possible_langs);
                   2887:     my @preferred_possibilities;
                   2888:     foreach my $preferred_lang (@preferred_langs) {
                   2889: 	if (exists($possibilities{$preferred_lang})) {
                   2890: 	    push(@preferred_possibilities, $preferred_lang);
                   2891: 	}
                   2892:     }
                   2893:     if( wantarray ) {
                   2894: 	return @preferred_possibilities;
                   2895:     }
                   2896:     return $preferred_possibilities[0];
                   2897: }
                   2898: 
1.112     bowersj2 2899: ###############################################################
                   2900: ##               Student Answer Attempts                     ##
                   2901: ###############################################################
                   2902: 
                   2903: =pod
                   2904: 
                   2905: =head1 Alternate Problem Views
                   2906: 
                   2907: =over 4
                   2908: 
                   2909: =item * get_previous_attempt($symb, $username, $domain, $course,
                   2910:     $getattempt, $regexp, $gradesub)
                   2911: 
                   2912: Return string with previous attempt on problem. Arguments:
                   2913: 
                   2914: =over 4
                   2915: 
                   2916: =item * $symb: Problem, including path
                   2917: 
                   2918: =item * $username: username of the desired student
                   2919: 
                   2920: =item * $domain: domain of the desired student
1.14      harris41 2921: 
1.112     bowersj2 2922: =item * $course: Course ID
1.14      harris41 2923: 
1.112     bowersj2 2924: =item * $getattempt: Leave blank for all attempts, otherwise put
                   2925:     something
1.14      harris41 2926: 
1.112     bowersj2 2927: =item * $regexp: if string matches this regexp, the string will be
                   2928:     sent to $gradesub
1.14      harris41 2929: 
1.112     bowersj2 2930: =item * $gradesub: routine that processes the string if it matches $regexp
1.14      harris41 2931: 
1.112     bowersj2 2932: =back
1.14      harris41 2933: 
1.112     bowersj2 2934: The output string is a table containing all desired attempts, if any.
1.16      harris41 2935: 
1.112     bowersj2 2936: =cut
1.1       albertel 2937: 
                   2938: sub get_previous_attempt {
1.43      ng       2939:   my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub)=@_;
1.1       albertel 2940:   my $prevattempts='';
1.43      ng       2941:   no strict 'refs';
1.1       albertel 2942:   if ($symb) {
1.3       albertel 2943:     my (%returnhash)=
                   2944:       &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1       albertel 2945:     if ($returnhash{'version'}) {
                   2946:       my %lasthash=();
                   2947:       my $version;
                   2948:       for ($version=1;$version<=$returnhash{'version'};$version++) {
1.356     albertel 2949:         foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
                   2950: 	  $lasthash{$key}=$returnhash{$version.':'.$key};
1.19      harris41 2951:         }
1.1       albertel 2952:       }
1.43      ng       2953:       $prevattempts='<table border="0" width="100%"><tr><td bgcolor="#777777">';
1.40      ng       2954:       $prevattempts.='<table border="0" width="100%"><tr bgcolor="#e6ffff"><td>History</td>';
1.356     albertel 2955:       foreach my $key (sort(keys(%lasthash))) {
                   2956: 	my ($ign,@parts) = split(/\./,$key);
1.41      ng       2957: 	if ($#parts > 0) {
1.31      albertel 2958: 	  my $data=$parts[-1];
                   2959: 	  pop(@parts);
1.40      ng       2960: 	  $prevattempts.='<td>Part '.join('.',@parts).'<br />'.$data.'&nbsp;</td>';
1.31      albertel 2961: 	} else {
1.41      ng       2962: 	  if ($#parts == 0) {
                   2963: 	    $prevattempts.='<th>'.$parts[0].'</th>';
                   2964: 	  } else {
                   2965: 	    $prevattempts.='<th>'.$ign.'</th>';
                   2966: 	  }
1.31      albertel 2967: 	}
1.16      harris41 2968:       }
1.40      ng       2969:       if ($getattempt eq '') {
                   2970: 	for ($version=1;$version<=$returnhash{'version'};$version++) {
                   2971: 	  $prevattempts.='</tr><tr bgcolor="#ffffe6"><td>Transaction '.$version.'</td>';
1.356     albertel 2972: 	    foreach my $key (sort(keys(%lasthash))) {
1.581     albertel 2973: 		my $value = &format_previous_attempt_value($key,
                   2974: 							   $returnhash{$version.':'.$key});
                   2975: 		$prevattempts.='<td>'.$value.'&nbsp;</td>';   
1.40      ng       2976: 	    }
                   2977: 	 }
1.1       albertel 2978:       }
1.40      ng       2979:       $prevattempts.='</tr><tr bgcolor="#ffffe6"><td>Current</td>';
1.356     albertel 2980:       foreach my $key (sort(keys(%lasthash))) {
1.581     albertel 2981: 	my $value = &format_previous_attempt_value($key,$lasthash{$key});
1.356     albertel 2982: 	if ($key =~/$regexp$/ && (defined &$gradesub)) {$value = &$gradesub($value)}
1.40      ng       2983: 	$prevattempts.='<td>'.$value.'&nbsp;</td>';
1.16      harris41 2984:       }
1.40      ng       2985:       $prevattempts.='</tr></table></td></tr></table>';
1.1       albertel 2986:     } else {
                   2987:       $prevattempts='Nothing submitted - no attempts.';
                   2988:     }
                   2989:   } else {
                   2990:     $prevattempts='No data.';
                   2991:   }
1.10      albertel 2992: }
                   2993: 
1.581     albertel 2994: sub format_previous_attempt_value {
                   2995:     my ($key,$value) = @_;
                   2996:     if ($key =~ /timestamp/) {
                   2997: 	$value = &Apache::lonlocal::locallocaltime($value);
                   2998:     } elsif (ref($value) eq 'ARRAY') {
                   2999: 	$value = '('.join(', ', @{ $value }).')';
                   3000:     } else {
                   3001: 	$value = &unescape($value);
                   3002:     }
                   3003:     return $value;
                   3004: }
                   3005: 
                   3006: 
1.107     albertel 3007: sub relative_to_absolute {
                   3008:     my ($url,$output)=@_;
                   3009:     my $parser=HTML::TokeParser->new(\$output);
                   3010:     my $token;
                   3011:     my $thisdir=$url;
                   3012:     my @rlinks=();
                   3013:     while ($token=$parser->get_token) {
                   3014: 	if ($token->[0] eq 'S') {
                   3015: 	    if ($token->[1] eq 'a') {
                   3016: 		if ($token->[2]->{'href'}) {
                   3017: 		    $rlinks[$#rlinks+1]=$token->[2]->{'href'};
                   3018: 		}
                   3019: 	    } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
                   3020: 		$rlinks[$#rlinks+1]=$token->[2]->{'src'};
                   3021: 	    } elsif ($token->[1] eq 'base') {
                   3022: 		$thisdir=$token->[2]->{'href'};
                   3023: 	    }
                   3024: 	}
                   3025:     }
                   3026:     $thisdir=~s-/[^/]*$--;
1.356     albertel 3027:     foreach my $link (@rlinks) {
                   3028: 	unless (($link=~/^http:\/\//i) ||
                   3029: 		($link=~/^\//) ||
                   3030: 		($link=~/^javascript:/i) ||
                   3031: 		($link=~/^mailto:/i) ||
                   3032: 		($link=~/^\#/)) {
                   3033: 	    my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
                   3034: 	    $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107     albertel 3035: 	}
                   3036:     }
                   3037: # -------------------------------------------------- Deal with Applet codebases
                   3038:     $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
                   3039:     return $output;
                   3040: }
                   3041: 
1.112     bowersj2 3042: =pod
                   3043: 
                   3044: =item * get_student_view
                   3045: 
                   3046: show a snapshot of what student was looking at
                   3047: 
                   3048: =cut
                   3049: 
1.10      albertel 3050: sub get_student_view {
1.186     albertel 3051:   my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114     www      3052:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 3053:   my (%form);
1.10      albertel 3054:   my @elements=('symb','courseid','domain','username');
                   3055:   foreach my $element (@elements) {
1.186     albertel 3056:       $form{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 3057:   }
1.186     albertel 3058:   if (defined($moreenv)) {
                   3059:       %form=(%form,%{$moreenv});
                   3060:   }
1.236     albertel 3061:   if (defined($target)) { $form{'grade_target'} = $target; }
1.107     albertel 3062:   $feedurl=&Apache::lonnet::clutter($feedurl);
1.186     albertel 3063:   my $userview=&Apache::lonnet::ssi_body($feedurl,%form);
1.11      albertel 3064:   $userview=~s/\<body[^\>]*\>//gi;
                   3065:   $userview=~s/\<\/body\>//gi;
                   3066:   $userview=~s/\<html\>//gi;
                   3067:   $userview=~s/\<\/html\>//gi;
                   3068:   $userview=~s/\<head\>//gi;
                   3069:   $userview=~s/\<\/head\>//gi;
                   3070:   $userview=~s/action\s*\=/would_be_action\=/gi;
1.107     albertel 3071:   $userview=&relative_to_absolute($feedurl,$userview);
1.11      albertel 3072:   return $userview;
                   3073: }
                   3074: 
1.112     bowersj2 3075: =pod
                   3076: 
                   3077: =item * get_student_answers() 
                   3078: 
                   3079: show a snapshot of how student was answering problem
                   3080: 
                   3081: =cut
                   3082: 
1.11      albertel 3083: sub get_student_answers {
1.100     sakharuk 3084:   my ($symb,$username,$domain,$courseid,%form) = @_;
1.114     www      3085:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 3086:   my (%moreenv);
1.11      albertel 3087:   my @elements=('symb','courseid','domain','username');
                   3088:   foreach my $element (@elements) {
1.186     albertel 3089:     $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 3090:   }
1.186     albertel 3091:   $moreenv{'grade_target'}='answer';
                   3092:   %moreenv=(%form,%moreenv);
1.497     raeburn  3093:   $feedurl = &Apache::lonnet::clutter($feedurl);
                   3094:   my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10      albertel 3095:   return $userview;
1.1       albertel 3096: }
1.116     albertel 3097: 
                   3098: =pod
                   3099: 
                   3100: =item * &submlink()
                   3101: 
1.242     albertel 3102: Inputs: $text $uname $udom $symb $target
1.116     albertel 3103: 
                   3104: Returns: A link to grades.pm such as to see the SUBM view of a student
                   3105: 
                   3106: =cut
                   3107: 
                   3108: ###############################################
                   3109: sub submlink {
1.242     albertel 3110:     my ($text,$uname,$udom,$symb,$target)=@_;
1.116     albertel 3111:     if (!($uname && $udom)) {
                   3112: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 3113: 	    &Apache::lonnet::whichuser($symb);
1.116     albertel 3114: 	if (!$symb) { $symb=$cursymb; }
                   3115:     }
1.254     matthew  3116:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      3117:     $symb=&escape($symb);
1.242     albertel 3118:     if ($target) { $target="target=\"$target\""; }
                   3119:     return '<a href="/adm/grades?&command=submission&'.
                   3120: 	'symb='.$symb.'&student='.$uname.
                   3121: 	'&userdom='.$udom.'" '.$target.'>'.$text.'</a>';
                   3122: }
                   3123: ##############################################
                   3124: 
                   3125: =pod
                   3126: 
                   3127: =item * &pgrdlink()
                   3128: 
                   3129: Inputs: $text $uname $udom $symb $target
                   3130: 
                   3131: Returns: A link to grades.pm such as to see the PGRD view of a student
                   3132: 
                   3133: =cut
                   3134: 
                   3135: ###############################################
                   3136: sub pgrdlink {
                   3137:     my $link=&submlink(@_);
                   3138:     $link=~s/(&command=submission)/$1&showgrading=yes/;
                   3139:     return $link;
                   3140: }
                   3141: ##############################################
                   3142: 
                   3143: =pod
                   3144: 
                   3145: =item * &pprmlink()
                   3146: 
                   3147: Inputs: $text $uname $udom $symb $target
                   3148: 
                   3149: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283     albertel 3150: student and a specific resource
1.242     albertel 3151: 
                   3152: =cut
                   3153: 
                   3154: ###############################################
                   3155: sub pprmlink {
                   3156:     my ($text,$uname,$udom,$symb,$target)=@_;
                   3157:     if (!($uname && $udom)) {
                   3158: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 3159: 	    &Apache::lonnet::whichuser($symb);
1.242     albertel 3160: 	if (!$symb) { $symb=$cursymb; }
                   3161:     }
1.254     matthew  3162:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      3163:     $symb=&escape($symb);
1.242     albertel 3164:     if ($target) { $target="target=\"$target\""; }
                   3165:     return '<a href="/adm/parmset?&command=set&'.
                   3166: 	'symb='.$symb.'&uname='.$uname.
                   3167: 	'&udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116     albertel 3168: }
                   3169: ##############################################
1.37      matthew  3170: 
1.112     bowersj2 3171: =pod
                   3172: 
                   3173: =back
                   3174: 
                   3175: =cut
                   3176: 
1.37      matthew  3177: ###############################################
1.51      www      3178: 
                   3179: 
                   3180: sub timehash {
                   3181:     my @ltime=localtime(shift);
                   3182:     return ( 'seconds' => $ltime[0],
                   3183:              'minutes' => $ltime[1],
                   3184:              'hours'   => $ltime[2],
                   3185:              'day'     => $ltime[3],
                   3186:              'month'   => $ltime[4]+1,
                   3187:              'year'    => $ltime[5]+1900,
                   3188:              'weekday' => $ltime[6],
                   3189:              'dayyear' => $ltime[7]+1,
                   3190:              'dlsav'   => $ltime[8] );
                   3191: }
                   3192: 
1.370     www      3193: sub utc_string {
                   3194:     my ($date)=@_;
1.371     www      3195:     return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370     www      3196: }
                   3197: 
1.51      www      3198: sub maketime {
                   3199:     my %th=@_;
                   3200:     return POSIX::mktime(
                   3201:         ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210     www      3202:          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70      www      3203: }
                   3204: 
                   3205: #########################################
1.51      www      3206: 
                   3207: sub findallcourses {
1.482     raeburn  3208:     my ($roles,$uname,$udom) = @_;
1.355     albertel 3209:     my %roles;
                   3210:     if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348     albertel 3211:     my %courses;
1.51      www      3212:     my $now=time;
1.482     raeburn  3213:     if (!defined($uname)) {
                   3214:         $uname = $env{'user.name'};
                   3215:     }
                   3216:     if (!defined($udom)) {
                   3217:         $udom = $env{'user.domain'};
                   3218:     }
                   3219:     if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
                   3220:         my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
                   3221:         if (!%roles) {
                   3222:             %roles = (
                   3223:                        cc => 1,
                   3224:                        in => 1,
                   3225:                        ep => 1,
                   3226:                        ta => 1,
                   3227:                        cr => 1,
                   3228:                        st => 1,
                   3229:              );
                   3230:         }
                   3231:         foreach my $entry (keys(%roleshash)) {
                   3232:             my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
                   3233:             if ($trole =~ /^cr/) { 
                   3234:                 next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
                   3235:             } else {
                   3236:                 next if (!exists($roles{$trole}));
                   3237:             }
                   3238:             if ($tend) {
                   3239:                 next if ($tend < $now);
                   3240:             }
                   3241:             if ($tstart) {
                   3242:                 next if ($tstart > $now);
                   3243:             }
                   3244:             my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role,$realsec);
                   3245:             (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
                   3246:             if ($secpart eq '') {
                   3247:                 ($cnum,$role) = split(/_/,$cnumpart); 
                   3248:                 $sec = 'none';
                   3249:                 $realsec = '';
                   3250:             } else {
                   3251:                 $cnum = $cnumpart;
                   3252:                 ($sec,$role) = split(/_/,$secpart);
                   3253:                 $realsec = $sec;
1.490     raeburn  3254:             }
1.482     raeburn  3255:             $courses{$cdom.'_'.$cnum}{$sec} = $trole.'/'.$cdom.'/'.$cnum.'/'.$realsec;
                   3256:         }
                   3257:     } else {
                   3258:         foreach my $key (keys(%env)) {
1.483     albertel 3259: 	    if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
                   3260:                  $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482     raeburn  3261: 	        my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
                   3262: 	        next if ($role eq 'ca' || $role eq 'aa');
                   3263: 	        next if (%roles && !exists($roles{$role}));
                   3264: 	        my ($starttime,$endtime)=split(/\./,$env{$key});
                   3265:                 my $active=1;
                   3266:                 if ($starttime) {
                   3267: 		    if ($now<$starttime) { $active=0; }
                   3268:                 }
                   3269:                 if ($endtime) {
                   3270:                     if ($now>$endtime) { $active=0; }
                   3271:                 }
                   3272:                 if ($active) {
                   3273:                     if ($sec eq '') {
                   3274:                         $sec = 'none';
                   3275:                     }
                   3276:                     $courses{$cdom.'_'.$cnum}{$sec} = 
                   3277:                                      $role.'/'.$cdom.'/'.$cnum.'/'.$sec;
1.474     raeburn  3278:                 }
                   3279:             }
1.51      www      3280:         }
                   3281:     }
1.474     raeburn  3282:     return %courses;
1.51      www      3283: }
1.37      matthew  3284: 
1.54      www      3285: ###############################################
1.474     raeburn  3286: 
                   3287: sub blockcheck {
1.482     raeburn  3288:     my ($setters,$activity,$uname,$udom) = @_;
1.490     raeburn  3289: 
                   3290:     if (!defined($udom)) {
                   3291:         $udom = $env{'user.domain'};
                   3292:     }
                   3293:     if (!defined($uname)) {
                   3294:         $uname = $env{'user.name'};
                   3295:     }
                   3296: 
                   3297:     # If uname and udom are for a course, check for blocks in the course.
                   3298: 
                   3299:     if (&Apache::lonnet::is_course($udom,$uname)) {
                   3300:         my %records = &Apache::lonnet::dump('comm_block',$udom,$uname);
1.502     raeburn  3301:         my ($startblock,$endblock)=&get_blocks($setters,$activity,$udom,$uname);
1.490     raeburn  3302:         return ($startblock,$endblock);
                   3303:     }
1.474     raeburn  3304: 
1.502     raeburn  3305:     my $startblock = 0;
                   3306:     my $endblock = 0;
1.482     raeburn  3307:     my %live_courses = &findallcourses(undef,$uname,$udom);
1.474     raeburn  3308: 
1.490     raeburn  3309:     # If uname is for a user, and activity is course-specific, i.e.,
                   3310:     # boards, chat or groups, check for blocking in current course only.
1.474     raeburn  3311: 
1.490     raeburn  3312:     if (($activity eq 'boards' || $activity eq 'chat' ||
                   3313:          $activity eq 'groups') && ($env{'request.course.id'})) {
                   3314:         foreach my $key (keys(%live_courses)) {
                   3315:             if ($key ne $env{'request.course.id'}) {
                   3316:                 delete($live_courses{$key});
                   3317:             }
                   3318:         }
                   3319:     }
                   3320: 
                   3321:     my $otheruser = 0;
                   3322:     my %own_courses;
                   3323:     if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
                   3324:         # Resource belongs to user other than current user.
                   3325:         $otheruser = 1;
                   3326:         # Gather courses for current user
                   3327:         %own_courses = 
                   3328:             &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
                   3329:     }
                   3330: 
                   3331:     # Gather active course roles - course coordinator, instructor, 
                   3332:     # exam proctor, ta, student, or custom role.
1.474     raeburn  3333: 
                   3334:     foreach my $course (keys(%live_courses)) {
1.482     raeburn  3335:         my ($cdom,$cnum);
                   3336:         if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
                   3337:             $cdom = $env{'course.'.$course.'.domain'};
                   3338:             $cnum = $env{'course.'.$course.'.num'};
                   3339:         } else {
1.490     raeburn  3340:             ($cdom,$cnum) = split(/_/,$course); 
1.482     raeburn  3341:         }
                   3342:         my $no_ownblock = 0;
                   3343:         my $no_userblock = 0;
1.533     raeburn  3344:         if ($otheruser && $activity ne 'com') {
1.490     raeburn  3345:             # Check if current user has 'evb' priv for this
                   3346:             if (defined($own_courses{$course})) {
                   3347:                 foreach my $sec (keys(%{$own_courses{$course}})) {
                   3348:                     my $checkrole = 'cm./'.$cdom.'/'.$cnum;
                   3349:                     if ($sec ne 'none') {
                   3350:                         $checkrole .= '/'.$sec;
                   3351:                     }
                   3352:                     if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   3353:                         $no_ownblock = 1;
                   3354:                         last;
                   3355:                     }
                   3356:                 }
                   3357:             }
                   3358:             # if they have 'evb' priv and are currently not playing student
                   3359:             next if (($no_ownblock) &&
                   3360:                  ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
                   3361:         }
1.474     raeburn  3362:         foreach my $sec (keys(%{$live_courses{$course}})) {
1.482     raeburn  3363:             my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474     raeburn  3364:             if ($sec ne 'none') {
1.482     raeburn  3365:                 $checkrole .= '/'.$sec;
1.474     raeburn  3366:             }
1.490     raeburn  3367:             if ($otheruser) {
                   3368:                 # Resource belongs to user other than current user.
                   3369:                 # Assemble privs for that user, and check for 'evb' priv.
1.482     raeburn  3370:                 my ($trole,$tdom,$tnum,$tsec);
                   3371:                 my $entry = $live_courses{$course}{$sec};
                   3372:                 if ($entry =~ /^cr/) {
                   3373:                     ($trole,$tdom,$tnum,$tsec) = 
                   3374:                       ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
                   3375:                 } else {
                   3376:                     ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
                   3377:                 }
                   3378:                 my ($spec,$area,$trest,%allroles,%userroles);
                   3379:                 $area = '/'.$tdom.'/'.$tnum;
                   3380:                 $trest = $tnum;
                   3381:                 if ($tsec ne '') {
                   3382:                     $area .= '/'.$tsec;
                   3383:                     $trest .= '/'.$tsec;
                   3384:                 }
                   3385:                 $spec = $trole.'.'.$area;
                   3386:                 if ($trole =~ /^cr/) {
                   3387:                     &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
                   3388:                                                       $tdom,$spec,$trest,$area);
                   3389:                 } else {
                   3390:                     &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
                   3391:                                                        $tdom,$spec,$trest,$area);
                   3392:                 }
                   3393:                 my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
1.486     raeburn  3394:                 if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
                   3395:                     if ($1) {
                   3396:                         $no_userblock = 1;
                   3397:                         last;
                   3398:                     }
                   3399:                 }
1.490     raeburn  3400:             } else {
                   3401:                 # Resource belongs to current user
                   3402:                 # Check for 'evb' priv via lonnet::allowed().
1.482     raeburn  3403:                 if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   3404:                     $no_ownblock = 1;
                   3405:                     last;
                   3406:                 }
1.474     raeburn  3407:             }
                   3408:         }
                   3409:         # if they have the evb priv and are currently not playing student
1.482     raeburn  3410:         next if (($no_ownblock) &&
1.491     albertel 3411:                  ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482     raeburn  3412:         next if ($no_userblock);
1.474     raeburn  3413: 
1.490     raeburn  3414:         # Retrieve blocking times and identity of blocker for course
                   3415:         # of specified user, unless user has 'evb' privilege.
1.502     raeburn  3416:         
                   3417:         my ($start,$end)=&get_blocks($setters,$activity,$cdom,$cnum);
                   3418:         if (($start != 0) && 
                   3419:             (($startblock == 0) || ($startblock > $start))) {
                   3420:             $startblock = $start;
                   3421:         }
                   3422:         if (($end != 0)  &&
                   3423:             (($endblock == 0) || ($endblock < $end))) {
                   3424:             $endblock = $end;
                   3425:         }
1.490     raeburn  3426:     }
                   3427:     return ($startblock,$endblock);
                   3428: }
                   3429: 
                   3430: sub get_blocks {
                   3431:     my ($setters,$activity,$cdom,$cnum) = @_;
                   3432:     my $startblock = 0;
                   3433:     my $endblock = 0;
                   3434:     my $course = $cdom.'_'.$cnum;
                   3435:     $setters->{$course} = {};
                   3436:     $setters->{$course}{'staff'} = [];
                   3437:     $setters->{$course}{'times'} = [];
                   3438:     my %records = &Apache::lonnet::dump('comm_block',$cdom,$cnum);
                   3439:     foreach my $record (keys(%records)) {
                   3440:         my ($start,$end) = ($record =~ m/^(\d+)____(\d+)$/);
                   3441:         if ($start <= time && $end >= time) {
                   3442:             my ($staff_name,$staff_dom,$title,$blocks) =
                   3443:                 &parse_block_record($records{$record});
                   3444:             if ($blocks->{$activity} eq 'on') {
                   3445:                 push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
                   3446:                 push(@{$$setters{$course}{'times'}}, [$start,$end]);
1.491     albertel 3447:                 if ( ($startblock == 0) || ($startblock > $start) ) {
                   3448:                     $startblock = $start;
1.490     raeburn  3449:                 }
1.491     albertel 3450:                 if ( ($endblock == 0) || ($endblock < $end) ) {
                   3451:                     $endblock = $end;
1.474     raeburn  3452:                 }
                   3453:             }
                   3454:         }
                   3455:     }
                   3456:     return ($startblock,$endblock);
                   3457: }
                   3458: 
                   3459: sub parse_block_record {
                   3460:     my ($record) = @_;
                   3461:     my ($setuname,$setudom,$title,$blocks);
                   3462:     if (ref($record) eq 'HASH') {
                   3463:         ($setuname,$setudom) = split(/:/,$record->{'setter'});
                   3464:         $title = &unescape($record->{'event'});
                   3465:         $blocks = $record->{'blocks'};
                   3466:     } else {
                   3467:         my @data = split(/:/,$record,3);
                   3468:         if (scalar(@data) eq 2) {
                   3469:             $title = $data[1];
                   3470:             ($setuname,$setudom) = split(/@/,$data[0]);
                   3471:         } else {
                   3472:             ($setuname,$setudom,$title) = @data;
                   3473:         }
                   3474:         $blocks = { 'com' => 'on' };
                   3475:     }
                   3476:     return ($setuname,$setudom,$title,$blocks);
                   3477: }
                   3478: 
                   3479: sub build_block_table {
                   3480:     my ($startblock,$endblock,$setters) = @_;
                   3481:     my %lt = &Apache::lonlocal::texthash(
                   3482:         'cacb' => 'Currently active communication blocks',
                   3483:         'cour' => 'Course',
                   3484:         'dura' => 'Duration',
                   3485:         'blse' => 'Block set by'
                   3486:     );
                   3487:     my $output;
1.476     raeburn  3488:     $output = '<br />'.$lt{'cacb'}.':<br />';
1.474     raeburn  3489:     $output .= &start_data_table();
                   3490:     $output .= '
                   3491: <tr>
                   3492:  <th>'.$lt{'cour'}.'</th>
                   3493:  <th>'.$lt{'dura'}.'</th>
                   3494:  <th>'.$lt{'blse'}.'</th>
                   3495: </tr>
                   3496: ';
                   3497:     foreach my $course (keys(%{$setters})) {
                   3498:         my %courseinfo=&Apache::lonnet::coursedescription($course);
                   3499:         for (my $i=0; $i<@{$$setters{$course}{staff}}; $i++) {
                   3500:             my ($uname,$udom) = @{$$setters{$course}{staff}[$i]};
1.490     raeburn  3501:             my $fullname = &plainname($uname,$udom);
                   3502:             if (defined($env{'user.name'}) && defined($env{'user.domain'})
                   3503:                 && $env{'user.name'} ne 'public' 
                   3504:                 && $env{'user.domain'} ne 'public') {
                   3505:                 $fullname = &aboutmewrapper($fullname,$uname,$udom);
                   3506:             }
1.474     raeburn  3507:             my ($openblock,$closeblock) = @{$$setters{$course}{times}[$i]};
                   3508:             $openblock = &Apache::lonlocal::locallocaltime($openblock);
                   3509:             $closeblock= &Apache::lonlocal::locallocaltime($closeblock);
                   3510:             $output .= &Apache::loncommon::start_data_table_row().
                   3511:                        '<td>'.$courseinfo{'description'}.'</td>'.
                   3512:                        '<td>'.$openblock.' to '.$closeblock.'</td>'.
1.490     raeburn  3513:                        '<td>'.$fullname.'</td>'.
1.474     raeburn  3514:                         &Apache::loncommon::end_data_table_row();
                   3515:         }
                   3516:     }
                   3517:     $output .= &end_data_table();
                   3518: }
                   3519: 
1.490     raeburn  3520: sub blocking_status {
                   3521:     my ($activity,$uname,$udom) = @_;
                   3522:     my %setters;
                   3523:     my ($blocked,$output,$ownitem,$is_course);
                   3524:     my ($startblock,$endblock)=&blockcheck(\%setters,$activity,$uname,$udom);
                   3525:     if ($startblock && $endblock) {
                   3526:         $blocked = 1;
                   3527:         if (wantarray) {
                   3528:             my $category;
                   3529:             if ($activity eq 'boards') {
                   3530:                 $category = 'Discussion posts in this course';
                   3531:             } elsif ($activity eq 'blogs') {
                   3532:                 $category = 'Blogs';
                   3533:             } elsif ($activity eq 'port') {
                   3534:                 if (defined($uname) && defined($udom)) {
                   3535:                     if ($uname eq $env{'user.name'} &&
                   3536:                         $udom eq $env{'user.domain'}) {
                   3537:                         $ownitem = 1;
                   3538:                     }
                   3539:                 }
                   3540:                 $is_course = &Apache::lonnet::is_course($udom,$uname);
                   3541:                 if ($ownitem) { 
                   3542:                     $category = 'Your portfolio files';  
                   3543:                 } elsif ($is_course) {
                   3544:                     my $coursedesc;
                   3545:                     foreach my $course (keys(%setters)) {
                   3546:                         my %courseinfo =
                   3547:                              &Apache::lonnet::coursedescription($course);
                   3548:                         $coursedesc = $courseinfo{'description'};
                   3549:                     }
                   3550:                     $category = "Group files in the course '$coursedesc'";
                   3551:                 } else {
                   3552:                     $category = 'Portfolio files belonging to ';
                   3553:                     if ($env{'user.name'} eq 'public' && 
                   3554:                         $env{'user.domain'} eq 'public') {
                   3555:                         $category .= &plainname($uname,$udom);
                   3556:                     } else {
                   3557:                         $category .= &aboutmewrapper(&plainname($uname,$udom),$uname,$udom);  
                   3558:                     }
                   3559:                 }
                   3560:             } elsif ($activity eq 'groups') {
                   3561:                 $category = 'Groups in this course';
                   3562:             }
                   3563:             my $showstart = &Apache::lonlocal::locallocaltime($startblock);
                   3564:             my $showend = &Apache::lonlocal::locallocaltime($endblock);
                   3565:             $output = '<br />'.&mt('[_1] will be inaccessible between [_2] and [_3] because communication is being blocked.',$category,$showstart,$showend).'<br />';
                   3566:             if (!($activity eq 'port' && !($ownitem) && !($is_course))) { 
                   3567:                 $output .= &build_block_table($startblock,$endblock,\%setters);
                   3568:             }
                   3569:         }
                   3570:     }
                   3571:     if (wantarray) {
                   3572:         return ($blocked,$output);
                   3573:     } else {
                   3574:         return $blocked;
                   3575:     }
                   3576: }
                   3577: 
1.60      matthew  3578: ###############################################
                   3579: 
                   3580: =pod
                   3581: 
1.112     bowersj2 3582: =head1 Domain Template Functions
                   3583: 
                   3584: =over 4
                   3585: 
                   3586: =item * &determinedomain()
1.60      matthew  3587: 
                   3588: Inputs: $domain (usually will be undef)
                   3589: 
1.63      www      3590: Returns: Determines which domain should be used for designs
1.60      matthew  3591: 
                   3592: =cut
1.54      www      3593: 
1.60      matthew  3594: ###############################################
1.63      www      3595: sub determinedomain {
                   3596:     my $domain=shift;
1.531     albertel 3597:     if (! $domain) {
1.60      matthew  3598:         # Determine domain if we have not been given one
                   3599:         $domain = $Apache::lonnet::perlvar{'lonDefDomain'};
1.258     albertel 3600:         if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
                   3601:         if ($env{'request.role.domain'}) { 
                   3602:             $domain=$env{'request.role.domain'}; 
1.60      matthew  3603:         }
                   3604:     }
1.63      www      3605:     return $domain;
                   3606: }
                   3607: ###############################################
1.517     raeburn  3608: 
1.518     albertel 3609: sub devalidate_domconfig_cache {
                   3610:     my ($udom)=@_;
                   3611:     &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
                   3612: }
                   3613: 
                   3614: # ---------------------- Get domain configuration for a domain
                   3615: sub get_domainconf {
                   3616:     my ($udom) = @_;
                   3617:     my $cachetime=1800;
                   3618:     my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
                   3619:     if (defined($cached)) { return %{$result}; }
                   3620: 
                   3621:     my %domconfig = &Apache::lonnet::get_dom('configuration',
                   3622: 					     ['login','rolecolors'],$udom);
                   3623:     my %designhash;
                   3624:     if (keys(%domconfig) > 0) {
                   3625:         if (ref($domconfig{'login'}) eq 'HASH') {
                   3626:             foreach my $key (keys(%{$domconfig{'login'}})) {
                   3627:                 $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
                   3628:             }
                   3629:         }
                   3630:         if (ref($domconfig{'rolecolors'}) eq 'HASH') {
                   3631:             foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
                   3632:                 if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
                   3633:                     foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
                   3634:                         $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
                   3635:                     }
                   3636:                 }
                   3637:             }
                   3638:         }
                   3639:     } else {
                   3640:         my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
                   3641:         my $designfile =  $designdir.'/'.$udom.'.tab';
                   3642:         if (-e $designfile) {
                   3643:             if ( open (my $fh,"<$designfile") ) {
                   3644:                 while (my $line = <$fh>) {
                   3645:                     next if ($line =~ /^\#/);
                   3646:                     chomp($line);
                   3647:                     my ($key,$val)=(split(/\=/,$line));
                   3648:                     if ($val) { $designhash{$udom.'.'.$key}=$val; }
                   3649:                 }
                   3650:                 close($fh);
                   3651:             }
                   3652:         }
                   3653:         if (-e '/home/httpd/html/adm/lonDomLogos/'.$udom.'.gif') {
1.519     raeburn  3654:             $designhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
1.518     albertel 3655:         }
                   3656:     }
                   3657:     &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
                   3658: 				  $cachetime);
                   3659:     return %designhash;
                   3660: }
                   3661: 
1.63      www      3662: =pod
                   3663: 
1.112     bowersj2 3664: =item * &domainlogo()
1.63      www      3665: 
                   3666: Inputs: $domain (usually will be undef)
                   3667: 
                   3668: Returns: A link to a domain logo, if the domain logo exists.
                   3669: If the domain logo does not exist, a description of the domain.
                   3670: 
                   3671: =cut
1.112     bowersj2 3672: 
1.63      www      3673: ###############################################
                   3674: sub domainlogo {
1.517     raeburn  3675:     my $domain = &determinedomain(shift);
1.518     albertel 3676:     my %designhash = &get_domainconf($domain);    
1.517     raeburn  3677:     # See if there is a logo
                   3678:     if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519     raeburn  3679:         my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538     albertel 3680:         if ($imgsrc =~ m{^/(adm|res)/}) {
                   3681: 	    if ($imgsrc =~ m{^/res/}) {
                   3682: 		my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
                   3683: 		&Apache::lonnet::repcopy($local_name);
                   3684: 	    }
                   3685: 	   $imgsrc = &lonhttpdurl($imgsrc);
1.519     raeburn  3686:         } 
                   3687:         return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514     albertel 3688:     } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
                   3689:         return &Apache::lonnet::domain($domain,'description');
1.59      www      3690:     } else {
1.60      matthew  3691:         return '';
1.59      www      3692:     }
                   3693: }
1.63      www      3694: ##############################################
                   3695: 
                   3696: =pod
                   3697: 
1.112     bowersj2 3698: =item * &designparm()
1.63      www      3699: 
                   3700: Inputs: $which parameter; $domain (usually will be undef)
                   3701: 
                   3702: Returns: value of designparamter $which
                   3703: 
                   3704: =cut
1.112     bowersj2 3705: 
1.397     albertel 3706: 
1.400     albertel 3707: ##############################################
1.397     albertel 3708: sub designparm {
                   3709:     my ($which,$domain)=@_;
1.258     albertel 3710:     if ($env{'browser.blackwhite'} eq 'on') {
1.110     www      3711: 	if ($which=~/\.(font|alink|vlink|link)$/) {
                   3712: 	    return '#000000';
                   3713: 	}
                   3714: 	if ($which=~/\.(pgbg|sidebg)$/) {
                   3715: 	    return '#FFFFFF';
                   3716: 	}
                   3717: 	if ($which=~/\.tabbg$/) {
                   3718: 	    return '#CCCCCC';
                   3719: 	}
                   3720:     }
1.397     albertel 3721:     if (exists($env{'environment.color.'.$which})) {
1.258     albertel 3722: 	return $env{'environment.color.'.$which};
1.96      www      3723:     }
1.63      www      3724:     $domain=&determinedomain($domain);
1.518     albertel 3725:     my %domdesign = &get_domainconf($domain);
1.520     raeburn  3726:     my $output;
1.517     raeburn  3727:     if ($domdesign{$domain.'.'.$which} ne '') {
1.520     raeburn  3728: 	$output = $domdesign{$domain.'.'.$which};
1.63      www      3729:     } else {
1.520     raeburn  3730:         $output = $defaultdesign{$which};
                   3731:     }
                   3732:     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
                   3733:         ($which =~ /login\.(img|logo|domlogo)/)) {
1.538     albertel 3734:         if ($output =~ m{^/(adm|res)/}) {
                   3735: 	    if ($output =~ m{^/res/}) {
                   3736: 		my $local_name = &Apache::lonnet::filelocation('',$output);
                   3737: 		&Apache::lonnet::repcopy($local_name);
                   3738: 	    }
1.520     raeburn  3739:             $output = &lonhttpdurl($output);
                   3740:         }
1.63      www      3741:     }
1.520     raeburn  3742:     return $output;
1.63      www      3743: }
1.59      www      3744: 
1.60      matthew  3745: ###############################################
                   3746: ###############################################
                   3747: 
                   3748: =pod
                   3749: 
1.112     bowersj2 3750: =back
                   3751: 
1.549     albertel 3752: =head1 HTML Helpers
1.112     bowersj2 3753: 
                   3754: =over 4
                   3755: 
                   3756: =item * &bodytag()
1.60      matthew  3757: 
                   3758: Returns a uniform header for LON-CAPA web pages.
                   3759: 
                   3760: Inputs: 
                   3761: 
1.112     bowersj2 3762: =over 4
                   3763: 
                   3764: =item * $title, A title to be displayed on the page.
                   3765: 
                   3766: =item * $function, the current role (can be undef).
                   3767: 
                   3768: =item * $addentries, extra parameters for the <body> tag.
                   3769: 
                   3770: =item * $bodyonly, if defined, only return the <body> tag.
                   3771: 
                   3772: =item * $domain, if defined, force a given domain.
                   3773: 
                   3774: =item * $forcereg, if page should register as content page (relevant for 
1.86      www      3775:             text interface only)
1.60      matthew  3776: 
1.326     albertel 3777: =item * $customtitle, alternate text to use instead of $title
                   3778:                       in the title box that appears, this text
                   3779:                       is not auto translated like the $title is
1.309     albertel 3780: 
                   3781: =item * $notopbar, if true, keep the 'what is this' info but remove the
                   3782:                    navigational links
1.317     albertel 3783: 
1.338     albertel 3784: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
                   3785: 
                   3786: =item * $notitle, if true keep the nav controls, but remove the title bar
                   3787: 
1.361     albertel 3788: =item * $no_inline_link, if true and in remote mode, don't show the 
                   3789:          'Switch To Inline Menu' link
                   3790: 
1.460     albertel 3791: =item * $args, optional argument valid values are
                   3792:             no_auto_mt_title -> prevents &mt()ing the title arg
1.562     albertel 3793:             inherit_jsmath -> when creating popup window in a page,
                   3794:                               should it have jsmath forced on by the
                   3795:                               current page
1.460     albertel 3796: 
1.112     bowersj2 3797: =back
                   3798: 
1.60      matthew  3799: Returns: A uniform header for LON-CAPA web pages.  
                   3800: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
                   3801: If $bodyonly is undef or zero, an html string containing a <body> tag and 
                   3802: other decorations will be returned.
                   3803: 
                   3804: =cut
                   3805: 
1.54      www      3806: sub bodytag {
1.309     albertel 3807:     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,$customtitle,
1.460     albertel 3808: 	$notopbar,$bgcolor,$notitle,$no_inline_link,$args)=@_;
1.339     albertel 3809: 
1.460     albertel 3810:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.339     albertel 3811: 
1.183     matthew  3812:     $function = &get_users_function() if (!$function);
1.339     albertel 3813:     my $img =    &designparm($function.'.img',$domain);
                   3814:     my $font =   &designparm($function.'.font',$domain);
                   3815:     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
                   3816: 
                   3817:     my %design = ( 'style'   => 'margin-top: 0px',
1.535     albertel 3818: 		   'bgcolor' => $pgbg,
1.339     albertel 3819: 		   'text'    => $font,
                   3820:                    'alink'   => &designparm($function.'.alink',$domain),
                   3821: 		   'vlink'   => &designparm($function.'.vlink',$domain),
                   3822: 		   'link'    => &designparm($function.'.link',$domain),);
1.438     albertel 3823:     @design{keys(%$addentries)} = @$addentries{keys(%$addentries)}; 
1.339     albertel 3824: 
1.63      www      3825:  # role and realm
1.378     raeburn  3826:     my ($role,$realm) = split(/\./,$env{'request.role'},2);
                   3827:     if ($role  eq 'ca') {
1.479     albertel 3828:         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500     albertel 3829:         $realm = &plainname($rname,$rdom);
1.378     raeburn  3830:     } 
1.55      www      3831: # realm
1.258     albertel 3832:     if ($env{'request.course.id'}) {
1.378     raeburn  3833:         if ($env{'request.role'} !~ /^cr/) {
                   3834:             $role = &Apache::lonnet::plaintext($role,&course_type());
                   3835:         }
1.359     albertel 3836: 	$realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378     raeburn  3837:     } else {
                   3838:         $role = &Apache::lonnet::plaintext($role);
1.54      www      3839:     }
1.433     albertel 3840: 
1.359     albertel 3841:     if (!$realm) { $realm='&nbsp;'; }
1.55      www      3842: # Set messages
1.60      matthew  3843:     my $messages=&domainlogo($domain);
1.330     albertel 3844: 
1.438     albertel 3845:     my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329     albertel 3846: 
1.101     www      3847: # construct main body tag
1.359     albertel 3848:     my $bodytag = "<body $extra_body_attr>".
1.562     albertel 3849: 	&Apache::lontexconvert::init_math_support($args->{'inherit_jsmath'});
1.252     albertel 3850: 
1.530     albertel 3851:     if ($bodyonly) {
1.60      matthew  3852:         return $bodytag;
1.258     albertel 3853:     } elsif ($env{'browser.interface'} eq 'textual') {
1.95      www      3854: # Accessibility
1.224     raeburn  3855:           
1.337     albertel 3856: 	$bodytag.=&Apache::lonmenu::menubuttons($forcereg,$forcereg);
1.338     albertel 3857: 	if (!$notitle) {
1.337     albertel 3858: 	    $bodytag.='<h1>LON-CAPA: '.$title.'</h1>';
                   3859: 	}
                   3860: 	return $bodytag;
1.359     albertel 3861:     }
                   3862: 
1.410     albertel 3863:     my $name = &plainname($env{'user.name'},$env{'user.domain'});
1.433     albertel 3864:     if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
                   3865: 	undef($role);
1.434     albertel 3866:     } else {
                   3867: 	$name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'});
1.433     albertel 3868:     }
1.359     albertel 3869:     
                   3870:     my $roleinfo=(<<ENDROLE);
                   3871: <td class="LC_title_bar_who">
                   3872: <div class="LC_title_bar_name">
1.410     albertel 3873:     $name
1.361     albertel 3874:     &nbsp;
1.359     albertel 3875: </div>
                   3876: <div class="LC_title_bar_role">
1.361     albertel 3877: $role&nbsp;
1.359     albertel 3878: </div>
                   3879: <div class="LC_title_bar_realm">
1.361     albertel 3880: $realm&nbsp;
1.359     albertel 3881: </div>
1.206     albertel 3882: </td>
                   3883: ENDROLE
1.235     raeburn  3884: 
1.359     albertel 3885:     my $titleinfo = '<span class="LC_title_bar_title">'.$title.'</span>';
                   3886:     if ($customtitle) {
                   3887:         $titleinfo = $customtitle;
                   3888:     }
                   3889:     #
                   3890:     # Extra info if you are the DC
                   3891:     my $dc_info = '';
                   3892:     if ($env{'user.adv'} && exists($env{'user.role.dc./'.
                   3893:                         $env{'course.'.$env{'request.course.id'}.
                   3894:                                  '.domain'}.'/'})) {
                   3895:         my $cid = $env{'request.course.id'};
                   3896:         $dc_info.= $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380     www      3897:         $dc_info =~ s/\s+$//;
1.359     albertel 3898:         $dc_info = '('.$dc_info.')';
                   3899:     }
                   3900: 
                   3901:     if ($env{'environment.remote'} eq 'off') {
                   3902:         # No Remote
1.258     albertel 3903: 	if ($env{'request.state'} eq 'construct') {
1.359     albertel 3904: 	    $forcereg=1;
                   3905: 	}
                   3906: 
                   3907: 	if (!$customtitle && $env{'request.state'} eq 'construct') {
                   3908: 	    # this is for resources; directories have customtitle, and crumbs
                   3909:             # and select recent are created in lonpubdir.pm  
1.229     albertel 3910: 	    my ($uname,$thisdisfn)=
1.258     albertel 3911: 		($env{'request.filename'} =~ m|^/home/([^/]+)/public_html/(.*)|);
1.229     albertel 3912: 	    my $formaction='/priv/'.$uname.'/'.$thisdisfn;
                   3913: 	    $formaction=~s/\/+/\//g;
                   3914: 
1.359     albertel 3915: 	    my $parentpath = '';
                   3916: 	    my $lastitem = '';
                   3917: 	    if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
                   3918: 		$parentpath = $1;
                   3919: 		$lastitem = $2;
                   3920: 	    } else {
                   3921: 		$lastitem = $thisdisfn;
                   3922: 	    }
                   3923: 	    $titleinfo = 
1.401     albertel 3924: 		&Apache::loncommon::help_open_menu('','',3,'Authoring').
1.359     albertel 3925: 		'<b>Construction Space</b>:&nbsp;'. 
                   3926: 		'<form name="dirs" method="post" action="'.$formaction
                   3927: 		.'" target="_top"><tt><b>'
                   3928: 		.&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."<font size=\"+1\">$lastitem</font></b></tt><br />"
                   3929: 		.&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
                   3930: 		.'</form>'
                   3931: 		.&Apache::lonmenu::constspaceform();
1.235     raeburn  3932:         }
1.359     albertel 3933: 
1.337     albertel 3934:         my $titletable;
1.338     albertel 3935: 	if (!$notitle) {
1.337     albertel 3936: 	    $titletable =
1.359     albertel 3937: 		'<table id="LC_title_bar">'.
                   3938:                          "<tr><td> $titleinfo $dc_info</td>".$roleinfo.
                   3939: 			 '</tr></table>';
1.337     albertel 3940: 	}
1.359     albertel 3941: 	if ($notopbar) {
                   3942: 	    $bodytag .= $titletable;
                   3943: 	} else {
                   3944: 	    if ($env{'request.state'} eq 'construct') {
1.337     albertel 3945:                 $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg,
                   3946: 							  $titletable);
1.272     raeburn  3947:             } else {
1.336     albertel 3948:                 $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg).
1.359     albertel 3949: 		    $titletable;
1.272     raeburn  3950:             }
1.235     raeburn  3951:         }
                   3952:         return $bodytag;
1.94      www      3953:     }
1.95      www      3954: 
1.93      www      3955: #
1.95      www      3956: # Top frame rendering, Remote is up
1.93      www      3957: #
1.359     albertel 3958: 
1.517     raeburn  3959:     my $imgsrc = $img;
                   3960:     if ($img =~ /^\/adm/) {
1.575     albertel 3961:         $imgsrc = &lonhttpdurl($img);
1.517     raeburn  3962:     }
                   3963:     my $upperleft='<img src="'.$imgsrc.'" alt="'.$function.'" />';
1.359     albertel 3964: 
1.305     www      3965:     # Explicit link to get inline menu
1.361     albertel 3966:     my $menu= ($no_inline_link?''
                   3967: 	       :'<br /><a href="/adm/remote?action=collapse">'.&mt('Switch to Inline Menu Mode').'</a>');
1.245     matthew  3968:     #
1.338     albertel 3969:     if ($notitle) {
1.337     albertel 3970: 	return $bodytag;
                   3971:     }
1.94      www      3972:     return(<<ENDBODY);
1.60      matthew  3973: $bodytag
1.359     albertel 3974: <table id="LC_title_bar" class="LC_with_remote">
1.368     albertel 3975: <tr><td class="LC_title_bar_role_logo">$upperleft</td>
1.359     albertel 3976:     <td class="LC_title_bar_domain_logo">$messages&nbsp;</td>
1.54      www      3977: </tr>
1.359     albertel 3978: <tr><td>$titleinfo $dc_info $menu</td>
                   3979: $roleinfo
1.368     albertel 3980: </tr>
1.356     albertel 3981: </table>
1.54      www      3982: ENDBODY
1.182     matthew  3983: }
                   3984: 
1.330     albertel 3985: sub make_attr_string {
                   3986:     my ($register,$attr_ref) = @_;
                   3987: 
                   3988:     if ($attr_ref && !ref($attr_ref)) {
                   3989: 	die("addentries Must be a hash ref ".
                   3990: 	    join(':',caller(1))." ".
                   3991: 	    join(':',caller(0))." ");
                   3992:     }
                   3993: 
                   3994:     if ($register) {
1.339     albertel 3995: 	my ($on_load,$on_unload);
                   3996: 	foreach my $key (keys(%{$attr_ref})) {
                   3997: 	    if      (lc($key) eq 'onload') {
                   3998: 		$on_load.=$attr_ref->{$key}.';';
                   3999: 		delete($attr_ref->{$key});
                   4000: 
                   4001: 	    } elsif (lc($key) eq 'onunload') {
                   4002: 		$on_unload.=$attr_ref->{$key}.';';
                   4003: 		delete($attr_ref->{$key});
                   4004: 	    }
                   4005: 	}
                   4006: 	$attr_ref->{'onload'}  =
                   4007: 	    &Apache::lonmenu::loadevents().  $on_load;
                   4008: 	$attr_ref->{'onunload'}=
                   4009: 	    &Apache::lonmenu::unloadevents().$on_unload;
                   4010:     }
                   4011: 
                   4012: # Accessibility font enhance
                   4013:     if ($env{'browser.fontenhance'} eq 'on') {
                   4014: 	my $style;
                   4015: 	foreach my $key (keys(%{$attr_ref})) {
                   4016: 	    if (lc($key) eq 'style') {
                   4017: 		$style.=$attr_ref->{$key}.';';
                   4018: 		delete($attr_ref->{$key});
                   4019: 	    }
                   4020: 	}
                   4021: 	$attr_ref->{'style'}=$style.'; font-size: x-large;';
1.330     albertel 4022:     }
1.339     albertel 4023: 
                   4024:     if ($env{'browser.blackwhite'} eq 'on') {
                   4025: 	delete($attr_ref->{'font'});
                   4026: 	delete($attr_ref->{'link'});
                   4027: 	delete($attr_ref->{'alink'});
                   4028: 	delete($attr_ref->{'vlink'});
                   4029: 	delete($attr_ref->{'bgcolor'});
                   4030: 	delete($attr_ref->{'background'});
                   4031:     }
                   4032: 
1.330     albertel 4033:     my $attr_string;
                   4034:     foreach my $attr (keys(%$attr_ref)) {
                   4035: 	$attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
                   4036:     }
                   4037:     return $attr_string;
                   4038: }
                   4039: 
                   4040: 
1.182     matthew  4041: ###############################################
1.251     albertel 4042: ###############################################
                   4043: 
                   4044: =pod
                   4045: 
                   4046: =item * &endbodytag()
                   4047: 
                   4048: Returns a uniform footer for LON-CAPA web pages.
                   4049: 
1.306     albertel 4050: Inputs: none
1.251     albertel 4051: 
                   4052: =cut
                   4053: 
                   4054: sub endbodytag {
                   4055:     my $endbodytag='</body>';
1.269     albertel 4056:     $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
1.315     albertel 4057:     if ( exists( $env{'internal.head.redirect'} ) ) {
                   4058: 	$endbodytag=
                   4059: 	    "<br /><a href=\"$env{'internal.head.redirect'}\">".
                   4060: 	    &mt('Continue').'</a>'.
                   4061: 	    $endbodytag;
                   4062:     }
1.251     albertel 4063:     return $endbodytag;
                   4064: }
                   4065: 
1.352     albertel 4066: =pod
                   4067: 
                   4068: =item * &standard_css()
                   4069: 
                   4070: Returns a style sheet
                   4071: 
                   4072: Inputs: (all optional)
                   4073:             domain         -> force to color decorate a page for a specific
                   4074:                                domain
                   4075:             function       -> force usage of a specific rolish color scheme
                   4076:             bgcolor        -> override the default page bgcolor
                   4077: 
                   4078: =cut
                   4079: 
1.343     albertel 4080: sub standard_css {
1.345     albertel 4081:     my ($function,$domain,$bgcolor) = @_;
1.352     albertel 4082:     $function  = &get_users_function() if (!$function);
                   4083:     my $img    = &designparm($function.'.img',   $domain);
                   4084:     my $tabbg  = &designparm($function.'.tabbg', $domain);
                   4085:     my $font   = &designparm($function.'.font',  $domain);
1.345     albertel 4086:     my $sidebg = &designparm($function.'.sidebg',$domain);
1.382     albertel 4087:     my $pgbg_or_bgcolor =
                   4088: 	         $bgcolor ||
1.352     albertel 4089: 	         &designparm($function.'.pgbg',  $domain);
1.382     albertel 4090:     my $pgbg   = &designparm($function.'.pgbg',  $domain);
1.352     albertel 4091:     my $alink  = &designparm($function.'.alink', $domain);
                   4092:     my $vlink  = &designparm($function.'.vlink', $domain);
                   4093:     my $link   = &designparm($function.'.link',  $domain);
                   4094: 
                   4095:     my $sans                 = 'Arial,Helvetica,sans-serif';
1.395     albertel 4096:     my $mono                 = 'monospace';
1.352     albertel 4097:     my $data_table_head      = $tabbg;
                   4098:     my $data_table_light     = '#EEEEEE';
1.470     banghart 4099:     my $data_table_dark      = '#DDDDDD';
                   4100:     my $data_table_darker    = '#CCCCCC';
1.349     albertel 4101:     my $data_table_highlight = '#FFFF00';
1.352     albertel 4102:     my $mail_new             = '#FFBB77';
                   4103:     my $mail_new_hover       = '#DD9955';
                   4104:     my $mail_read            = '#BBBB77';
                   4105:     my $mail_read_hover      = '#999944';
                   4106:     my $mail_replied         = '#AAAA88';
                   4107:     my $mail_replied_hover   = '#888855';
                   4108:     my $mail_other           = '#99BBBB';
                   4109:     my $mail_other_hover     = '#669999';
1.391     albertel 4110:     my $table_header         = '#DDDDDD';
1.489     raeburn  4111:     my $feedback_link_bg     = '#BBBBBB';
1.392     albertel 4112: 
                   4113:     my $border = ($env{'browser.type'} eq 'explorer') ? '0px 2px 0px 2px'
                   4114: 	                                              : '0px 3px 0px 4px';
1.448     albertel 4115: 
1.523     albertel 4116: 
1.343     albertel 4117:     return <<END;
1.345     albertel 4118: h1, h2, h3, th { font-family: $sans }
1.343     albertel 4119: a:focus { color: red; background: yellow } 
1.510     albertel 4120: table.thinborder,
1.523     albertel 4121: 
1.510     albertel 4122: table.thinborder tr th {
                   4123:   border-style: solid;
                   4124:   border-width: 1px;
                   4125:   background: $tabbg;
                   4126: }
1.523     albertel 4127: table.thinborder tr td {
1.510     albertel 4128:   border-style: solid;
                   4129:   border-width: 1px
                   4130: }
1.426     albertel 4131: 
1.343     albertel 4132: form, .inline { display: inline; }
                   4133: .center { text-align: center; }
1.395     albertel 4134: .LC_filename {font-family: $mono;}
1.350     albertel 4135: .LC_error {
                   4136:   color: red;
                   4137:   font-size: larger;
                   4138: }
1.457     albertel 4139: .LC_warning,
                   4140: .LC_diff_removed {
1.394     albertel 4141:   color: red;
                   4142: }
1.532     albertel 4143: 
                   4144: .LC_info,
1.457     albertel 4145: .LC_success,
                   4146: .LC_diff_added {
1.350     albertel 4147:   color: green;
                   4148: }
1.543     albertel 4149: .LC_unknown {
                   4150:   color: yellow;
                   4151: }
                   4152: 
1.440     albertel 4153: .LC_icon {
                   4154:   border: 0px;
                   4155: }
1.539     albertel 4156: .LC_indexer_icon {
                   4157:   border: 0px;
                   4158:   height: 22px;
                   4159: }
1.543     albertel 4160: .LC_docs_spacer {
                   4161:   width: 25px;
                   4162:   height: 1px;
                   4163:   border: 0px;
                   4164: }
1.346     albertel 4165: 
1.532     albertel 4166: .LC_internal_info {
                   4167:   color: #999;
                   4168: }
                   4169: 
1.458     albertel 4170: table.LC_pastsubmission {
                   4171:   border: 1px solid black;
                   4172:   margin: 2px;
                   4173: }
                   4174: 
1.392     albertel 4175: table#LC_top_nav, table#LC_menubuttons {
1.345     albertel 4176:   width: 100%;
                   4177:   background: $pgbg;
1.392     albertel 4178:   border: 2px;
1.402     albertel 4179:   border-collapse: separate;
1.403     albertel 4180:   padding: 0px;
1.345     albertel 4181: }
1.392     albertel 4182: 
1.409     albertel 4183: table#LC_title_bar, table.LC_breadcrumbs, table#LC_nav_location,
1.393     albertel 4184: table#LC_title_bar.LC_with_remote {
1.359     albertel 4185:   width: 100%;
1.392     albertel 4186:   border-color: $pgbg;
                   4187:   border-style: solid;
                   4188:   border-width: $border;
                   4189: 
1.379     albertel 4190:   background: $pgbg;
                   4191:   font-family: $sans;
1.392     albertel 4192:   border-collapse: collapse;
1.403     albertel 4193:   padding: 0px;
1.359     albertel 4194: }
1.392     albertel 4195: 
1.409     albertel 4196: table.LC_docs_path {
                   4197:   width: 100%;
                   4198:   border: 0;
                   4199:   background: $pgbg;
                   4200:   font-family: $sans;
                   4201:   border-collapse: collapse;
                   4202:   padding: 0px;
                   4203: }
                   4204: 
1.359     albertel 4205: table#LC_title_bar td {
                   4206:   background: $tabbg;
                   4207: }
                   4208: table#LC_title_bar td.LC_title_bar_who {
                   4209:   background: $tabbg;
                   4210:   color: $font;
1.427     albertel 4211:   font: small $sans;
1.359     albertel 4212:   text-align: right;
                   4213: }
1.469     banghart 4214: span.LC_metadata {
                   4215:     font-family: $sans;
                   4216: }
1.359     albertel 4217: span.LC_title_bar_title {
1.416     albertel 4218:   font: bold x-large $sans;
1.359     albertel 4219: }
                   4220: table#LC_title_bar td.LC_title_bar_domain_logo {
                   4221:   background: $sidebg;
                   4222:   text-align: right;
1.368     albertel 4223:   padding: 0px;
                   4224: }
                   4225: table#LC_title_bar td.LC_title_bar_role_logo {
                   4226:   background: $sidebg;
                   4227:   padding: 0px;
1.359     albertel 4228: }
                   4229: 
1.346     albertel 4230: table#LC_menubuttons_mainmenu {
1.526     www      4231:   width: 100%;
1.346     albertel 4232:   border: 0px;
                   4233:   border-spacing: 1px;
1.372     albertel 4234:   padding: 0px 1px;
1.346     albertel 4235:   margin: 0px;
                   4236:   border-collapse: separate;
                   4237: }
                   4238: table#LC_menubuttons img, table#LC_menubuttons_mainmenu img {
                   4239:   border: 0px;
                   4240: }
1.345     albertel 4241: table#LC_top_nav td {
                   4242:   background: $tabbg;
1.392     albertel 4243:   border: 0px;
1.407     albertel 4244:   font-size: small;
1.345     albertel 4245: }
                   4246: table#LC_top_nav td a, div#LC_top_nav a {
                   4247:   color: $font;
                   4248:   font-family: $sans;
                   4249: }
1.364     albertel 4250: table#LC_top_nav td.LC_top_nav_logo {
                   4251:   background: $tabbg;
1.432     albertel 4252:   text-align: left;
1.408     albertel 4253:   white-space: nowrap;
1.432     albertel 4254:   width: 31px;
1.408     albertel 4255: }
                   4256: table#LC_top_nav td.LC_top_nav_logo img {
1.432     albertel 4257:   border: 0px;
1.408     albertel 4258:   vertical-align: bottom;
1.364     albertel 4259: }
1.432     albertel 4260: table#LC_top_nav td.LC_top_nav_exit,
                   4261: table#LC_top_nav td.LC_top_nav_help {
                   4262:   width: 2.0em;
                   4263: }
1.442     albertel 4264: table#LC_top_nav td.LC_top_nav_login {
                   4265:   width: 4.0em;
                   4266:   text-align: center;
                   4267: }
1.409     albertel 4268: table.LC_breadcrumbs td, table.LC_docs_path td  {
1.357     albertel 4269:   background: $tabbg;
                   4270:   color: $font;
                   4271:   font-family: $sans;
1.358     albertel 4272:   font-size: smaller;
1.357     albertel 4273: }
1.411     albertel 4274: table.LC_breadcrumbs td.LC_breadcrumbs_component,
1.409     albertel 4275: table.LC_docs_path td.LC_docs_path_component {
1.357     albertel 4276:   background: $tabbg;
                   4277:   color: $font;
                   4278:   font-family: $sans;
                   4279:   font-size: larger;
                   4280:   text-align: right;
                   4281: }
1.383     albertel 4282: td.LC_table_cell_checkbox {
                   4283:   text-align: center;
                   4284: }
                   4285: 
1.522     albertel 4286: table#LC_mainmenu td.LC_mainmenu_column {
                   4287:     vertical-align: top;
                   4288: }
                   4289: 
1.346     albertel 4290: .LC_menubuttons_inline_text {
                   4291:   color: $font;
                   4292:   font-family: $sans;
                   4293:   font-size: smaller;
                   4294: }
                   4295: 
1.526     www      4296: .LC_menubuttons_link {
                   4297:   text-decoration: none;
                   4298: }
                   4299: 
1.522     albertel 4300: .LC_menubuttons_category {
1.521     www      4301:   color: $font;
1.526     www      4302:   background: $pgbg;
1.521     www      4303:   font-family: $sans;
                   4304:   font-size: larger;
                   4305:   font-weight: bold;
                   4306: }
                   4307: 
1.346     albertel 4308: td.LC_menubuttons_text {
1.526     www      4309:   width: 90%;
1.346     albertel 4310:   color: $font;
                   4311:   font-family: $sans;
                   4312: }
1.526     www      4313: 
1.346     albertel 4314: td.LC_menubuttons_img {
                   4315: }
1.526     www      4316: 
1.346     albertel 4317: .LC_current_location {
                   4318:   font-family: $sans;
                   4319:   background: $tabbg;
                   4320: }
                   4321: .LC_new_mail {
                   4322:   font-family: $sans;
                   4323:   font-weight: bold;
                   4324: }
1.347     albertel 4325: 
1.526     www      4326: .LC_rolesmenu_is {
                   4327:   font-family: $sans;
                   4328: }
                   4329: 
                   4330: .LC_rolesmenu_selected {
                   4331:   font-family: $sans;
                   4332: }
                   4333: 
                   4334: .LC_rolesmenu_future {
                   4335:   font-family: $sans;
                   4336: }
                   4337: 
                   4338: 
                   4339: .LC_rolesmenu_will {
                   4340:   font-family: $sans;
                   4341: }
                   4342: 
                   4343: .LC_rolesmenu_will_not {
                   4344:   font-family: $sans;
                   4345: }
                   4346: 
                   4347: .LC_rolesmenu_expired {
                   4348:   font-family: $sans;
                   4349: }
                   4350: 
                   4351: .LC_rolesinfo {
                   4352:   font-family: $sans;
                   4353: }
                   4354: 
1.527     www      4355: .LC_dropadd_labeltext {
                   4356:   font-family: $sans;
                   4357:   text-align: right;
                   4358: }
                   4359: 
                   4360: .LC_preferences_labeltext {
                   4361:   font-family: $sans;
                   4362:   text-align: right;
                   4363: }
                   4364: 
1.440     albertel 4365: table.LC_aboutme_port {
                   4366:   border: 0px;
                   4367:   border-collapse: collapse;
                   4368:   border-spacing: 0px;
                   4369: }
1.349     albertel 4370: table.LC_data_table, table.LC_mail_list {
1.347     albertel 4371:   border: 1px solid #000000;
1.402     albertel 4372:   border-collapse: separate;
1.426     albertel 4373:   border-spacing: 1px;
1.347     albertel 4374: }
1.422     albertel 4375: .LC_data_table_dense {
                   4376:   font-size: small;
                   4377: }
1.507     raeburn  4378: table.LC_nested_outer {
                   4379:   border: 1px solid #000000;
1.589     raeburn  4380:   border-collapse: collapse;
1.507     raeburn  4381:   border-spacing: 0px;
                   4382:   width: 100%;
                   4383: }
                   4384: table.LC_nested {
                   4385:   border: 0px;
1.589     raeburn  4386:   border-collapse: collapse;
1.507     raeburn  4387:   border-spacing: 0px;
                   4388:   width: 100%;
                   4389: }
1.523     albertel 4390: table.LC_data_table tr th, table.LC_calendar tr th, table.LC_mail_list tr th,
                   4391: table.LC_prior_tries tr th {
1.349     albertel 4392:   font-weight: bold;
                   4393:   background-color: $data_table_head;
1.421     albertel 4394:   font-size: smaller;
1.347     albertel 4395: }
1.440     albertel 4396: table.LC_data_table tr td, 
                   4397: table.LC_aboutme_port tr td {
1.349     albertel 4398:   background-color: $data_table_light;
1.425     albertel 4399:   padding: 2px;
1.347     albertel 4400: }
1.440     albertel 4401: table.LC_data_table tr.LC_even_row td,
                   4402: table.LC_aboutme_port tr.LC_even_row td {
1.349     albertel 4403:   background-color: $data_table_dark;
1.347     albertel 4404: }
1.425     albertel 4405: table.LC_data_table tr.LC_data_table_highlight td {
                   4406:   background-color: $data_table_darker;
                   4407: }
1.451     albertel 4408: table.LC_data_table tr.LC_empty_row td,
1.507     raeburn  4409: table.LC_nested tr.LC_empty_row td {
1.347     albertel 4410:   background-color: #FFFFFF;
1.421     albertel 4411:   font-weight: bold;
                   4412:   font-style: italic;
                   4413:   text-align: center;
                   4414:   padding: 8px;
1.347     albertel 4415: }
1.507     raeburn  4416: table.LC_nested tr.LC_empty_row td {
1.465     albertel 4417:   padding: 4ex
                   4418: }
1.507     raeburn  4419: table.LC_nested_outer tr th {
                   4420:   font-weight: bold;
                   4421:   background-color: $data_table_head;
                   4422:   font-size: smaller;
                   4423:   border-bottom: 1px solid #000000;
                   4424: }
                   4425: table.LC_nested_outer tr td.LC_subheader {
                   4426:   background-color: $data_table_head;
                   4427:   font-weight: bold;
                   4428:   font-size: small;
                   4429:   border-bottom: 1px solid #000000;
                   4430:   text-align: right;
1.451     albertel 4431: }
1.507     raeburn  4432: table.LC_nested tr.LC_info_row td {
1.451     albertel 4433:   background-color: #CCC;
                   4434:   font-weight: bold;
                   4435:   font-size: small;
1.507     raeburn  4436:   text-align: center;
                   4437: }
1.589     raeburn  4438: table.LC_nested tr.LC_info_row td.LC_left_item,
                   4439: table.LC_nested_outer tr th.LC_left_item {
1.507     raeburn  4440:   text-align: left;
1.451     albertel 4441: }
1.507     raeburn  4442: table.LC_nested td {
1.451     albertel 4443:   background-color: #FFF;
                   4444:   font-size: small;
1.507     raeburn  4445: }
                   4446: table.LC_nested_outer tr th.LC_right_item,
                   4447: table.LC_nested tr.LC_info_row td.LC_right_item,
                   4448: table.LC_nested tr.LC_odd_row td.LC_right_item,
                   4449: table.LC_nested tr td.LC_right_item {
1.451     albertel 4450:   text-align: right;
                   4451: }
                   4452: 
1.507     raeburn  4453: table.LC_nested tr.LC_odd_row td {
1.451     albertel 4454:   background-color: #EEE;
                   4455: }
                   4456: 
1.473     raeburn  4457: table.LC_createuser {
                   4458: }
                   4459: 
                   4460: table.LC_createuser tr.LC_section_row td {
                   4461:   font-size: smaller;
                   4462: }
                   4463: 
                   4464: table.LC_createuser tr.LC_info_row td  {
                   4465:   background-color: #CCC;
                   4466:   font-weight: bold;
                   4467:   text-align: center;
                   4468: }
                   4469: 
1.349     albertel 4470: table.LC_calendar {
                   4471:   border: 1px solid #000000;
                   4472:   border-collapse: collapse;
                   4473: }
                   4474: table.LC_calendar_pickdate {
                   4475:   font-size: xx-small;
                   4476: }
                   4477: table.LC_calendar tr td {
                   4478:   border: 1px solid #000000;
                   4479:   vertical-align: top;
                   4480: }
                   4481: table.LC_calendar tr td.LC_calendar_day_empty {
                   4482:   background-color: $data_table_dark;
                   4483: }
                   4484: table.LC_calendar tr td.LC_calendar_day_current {
                   4485:   background-color: $data_table_highlight;
                   4486: }
                   4487: 
                   4488: table.LC_mail_list tr.LC_mail_new {
                   4489:   background-color: $mail_new;
                   4490: }
                   4491: table.LC_mail_list tr.LC_mail_new:hover {
                   4492:   background-color: $mail_new_hover;
                   4493: }
                   4494: table.LC_mail_list tr.LC_mail_read {
                   4495:   background-color: $mail_read;
                   4496: }
                   4497: table.LC_mail_list tr.LC_mail_read:hover {
                   4498:   background-color: $mail_read_hover;
                   4499: }
                   4500: table.LC_mail_list tr.LC_mail_replied {
                   4501:   background-color: $mail_replied;
                   4502: }
                   4503: table.LC_mail_list tr.LC_mail_replied:hover {
                   4504:   background-color: $mail_replied_hover;
                   4505: }
                   4506: table.LC_mail_list tr.LC_mail_other {
                   4507:   background-color: $mail_other;
                   4508: }
                   4509: table.LC_mail_list tr.LC_mail_other:hover {
                   4510:   background-color: $mail_other_hover;
                   4511: }
1.494     raeburn  4512: table.LC_mail_list tr.LC_mail_even {
                   4513: }
                   4514: table.LC_mail_list tr.LC_mail_odd {
                   4515: }
                   4516: 
1.385     albertel 4517: 
1.386     albertel 4518: table#LC_portfolio_actions {
                   4519:   width: auto;
                   4520:   background: $pgbg;
                   4521:   border: 0px;
                   4522:   border-spacing: 2px 2px;
                   4523:   padding: 0px;
                   4524:   margin: 0px;
                   4525:   border-collapse: separate;
                   4526: }
                   4527: table#LC_portfolio_actions td.LC_label {
                   4528:   background: $tabbg;
                   4529:   text-align: right;
                   4530: }
                   4531: table#LC_portfolio_actions td.LC_value {
                   4532:   background: $tabbg;
                   4533: }
1.385     albertel 4534: 
1.391     albertel 4535: table#LC_cstr_controls {
                   4536:   width: 100%;
                   4537:   border-collapse: collapse;
                   4538: }
                   4539: table#LC_cstr_controls tr td {
                   4540:   border: 4px solid $pgbg;
                   4541:   padding: 4px;
                   4542:   text-align: center;
                   4543:   background: $tabbg;
                   4544: }
                   4545: table#LC_cstr_controls tr th {
                   4546:   border: 4px solid $pgbg;
                   4547:   background: $table_header;
                   4548:   text-align: center;
                   4549:   font-family: $sans;
                   4550:   font-size: smaller;
                   4551: }
                   4552: 
1.389     albertel 4553: table#LC_browser {
                   4554:  
                   4555: }
                   4556: table#LC_browser tr th {
1.391     albertel 4557:   background: $table_header;
1.389     albertel 4558: }
1.390     albertel 4559: table#LC_browser tr td {
                   4560:   padding: 2px;
                   4561: }
1.389     albertel 4562: table#LC_browser tr.LC_browser_file,
                   4563: table#LC_browser tr.LC_browser_file_published {
                   4564:   background: #CCFF88;
                   4565: }
                   4566: table#LC_browser tr.LC_browser_file_locked,
                   4567: table#LC_browser tr.LC_browser_file_unpublished {
                   4568:   background: #FFAA99;
1.387     albertel 4569: }
1.389     albertel 4570: table#LC_browser tr.LC_browser_file_obsolete {
                   4571:   background: #AAAAAA;
1.387     albertel 4572: }
1.455     albertel 4573: table#LC_browser tr.LC_browser_file_modified,
                   4574: table#LC_browser tr.LC_browser_file_metamodified {
1.389     albertel 4575:   background: #FFFF77;
1.387     albertel 4576: }
1.389     albertel 4577: table#LC_browser tr.LC_browser_folder {
                   4578:   background: #CCCCFF;
1.387     albertel 4579: }
1.388     albertel 4580: span.LC_current_location {
                   4581:   font-size: x-large;
                   4582:   background: $pgbg;
                   4583: }
1.387     albertel 4584: 
1.395     albertel 4585: span.LC_parm_menu_item {
                   4586:   font-size: larger;
                   4587:   font-family: $sans;
                   4588: }
                   4589: span.LC_parm_scope_all {
                   4590:   color: red;
                   4591: }
                   4592: span.LC_parm_scope_folder {
                   4593:   color: green;
                   4594: }
                   4595: span.LC_parm_scope_resource {
                   4596:   color: orange;
                   4597: }
                   4598: span.LC_parm_part {
                   4599:   color: blue;
                   4600: }
                   4601: span.LC_parm_folder, span.LC_parm_symb {
                   4602:   font-size: x-small;
                   4603:   font-family: $mono;
                   4604:   color: #AAAAAA;
                   4605: }
                   4606: 
1.396     albertel 4607: td.LC_parm_overview_level_menu, td.LC_parm_overview_map_menu,
                   4608: td.LC_parm_overview_parm_selectors, td.LC_parm_overview_parm_restrictions {
                   4609:   border: 1px solid black;
                   4610:   border-collapse: collapse;
                   4611: }
                   4612: table.LC_parm_overview_restrictions td {
                   4613:   border-width: 1px 4px 1px 4px;
                   4614:   border-style: solid;
                   4615:   border-color: $pgbg;
                   4616:   text-align: center;
                   4617: }
                   4618: table.LC_parm_overview_restrictions th {
                   4619:   background: $tabbg;
                   4620:   border-width: 1px 4px 1px 4px;
                   4621:   border-style: solid;
                   4622:   border-color: $pgbg;
                   4623: }
1.398     albertel 4624: table#LC_helpmenu {
                   4625:   border: 0px;
                   4626:   height: 55px;
                   4627:   border-spacing: 0px;
                   4628: }
                   4629: 
                   4630: table#LC_helpmenu fieldset legend {
                   4631:   font-size: larger;
                   4632:   font-weight: bold;
                   4633: }
1.397     albertel 4634: table#LC_helpmenu_links {
                   4635:   width: 100%;
                   4636:   border: 1px solid black;
                   4637:   background: $pgbg;
                   4638:   padding: 0px;
                   4639:   border-spacing: 1px;
                   4640: }
                   4641: table#LC_helpmenu_links tr td {
                   4642:   padding: 1px;
                   4643:   background: $tabbg;
1.399     albertel 4644:   text-align: center;
                   4645:   font-weight: bold;
1.397     albertel 4646: }
1.396     albertel 4647: 
1.397     albertel 4648: table#LC_helpmenu_links a:link, table#LC_helpmenu_links a:visited,
                   4649: table#LC_helpmenu_links a:active {
                   4650:   text-decoration: none;
                   4651:   color: $font;
                   4652: }
                   4653: table#LC_helpmenu_links a:hover {
                   4654:   text-decoration: underline;
                   4655:   color: $vlink;
                   4656: }
1.396     albertel 4657: 
1.417     albertel 4658: .LC_chrt_popup_exists {
                   4659:   border: 1px solid #339933;
                   4660:   margin: -1px;
                   4661: }
                   4662: .LC_chrt_popup_up {
                   4663:   border: 1px solid yellow;
                   4664:   margin: -1px;
                   4665: }
                   4666: .LC_chrt_popup {
                   4667:   border: 1px solid #8888FF;
                   4668:   background: #CCCCFF;
                   4669: }
1.421     albertel 4670: table.LC_pick_box {
                   4671:   border-collapse: separate;
                   4672:   background: white;
                   4673:   border: 1px solid black;
                   4674:   border-spacing: 1px;
                   4675: }
                   4676: table.LC_pick_box td.LC_pick_box_title {
                   4677:   background: $tabbg;
                   4678:   font-weight: bold;
                   4679:   text-align: right;
                   4680:   width: 184px;
                   4681:   padding: 8px;
                   4682: }
1.579     raeburn  4683: table.LC_pick_box td.LC_pick_box_value {
                   4684:   text-align: left;
                   4685:   padding: 8px;
                   4686: }
                   4687: table.LC_pick_box td.LC_pick_box_select {
                   4688:   text-align: left;
                   4689:   padding: 8px;
                   4690: }
1.424     albertel 4691: table.LC_pick_box td.LC_pick_box_separator {
1.421     albertel 4692:   padding: 0px;
                   4693:   height: 1px;
                   4694:   background: black;
                   4695: }
                   4696: table.LC_pick_box td.LC_pick_box_submit {
                   4697:   text-align: right;
                   4698: }
1.579     raeburn  4699: table.LC_pick_box td.LC_evenrow_value {
                   4700:   text-align: left;
                   4701:   padding: 8px;
                   4702:   background-color: $data_table_light;
                   4703: }
                   4704: table.LC_pick_box td.LC_oddrow_value {
                   4705:   text-align: left;
                   4706:   padding: 8px;
                   4707:   background-color: $data_table_light;
                   4708: }
                   4709: table.LC_helpform_receipt {
                   4710:   width: 620px;
                   4711:   border-collapse: separate;
                   4712:   background: white;
                   4713:   border: 1px solid black;
                   4714:   border-spacing: 1px;
                   4715: }
                   4716: table.LC_helpform_receipt td.LC_pick_box_title {
                   4717:   background: $tabbg;
                   4718:   font-weight: bold;
                   4719:   text-align: right;
                   4720:   width: 184px;
                   4721:   padding: 8px;
                   4722: }
                   4723: table.LC_helpform_receipt td.LC_evenrow_value {
                   4724:   text-align: left;
                   4725:   padding: 8px;
                   4726:   background-color: $data_table_light;
                   4727: }
                   4728: table.LC_helpform_receipt td.LC_oddrow_value {
                   4729:   text-align: left;
                   4730:   padding: 8px;
                   4731:   background-color: $data_table_light;
                   4732: }
                   4733: table.LC_helpform_receipt td.LC_pick_box_separator {
                   4734:   padding: 0px;
                   4735:   height: 1px;
                   4736:   background: black;
                   4737: }
                   4738: span.LC_helpform_receipt_cat {
                   4739:   font-weight: bold;
                   4740: }
1.424     albertel 4741: table.LC_group_priv_box {
                   4742:   background: white;
                   4743:   border: 1px solid black;
                   4744:   border-spacing: 1px;
                   4745: }
                   4746: table.LC_group_priv_box td.LC_pick_box_title {
                   4747:   background: $tabbg;
                   4748:   font-weight: bold;
                   4749:   text-align: right;
                   4750:   width: 184px;
                   4751: }
                   4752: table.LC_group_priv_box td.LC_groups_fixed {
                   4753:   background: $data_table_light;
                   4754:   text-align: center;
                   4755: }
                   4756: table.LC_group_priv_box td.LC_groups_optional {
                   4757:   background: $data_table_dark;
                   4758:   text-align: center;
                   4759: }
                   4760: table.LC_group_priv_box td.LC_groups_functionality {
                   4761:   background: $data_table_darker;
                   4762:   text-align: center;
                   4763:   font-weight: bold;
                   4764: }
                   4765: table.LC_group_priv td {
                   4766:   text-align: left;
                   4767:   padding: 0px;
                   4768: }
                   4769: 
1.421     albertel 4770: table.LC_notify_front_page {
                   4771:   background: white;
                   4772:   border: 1px solid black;
                   4773:   padding: 8px;
                   4774: }
                   4775: table.LC_notify_front_page td {
                   4776:   padding: 8px;
                   4777: }
1.424     albertel 4778: .LC_navbuttons {
                   4779:   margin: 2ex 0ex 2ex 0ex;
                   4780: }
1.423     albertel 4781: .LC_topic_bar {
                   4782:   font-family: $sans;
                   4783:   font-weight: bold;
                   4784:   width: 100%;
                   4785:   background: $tabbg;
                   4786:   vertical-align: middle;
                   4787:   margin: 2ex 0ex 2ex 0ex;
                   4788: }
                   4789: .LC_topic_bar span {
                   4790:   vertical-align: middle;
                   4791: }
                   4792: .LC_topic_bar img {
                   4793:   vertical-align: bottom;
                   4794: }
                   4795: table.LC_course_group_status {
                   4796:   margin: 20px;
                   4797: }
                   4798: table.LC_status_selector td {
                   4799:   vertical-align: top;
                   4800:   text-align: center;
1.424     albertel 4801:   padding: 4px;
                   4802: }
                   4803: table.LC_descriptive_input td.LC_description {
                   4804:   vertical-align: top;
                   4805:   text-align: right;
                   4806:   font-weight: bold;
1.423     albertel 4807: }
1.489     raeburn  4808: table.LC_feedback_link {
                   4809:     background: $feedback_link_bg;
                   4810: }
                   4811: span.LC_feedback_link {
                   4812:     background: $feedback_link_bg;
                   4813:     font-size: larger;
                   4814: }
1.421     albertel 4815: 
1.515     albertel 4816: table.LC_prior_tries {
1.524     albertel 4817:   border: 1px solid #000000;
                   4818:   border-collapse: separate;
                   4819:   border-spacing: 1px;
1.515     albertel 4820: }
1.523     albertel 4821: 
1.515     albertel 4822: table.LC_prior_tries td {
1.524     albertel 4823:   padding: 2px;
1.515     albertel 4824: }
1.523     albertel 4825: 
                   4826: .LC_answer_correct {
                   4827:   background: #AAFFAA;
                   4828:   color: black;
                   4829: }
                   4830: .LC_answer_charged_try {
                   4831:   background: #FFAAAA ! important;
                   4832:   color: black;
                   4833: }
                   4834: .LC_answer_not_charged_try, 
                   4835: .LC_answer_no_grade,
                   4836: .LC_answer_late {
                   4837:   background: #FFFFAA;
                   4838:   color: black;
                   4839: }
                   4840: .LC_answer_previous {
                   4841:   background: #AAAAFF;
                   4842:   color: black;
                   4843: }
                   4844: .LC_answer_no_message {
                   4845:   background: #FFFFFF;
                   4846:   color: black;
                   4847: }
                   4848: .LC_answer_unknown {
                   4849:   background: orange;
                   4850:   color: black;
                   4851: }
                   4852: 
                   4853: 
1.529     albertel 4854: span.LC_prior_numerical,
                   4855: span.LC_prior_string,
                   4856: span.LC_prior_custom,
                   4857: span.LC_prior_reaction,
                   4858: span.LC_prior_math {
1.523     albertel 4859:   font-family: monospace;
                   4860:   white-space: pre;
                   4861: }
                   4862: 
1.525     albertel 4863: span.LC_prior_string {
                   4864:   font-family: monospace;
                   4865:   white-space: pre;
                   4866: }
                   4867: 
1.523     albertel 4868: table.LC_prior_option {
                   4869:   width: 100%;
                   4870:   border-collapse: collapse;
                   4871: }
1.528     albertel 4872: table.LC_prior_rank, table.LC_prior_match {
                   4873:   border-collapse: collapse;
                   4874: }
                   4875: table.LC_prior_option tr td,
                   4876: table.LC_prior_rank tr td,
                   4877: table.LC_prior_match tr td {
1.524     albertel 4878:   border: 1px solid #000000;
1.515     albertel 4879: }
                   4880: 
1.519     raeburn  4881: span.LC_nobreak {
1.544     albertel 4882:   white-space: nowrap;
1.519     raeburn  4883: }
                   4884: 
1.576     raeburn  4885: span.LC_cusr_emph {
                   4886:   font-style: italic;
                   4887: }
                   4888: 
1.545     albertel 4889: table.LC_docs_documents {
                   4890:   background: #BBBBBB;
1.547     albertel 4891:   border-width: 0px;
1.545     albertel 4892:   border-collapse: collapse;
                   4893: }
                   4894: 
                   4895: table.LC_docs_documents td.LC_docs_document {
                   4896:   border: 2px solid black;
                   4897:   padding: 4px;
                   4898: }
                   4899: 
                   4900: .LC_docs_course_commands div {
                   4901:   float: left;
                   4902:   border: 4px solid #AAAAAA;
                   4903:   padding: 4px;
                   4904:   background: #DDDDCC;
                   4905: }
                   4906: 
                   4907: .LC_docs_entry_move {
                   4908:   border: 0px;
                   4909:   border-collapse: collapse;
1.544     albertel 4910: }
                   4911: 
1.545     albertel 4912: .LC_docs_entry_move td {
                   4913:   border: 2px solid #BBBBBB;
                   4914:   background: #DDDDDD;
                   4915: }
                   4916: 
                   4917: .LC_docs_editor td.LC_docs_entry_commands {
                   4918:   background: #DDDDDD;
                   4919:   font-size: x-small;
                   4920: }
1.544     albertel 4921: .LC_docs_copy {
1.545     albertel 4922:   color: #000099;
1.544     albertel 4923: }
                   4924: .LC_docs_cut {
1.545     albertel 4925:   color: #550044;
1.544     albertel 4926: }
                   4927: .LC_docs_rename {
1.545     albertel 4928:   color: #009900;
1.544     albertel 4929: }
                   4930: .LC_docs_remove {
1.545     albertel 4931:   color: #990000;
                   4932: }
                   4933: 
1.547     albertel 4934: .LC_docs_reinit_warn,
                   4935: .LC_docs_ext_edit {
                   4936:   font-size: x-small;
                   4937: }
                   4938: 
1.545     albertel 4939: .LC_docs_editor td.LC_docs_entry_title,
                   4940: .LC_docs_editor td.LC_docs_entry_icon {
                   4941:   background: #FFFFBB;
                   4942: }
                   4943: .LC_docs_editor td.LC_docs_entry_parameter {
                   4944:   background: #BBBBFF;
                   4945:   font-size: x-small;
                   4946:   white-space: nowrap;
                   4947: }
                   4948: 
                   4949: table.LC_docs_adddocs td,
                   4950: table.LC_docs_adddocs th {
                   4951:   border: 1px solid #BBBBBB;
                   4952:   padding: 4px;
                   4953:   background: #DDDDDD;
1.543     albertel 4954: }
                   4955: 
1.584     albertel 4956: table.LC_sty_begin {
                   4957:   background: #BBFFBB;
                   4958: }
                   4959: table.LC_sty_end {
                   4960:   background: #FFBBBB;
                   4961: }
                   4962: 
1.589     raeburn  4963: table.LC_double_column {
                   4964:   border-width: 0px;
                   4965:   border-collapse: collapse;
                   4966:   width: 100%;
                   4967:   padding: 2px;
                   4968: }
                   4969: 
                   4970: table.LC_double_column tr td.LC_left_col {
1.590   ! raeburn  4971:   top: 2px;
1.589     raeburn  4972:   left: 2px;
                   4973:   width: 47%;
                   4974:   vertical-align: top;
                   4975: }
                   4976: 
                   4977: table.LC_double_column tr td.LC_right_col {
                   4978:   top: 2px;
                   4979:   right: 2px; 
                   4980:   width: 47%;
                   4981:   vertical-align: top;
                   4982: }
                   4983: 
1.343     albertel 4984: END
                   4985: }
                   4986: 
1.306     albertel 4987: =pod
                   4988: 
                   4989: =item * &headtag()
                   4990: 
                   4991: Returns a uniform footer for LON-CAPA web pages.
                   4992: 
1.307     albertel 4993: Inputs: $title - optional title for the head
                   4994:         $head_extra - optional extra HTML to put inside the <head>
1.315     albertel 4995:         $args - optional arguments
1.319     albertel 4996:             force_register - if is true call registerurl so the remote is 
                   4997:                              informed
1.415     albertel 4998:             redirect       -> array ref of
                   4999:                                    1- seconds before redirect occurs
                   5000:                                    2- url to redirect to
                   5001:                                    3- whether the side effect should occur
1.315     albertel 5002:                            (side effect of setting 
                   5003:                                $env{'internal.head.redirect'} to the url 
                   5004:                                redirected too)
1.352     albertel 5005:             domain         -> force to color decorate a page for a specific
                   5006:                                domain
                   5007:             function       -> force usage of a specific rolish color scheme
                   5008:             bgcolor        -> override the default page bgcolor
1.460     albertel 5009:             no_auto_mt_title
                   5010:                            -> prevent &mt()ing the title arg
1.464     albertel 5011: 
1.306     albertel 5012: =cut
                   5013: 
                   5014: sub headtag {
1.313     albertel 5015:     my ($title,$head_extra,$args) = @_;
1.306     albertel 5016:     
1.363     albertel 5017:     my $function = $args->{'function'} || &get_users_function();
                   5018:     my $domain   = $args->{'domain'}   || &determinedomain();
                   5019:     my $bgcolor  = $args->{'bgcolor'}  || &designparm($function.'.pgbg',$domain);
1.418     albertel 5020:     my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458     albertel 5021: 		   $Apache::lonnet::perlvar{'lonVersion'},
1.531     albertel 5022: 		   #time(),
1.418     albertel 5023: 		   $env{'environment.color.timestamp'},
1.363     albertel 5024: 		   $function,$domain,$bgcolor);
                   5025: 
1.369     www      5026:     $url = '/adm/css/'.&escape($url).'.css';
1.363     albertel 5027: 
1.308     albertel 5028:     my $result =
                   5029: 	'<head>'.
1.461     albertel 5030: 	&font_settings();
1.319     albertel 5031: 
1.461     albertel 5032:     if (!$args->{'frameset'}) {
                   5033: 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
                   5034:     }
1.319     albertel 5035:     if ($args->{'force_register'}) {
                   5036: 	$result .= &Apache::lonmenu::registerurl(1);
                   5037:     }
1.436     albertel 5038:     if (!$args->{'no_nav_bar'} 
                   5039: 	&& !$args->{'only_body'}
                   5040: 	&& !$args->{'frameset'}) {
                   5041: 	$result .= &help_menu_js();
                   5042:     }
1.319     albertel 5043: 
1.314     albertel 5044:     if (ref($args->{'redirect'})) {
1.414     albertel 5045: 	my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315     albertel 5046: 	$url = &Apache::lonenc::check_encrypt($url);
1.414     albertel 5047: 	if (!$inhibit_continue) {
                   5048: 	    $env{'internal.head.redirect'} = $url;
                   5049: 	}
1.313     albertel 5050: 	$result.=<<ADDMETA
                   5051: <meta http-equiv="pragma" content="no-cache" />
1.344     albertel 5052: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313     albertel 5053: ADDMETA
                   5054:     }
1.306     albertel 5055:     if (!defined($title)) {
                   5056: 	$title = 'The LearningOnline Network with CAPA';
                   5057:     }
1.460     albertel 5058:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
                   5059:     $result .= '<title> LON-CAPA '.$title.'</title>'
1.414     albertel 5060: 	.'<link rel="stylesheet" type="text/css" href="'.$url.'" />'
                   5061: 	.$head_extra;
1.306     albertel 5062:     return $result;
                   5063: }
                   5064: 
                   5065: =pod
                   5066: 
1.340     albertel 5067: =item * &font_settings()
                   5068: 
                   5069: Returns neccessary <meta> to set the proper encoding
                   5070: 
                   5071: Inputs: none
                   5072: 
                   5073: =cut
                   5074: 
                   5075: sub font_settings {
                   5076:     my $headerstring='';
                   5077:     if (($env{'browser.os'} eq 'mac') && (!$env{'browser.mathml'})) { 
                   5078: 	$headerstring.=
                   5079: 	    '<meta Content-Type="text/html; charset=x-mac-roman" />';
                   5080:     } elsif (!$env{'browser.mathml'} && $env{'browser.unicode'}) {
                   5081: 	$headerstring.=
                   5082: 	    '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
                   5083:     }
                   5084:     return $headerstring;
                   5085: }
                   5086: 
1.341     albertel 5087: =pod
                   5088: 
                   5089: =item * &xml_begin()
                   5090: 
                   5091: Returns the needed doctype and <html>
                   5092: 
                   5093: Inputs: none
                   5094: 
                   5095: =cut
                   5096: 
                   5097: sub xml_begin {
                   5098:     my $output='';
                   5099: 
1.342     albertel 5100:     &Apache::lonhtmlcommon::init_htmlareafields();
                   5101: 
1.341     albertel 5102:     if ($env{'browser.mathml'}) {
                   5103: 	$output='<?xml version="1.0"?>'
                   5104:             #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
                   5105: #            .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
                   5106:             
                   5107: #	    .'<!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">] >'
                   5108: 	    .'<!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">'
                   5109:             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" ' 
                   5110: 	    .'xmlns="http://www.w3.org/1999/xhtml">';
                   5111:     } else {
                   5112: 	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>';
                   5113:     }
                   5114:     return $output;
                   5115: }
1.340     albertel 5116: 
                   5117: =pod
                   5118: 
1.306     albertel 5119: =item * &endheadtag()
                   5120: 
                   5121: Returns a uniform </head> for LON-CAPA web pages.
                   5122: 
                   5123: Inputs: none
                   5124: 
                   5125: =cut
                   5126: 
                   5127: sub endheadtag {
                   5128:     return '</head>';
                   5129: }
                   5130: 
                   5131: =pod
                   5132: 
                   5133: =item * &head()
                   5134: 
                   5135: Returns a uniform complete <head>..</head> section for LON-CAPA web pages.
                   5136: 
                   5137: Inputs: $title - optional title for the page
1.307     albertel 5138:         $head_extra - optional extra HTML to put inside the <head>
1.405     albertel 5139: 
1.306     albertel 5140: =cut
                   5141: 
                   5142: sub head {
1.325     albertel 5143:     my ($title,$head_extra,$args) = @_;
                   5144:     return &headtag($title,$head_extra,$args).&endheadtag();
1.306     albertel 5145: }
                   5146: 
                   5147: =pod
                   5148: 
                   5149: =item * &start_page()
                   5150: 
                   5151: Returns a complete <html> .. <body> section for LON-CAPA web pages.
                   5152: 
                   5153: Inputs: $title - optional title for the page
                   5154:         $head_extra - optional extra HTML to incude inside the <head>
1.315     albertel 5155:         $args - additional optional args supported are:
1.317     albertel 5156:                   only_body      -> is true will set &bodytag() onlybodytag
                   5157:                                     arg on
                   5158:                   no_nav_bar     -> is true will set &bodytag() notopbar arg on
                   5159:                   add_entries    -> additional attributes to add to the  <body>
                   5160:                   domain         -> force to color decorate a page for a 
                   5161:                                     specific domain
                   5162:                   function       -> force usage of a specific rolish color
                   5163:                                     scheme
                   5164:                   redirect       -> see &headtag()
                   5165:                   bgcolor        -> override the default page bg color
                   5166:                   js_ready       -> return a string ready for being used in 
                   5167:                                     a javascript writeln
1.320     albertel 5168:                   html_encode    -> return a string ready for being used in 
                   5169:                                     a html attribute
1.317     albertel 5170:                   force_register -> if is true will turn on the &bodytag()
                   5171:                                     $forcereg arg
1.326     albertel 5172:                   body_title     -> alternate text to use instead of $title
                   5173:                                     in the title box that appears, this text
                   5174:                                     is not auto translated like the $title is
1.330     albertel 5175:                   frameset       -> if true will start with a <frameset>
                   5176:                                     rather than <body>
1.338     albertel 5177:                   no_title       -> if true the title bar won't be shown
                   5178:                   skip_phases    -> hash ref of 
                   5179:                                     head -> skip the <html><head> generation
                   5180:                                     body -> skip all <body> generation
1.337     albertel 5181: 
1.361     albertel 5182:                   no_inline_link -> if true and in remote mode, don't show the 
                   5183:                                     'Switch To Inline Menu' link
                   5184: 
1.460     albertel 5185:                   no_auto_mt_title -> prevent &mt()ing the title arg
                   5186: 
1.562     albertel 5187:                   inherit_jsmath -> when creating popup window in a page,
                   5188:                                     should it have jsmath forced on by the
                   5189:                                     current page
                   5190: 
1.306     albertel 5191: =cut
                   5192: 
                   5193: sub start_page {
1.309     albertel 5194:     my ($title,$head_extra,$args) = @_;
1.318     albertel 5195:     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.313     albertel 5196:     my %head_args;
1.352     albertel 5197:     foreach my $arg ('redirect','force_register','domain','function',
1.460     albertel 5198: 		     'bgcolor','frameset','no_nav_bar','only_body',
                   5199: 		     'no_auto_mt_title') {
1.319     albertel 5200: 	if (defined($args->{$arg})) {
1.324     raeburn  5201: 	    $head_args{$arg} = $args->{$arg};
1.319     albertel 5202: 	}
1.313     albertel 5203:     }
1.319     albertel 5204: 
1.315     albertel 5205:     $env{'internal.start_page'}++;
1.338     albertel 5206:     my $result;
                   5207:     if (! exists($args->{'skip_phases'}{'head'}) ) {
                   5208: 	$result.=
1.341     albertel 5209: 	    &xml_begin().
1.338     albertel 5210: 	    &headtag($title,$head_extra,\%head_args).&endheadtag();
                   5211:     }
                   5212:     
                   5213:     if (! exists($args->{'skip_phases'}{'body'}) ) {
                   5214: 	if ($args->{'frameset'}) {
                   5215: 	    my $attr_string = &make_attr_string($args->{'force_register'},
                   5216: 						$args->{'add_entries'});
                   5217: 	    $result .= "\n<frameset $attr_string>\n";
                   5218: 	} else {
                   5219: 	    $result .=
                   5220: 		&bodytag($title, 
                   5221: 			 $args->{'function'},       $args->{'add_entries'},
                   5222: 			 $args->{'only_body'},      $args->{'domain'},
                   5223: 			 $args->{'force_register'}, $args->{'body_title'},
                   5224: 			 $args->{'no_nav_bar'},     $args->{'bgcolor'},
1.460     albertel 5225: 			 $args->{'no_title'},       $args->{'no_inline_link'},
                   5226: 			 $args);
1.338     albertel 5227: 	}
1.330     albertel 5228:     }
1.338     albertel 5229: 
1.315     albertel 5230:     if ($args->{'js_ready'}) {
1.317     albertel 5231: 	$result = &js_ready($result);
1.315     albertel 5232:     }
1.320     albertel 5233:     if ($args->{'html_encode'}) {
                   5234: 	$result = &html_encode($result);
                   5235:     }
1.315     albertel 5236:     return $result;
1.306     albertel 5237: }
                   5238: 
1.330     albertel 5239: 
1.306     albertel 5240: =pod
                   5241: 
                   5242: =item * &head()
                   5243: 
                   5244: Returns a complete </body></html> section for LON-CAPA web pages.
                   5245: 
1.315     albertel 5246: Inputs:         $args - additional optional args supported are:
                   5247:                  js_ready     -> return a string ready for being used in 
                   5248:                                  a javascript writeln
1.320     albertel 5249:                  html_encode  -> return a string ready for being used in 
                   5250:                                  a html attribute
1.330     albertel 5251:                  frameset     -> if true will start with a <frameset>
                   5252:                                  rather than <body>
1.493     albertel 5253:                  dicsussion   -> if true will get discussion from
                   5254:                                   lonxml::xmlend
                   5255:                                  (you can pass the target and parser arguments
                   5256:                                   through optional 'target' and 'parser' args
                   5257:                                   to this routine)
1.306     albertel 5258: 
                   5259: =cut
                   5260: 
                   5261: sub end_page {
1.315     albertel 5262:     my ($args) = @_;
                   5263:     $env{'internal.end_page'}++;
1.330     albertel 5264:     my $result;
1.335     albertel 5265:     if ($args->{'discussion'}) {
                   5266: 	my ($target,$parser);
                   5267: 	if (ref($args->{'discussion'})) {
                   5268: 	    ($target,$parser) =($args->{'discussion'}{'target'},
                   5269: 				$args->{'discussion'}{'parser'});
                   5270: 	}
                   5271: 	$result .= &Apache::lonxml::xmlend($target,$parser);
                   5272:     }
                   5273: 
1.330     albertel 5274:     if ($args->{'frameset'}) {
                   5275: 	$result .= '</frameset>';
                   5276:     } else {
                   5277: 	$result .= &endbodytag();
                   5278:     }
                   5279:     $result .= "\n</html>";
                   5280: 
1.315     albertel 5281:     if ($args->{'js_ready'}) {
1.317     albertel 5282: 	$result = &js_ready($result);
1.315     albertel 5283:     }
1.335     albertel 5284: 
1.320     albertel 5285:     if ($args->{'html_encode'}) {
                   5286: 	$result = &html_encode($result);
                   5287:     }
1.335     albertel 5288: 
1.315     albertel 5289:     return $result;
                   5290: }
                   5291: 
1.320     albertel 5292: sub html_encode {
                   5293:     my ($result) = @_;
                   5294: 
1.322     albertel 5295:     $result = &HTML::Entities::encode($result,'<>&"');
1.320     albertel 5296:     
                   5297:     return $result;
                   5298: }
1.317     albertel 5299: sub js_ready {
                   5300:     my ($result) = @_;
                   5301: 
1.323     albertel 5302:     $result =~ s/[\n\r]/ /xmsg;
                   5303:     $result =~ s/\\/\\\\/xmsg;
                   5304:     $result =~ s/'/\\'/xmsg;
1.372     albertel 5305:     $result =~ s{</}{<\\/}xmsg;
1.317     albertel 5306:     
                   5307:     return $result;
                   5308: }
                   5309: 
1.315     albertel 5310: sub validate_page {
                   5311:     if (  exists($env{'internal.start_page'})
1.316     albertel 5312: 	  &&     $env{'internal.start_page'} > 1) {
                   5313: 	&Apache::lonnet::logthis('start_page called multiple times '.
1.318     albertel 5314: 				 $env{'internal.start_page'}.' '.
1.316     albertel 5315: 				 $ENV{'request.filename'});
1.315     albertel 5316:     }
                   5317:     if (  exists($env{'internal.end_page'})
1.316     albertel 5318: 	  &&     $env{'internal.end_page'} > 1) {
                   5319: 	&Apache::lonnet::logthis('end_page called multiple times '.
1.318     albertel 5320: 				 $env{'internal.end_page'}.' '.
1.316     albertel 5321: 				 $env{'request.filename'});
1.315     albertel 5322:     }
                   5323:     if (     exists($env{'internal.start_page'})
                   5324: 	&& ! exists($env{'internal.end_page'})) {
1.316     albertel 5325: 	&Apache::lonnet::logthis('start_page called without end_page '.
                   5326: 				 $env{'request.filename'});
1.315     albertel 5327:     }
                   5328:     if (   ! exists($env{'internal.start_page'})
                   5329: 	&&   exists($env{'internal.end_page'})) {
1.316     albertel 5330: 	&Apache::lonnet::logthis('end_page called without start_page'.
                   5331: 				 $env{'request.filename'});
1.315     albertel 5332:     }
1.306     albertel 5333: }
1.315     albertel 5334: 
1.318     albertel 5335: sub simple_error_page {
                   5336:     my ($r,$title,$msg) = @_;
                   5337:     my $page =
                   5338: 	&Apache::loncommon::start_page($title).
                   5339: 	&mt($msg).
                   5340: 	&Apache::loncommon::end_page();
                   5341:     if (ref($r)) {
                   5342: 	$r->print($page);
1.327     albertel 5343: 	return;
1.318     albertel 5344:     }
                   5345:     return $page;
                   5346: }
1.347     albertel 5347: 
                   5348: {
                   5349:     my $row_count;
                   5350:     sub start_data_table {
1.422     albertel 5351: 	my ($add_class) = @_;
                   5352: 	my $css_class = (join(' ','LC_data_table',$add_class));
1.347     albertel 5353: 	undef($row_count);
1.422     albertel 5354: 	return '<table class="'.$css_class.'">'."\n";
1.347     albertel 5355:     }
                   5356: 
                   5357:     sub end_data_table {
                   5358: 	undef($row_count);
1.389     albertel 5359: 	return '</table>'."\n";;
1.347     albertel 5360:     }
                   5361: 
                   5362:     sub start_data_table_row {
1.422     albertel 5363: 	my ($add_class) = @_;
1.347     albertel 5364: 	$row_count++;
1.422     albertel 5365: 	my $css_class = ($row_count % 2)?'':'LC_even_row';
1.428     albertel 5366: 	$css_class = (join(' ',$css_class,$add_class));
1.422     albertel 5367: 	return  '<tr class="'.$css_class.'">'."\n";;
1.347     albertel 5368:     }
1.471     banghart 5369:     
                   5370:     sub continue_data_table_row {
                   5371: 	my ($add_class) = @_;
                   5372: 	my $css_class = ($row_count % 2)?'':'LC_even_row';
                   5373: 	$css_class = (join(' ',$css_class,$add_class));
                   5374: 	return  '<tr class="'.$css_class.'">'."\n";;
                   5375:     }
1.347     albertel 5376: 
                   5377:     sub end_data_table_row {
1.389     albertel 5378: 	return '</tr>'."\n";;
1.347     albertel 5379:     }
1.367     www      5380: 
1.421     albertel 5381:     sub start_data_table_empty_row {
                   5382: 	$row_count++;
                   5383: 	return  '<tr class="LC_empty_row" >'."\n";;
                   5384:     }
                   5385: 
                   5386:     sub end_data_table_empty_row {
                   5387: 	return '</tr>'."\n";;
                   5388:     }
                   5389: 
1.367     www      5390:     sub start_data_table_header_row {
1.389     albertel 5391: 	return  '<tr class="LC_header_row">'."\n";;
1.367     www      5392:     }
                   5393: 
                   5394:     sub end_data_table_header_row {
1.389     albertel 5395: 	return '</tr>'."\n";;
1.367     www      5396:     }
1.347     albertel 5397: }
                   5398: 
1.548     albertel 5399: =pod
                   5400: 
                   5401: =item * &inhibit_menu_check($arg)
                   5402: 
                   5403: Checks for a inhibitmenu state and generates output to preserve it
                   5404: 
                   5405: Inputs:         $arg - can be any of
                   5406:                      - undef - in which case the return value is a string 
                   5407:                                to add  into arguments list of a uri
                   5408:                      - 'input' - in which case the return value is a HTML
                   5409:                                  <form> <input> field of type hidden to
                   5410:                                  preserve the value
                   5411:                      - a url - in which case the return value is the url with
                   5412:                                the neccesary cgi args added to preserve the
                   5413:                                inhibitmenu state
                   5414:                      - a ref to a url - no return value, but the string is
                   5415:                                         updated to include the neccessary cgi
                   5416:                                         args to preserve the inhibitmenu state
                   5417: 
                   5418: =cut
                   5419: 
                   5420: sub inhibit_menu_check {
                   5421:     my ($arg) = @_;
                   5422:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   5423:     if ($arg eq 'input') {
                   5424: 	if ($env{'form.inhibitmenu'}) {
                   5425: 	    return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
                   5426: 	} else {
                   5427: 	    return
                   5428: 	}
                   5429:     }
                   5430:     if ($env{'form.inhibitmenu'}) {
                   5431: 	if (ref($arg)) {
                   5432: 	    $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   5433: 	} elsif ($arg eq '') {
                   5434: 	    $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
                   5435: 	} else {
                   5436: 	    $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   5437: 	}
                   5438:     }
                   5439:     if (!ref($arg)) {
                   5440: 	return $arg;
                   5441:     }
                   5442: }
                   5443: 
1.251     albertel 5444: ###############################################
1.182     matthew  5445: 
                   5446: =pod
                   5447: 
1.549     albertel 5448: =back
                   5449: 
                   5450: =head1 User Information Routines
                   5451: 
                   5452: =over 4
                   5453: 
1.405     albertel 5454: =item * &get_users_function()
1.182     matthew  5455: 
                   5456: Used by &bodytag to determine the current users primary role.
                   5457: Returns either 'student','coordinator','admin', or 'author'.
                   5458: 
                   5459: =cut
                   5460: 
                   5461: ###############################################
                   5462: sub get_users_function {
                   5463:     my $function = 'student';
1.258     albertel 5464:     if ($env{'request.role'}=~/^(cc|in|ta|ep)/) {
1.182     matthew  5465:         $function='coordinator';
                   5466:     }
1.258     albertel 5467:     if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182     matthew  5468:         $function='admin';
                   5469:     }
1.258     albertel 5470:     if (($env{'request.role'}=~/^(au|ca)/) ||
1.182     matthew  5471:         ($ENV{'REQUEST_URI'}=~/^(\/priv|\~)/)) {
                   5472:         $function='author';
                   5473:     }
                   5474:     return $function;
1.54      www      5475: }
1.99      www      5476: 
                   5477: ###############################################
                   5478: 
1.233     raeburn  5479: =pod
                   5480: 
1.542     raeburn  5481: =item * &check_user_status()
1.274     raeburn  5482: 
                   5483: Determines current status of supplied role for a
                   5484: specific user. Roles can be active, previous or future.
                   5485: 
                   5486: Inputs: 
                   5487: user's domain, user's username, course's domain,
1.375     raeburn  5488: course's number, optional section ID.
1.274     raeburn  5489: 
                   5490: Outputs:
                   5491: role status: active, previous or future. 
                   5492: 
                   5493: =cut
                   5494: 
                   5495: sub check_user_status {
1.412     raeburn  5496:     my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.274     raeburn  5497:     my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
                   5498:     my @uroles = keys %userinfo;
                   5499:     my $srchstr;
                   5500:     my $active_chk = 'none';
1.412     raeburn  5501:     my $now = time;
1.274     raeburn  5502:     if (@uroles > 0) {
1.412     raeburn  5503:         if (($role eq 'cc') || ($sec eq '') || (!defined($sec))) {
1.274     raeburn  5504:             $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
                   5505:         } else {
1.412     raeburn  5506:             $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
                   5507:         }
                   5508:         if (grep/^\Q$srchstr\E$/,@uroles) {
1.274     raeburn  5509:             my $role_end = 0;
                   5510:             my $role_start = 0;
                   5511:             $active_chk = 'active';
1.412     raeburn  5512:             if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
                   5513:                 $role_end = $1;
                   5514:                 if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
                   5515:                     $role_start = $1;
1.274     raeburn  5516:                 }
                   5517:             }
                   5518:             if ($role_start > 0) {
1.412     raeburn  5519:                 if ($now < $role_start) {
1.274     raeburn  5520:                     $active_chk = 'future';
                   5521:                 }
                   5522:             }
                   5523:             if ($role_end > 0) {
1.412     raeburn  5524:                 if ($now > $role_end) {
1.274     raeburn  5525:                     $active_chk = 'previous';
                   5526:                 }
                   5527:             }
                   5528:         }
                   5529:     }
                   5530:     return $active_chk;
                   5531: }
                   5532: 
                   5533: ###############################################
                   5534: 
                   5535: =pod
                   5536: 
1.405     albertel 5537: =item * &get_sections()
1.233     raeburn  5538: 
                   5539: Determines all the sections for a course including
                   5540: sections with students and sections containing other roles.
1.419     raeburn  5541: Incoming parameters: 
                   5542: 
                   5543: 1. domain
                   5544: 2. course number 
                   5545: 3. reference to array containing roles for which sections should 
                   5546: be gathered (optional).
                   5547: 4. reference to array containing status types for which sections 
                   5548: should be gathered (optional).
                   5549: 
                   5550: If the third argument is undefined, sections are gathered for any role. 
                   5551: If the fourth argument is undefined, sections are gathered for any status.
                   5552: Permissible values are 'active' or 'future' or 'previous'.
1.233     raeburn  5553:  
1.374     raeburn  5554: Returns section hash (keys are section IDs, values are
                   5555: number of users in each section), subject to the
1.419     raeburn  5556: optional roles filter, optional status filter 
1.233     raeburn  5557: 
                   5558: =cut
                   5559: 
                   5560: ###############################################
                   5561: sub get_sections {
1.419     raeburn  5562:     my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366     albertel 5563:     if (!defined($cdom) || !defined($cnum)) {
                   5564:         my $cid =  $env{'request.course.id'};
                   5565: 
                   5566: 	return if (!defined($cid));
                   5567: 
                   5568:         $cdom = $env{'course.'.$cid.'.domain'};
                   5569:         $cnum = $env{'course.'.$cid.'.num'};
                   5570:     }
                   5571: 
                   5572:     my %sectioncount;
1.419     raeburn  5573:     my $now = time;
1.240     albertel 5574: 
1.366     albertel 5575:     if (!defined($possible_roles) || (grep(/^st$/,@$possible_roles))) {
1.276     albertel 5576: 	my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240     albertel 5577: 	my $sec_index = &Apache::loncoursedata::CL_SECTION();
                   5578: 	my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419     raeburn  5579:         my $start_index = &Apache::loncoursedata::CL_START();
                   5580:         my $end_index = &Apache::loncoursedata::CL_END();
                   5581:         my $status;
1.366     albertel 5582: 	while (my ($student,$data) = each(%$classlist)) {
1.419     raeburn  5583: 	    my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
                   5584: 				                     $data->[$status_index],
                   5585:                                                      $data->[$start_index],
                   5586:                                                      $data->[$end_index]);
                   5587:             if ($stu_status eq 'Active') {
                   5588:                 $status = 'active';
                   5589:             } elsif ($end < $now) {
                   5590:                 $status = 'previous';
                   5591:             } elsif ($start > $now) {
                   5592:                 $status = 'future';
                   5593:             } 
                   5594: 	    if ($section ne '-1' && $section !~ /^\s*$/) {
                   5595:                 if ((!defined($possible_status)) || (($status ne '') && 
                   5596:                     (grep/^\Q$status\E$/,@{$possible_status}))) { 
                   5597: 		    $sectioncount{$section}++;
                   5598:                 }
1.240     albertel 5599: 	    }
                   5600: 	}
                   5601:     }
                   5602:     my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   5603:     foreach my $user (sort(keys(%courseroles))) {
                   5604: 	if ($user !~ /^(\w{2})/) { next; }
                   5605: 	my ($role) = ($user =~ /^(\w{2})/);
                   5606: 	if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419     raeburn  5607: 	my ($section,$status);
1.240     albertel 5608: 	if ($role eq 'cr' &&
                   5609: 	    $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
                   5610: 	    $section=$1;
                   5611: 	}
                   5612: 	if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
                   5613: 	if (!defined($section) || $section eq '-1') { next; }
1.419     raeburn  5614:         my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
                   5615:         if ($end == -1 && $start == -1) {
                   5616:             next; #deleted role
                   5617:         }
                   5618:         if (!defined($possible_status)) { 
                   5619:             $sectioncount{$section}++;
                   5620:         } else {
                   5621:             if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
                   5622:                 $status = 'active';
                   5623:             } elsif ($end < $now) {
                   5624:                 $status = 'future';
                   5625:             } elsif ($start > $now) {
                   5626:                 $status = 'previous';
                   5627:             }
                   5628:             if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
                   5629:                 $sectioncount{$section}++;
                   5630:             }
                   5631:         }
1.233     raeburn  5632:     }
1.366     albertel 5633:     return %sectioncount;
1.233     raeburn  5634: }
                   5635: 
1.274     raeburn  5636: ###############################################
1.294     raeburn  5637: 
                   5638: =pod
1.405     albertel 5639: 
                   5640: =item * &get_course_users()
                   5641: 
1.275     raeburn  5642: Retrieves usernames:domains for users in the specified course
                   5643: with specific role(s), and access status. 
                   5644: 
                   5645: Incoming parameters:
1.277     albertel 5646: 1. course domain
                   5647: 2. course number
                   5648: 3. access status: users must have - either active, 
1.275     raeburn  5649: previous, future, or all.
1.277     albertel 5650: 4. reference to array of permissible roles
1.288     raeburn  5651: 5. reference to array of section restrictions (optional)
                   5652: 6. reference to results object (hash of hashes).
                   5653: 7. reference to optional userdata hash
1.275     raeburn  5654: Keys of top level hash are roles.
                   5655: Keys of inner hashes are username:domain, with 
                   5656: values set to access type.
1.288     raeburn  5657: Optional userdata hash returns an array with arguments in the 
                   5658: same order as loncoursedata::get_classlist() for student data.
                   5659: 
                   5660: Entries for end, start, section and status are blank because
                   5661: of the possibility of multiple values for non-student roles.
                   5662: 
1.275     raeburn  5663: =cut
1.405     albertel 5664: 
1.275     raeburn  5665: ###############################################
1.405     albertel 5666: 
1.275     raeburn  5667: sub get_course_users {
1.288     raeburn  5668:     my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata) = @_;
                   5669:     my %idx = ();
1.419     raeburn  5670:     my %seclists;
1.288     raeburn  5671: 
                   5672:     $idx{udom} = &Apache::loncoursedata::CL_SDOM();
                   5673:     $idx{uname} =  &Apache::loncoursedata::CL_SNAME();
                   5674:     $idx{end} = &Apache::loncoursedata::CL_END();
                   5675:     $idx{start} = &Apache::loncoursedata::CL_START();
                   5676:     $idx{id} = &Apache::loncoursedata::CL_ID();
                   5677:     $idx{section} = &Apache::loncoursedata::CL_SECTION();
                   5678:     $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
                   5679:     $idx{status} = &Apache::loncoursedata::CL_STATUS();
                   5680: 
1.290     albertel 5681:     if (grep(/^st$/,@{$roles})) {
1.276     albertel 5682:         my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278     raeburn  5683:         my $now = time;
1.277     albertel 5684:         foreach my $student (keys(%{$classlist})) {
1.288     raeburn  5685:             my $match = 0;
1.412     raeburn  5686:             my $secmatch = 0;
1.419     raeburn  5687:             my $section = $$classlist{$student}[$idx{section}];
                   5688:             if ($section eq '') {
                   5689:                 $section = 'none';
                   5690:             }
1.291     albertel 5691:             if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 5692:                 if (grep(/^all$/,@{$sections})) {
1.412     raeburn  5693:                     $secmatch = 1;
                   5694:                 } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420     albertel 5695:                     if (grep(/^none$/,@{$sections})) {
1.412     raeburn  5696:                         $secmatch = 1;
                   5697:                     }
                   5698:                 } else {  
1.419     raeburn  5699: 		    if (grep(/^\Q$section\E$/,@{$sections})) {
1.412     raeburn  5700: 		        $secmatch = 1;
                   5701:                     }
1.290     albertel 5702: 		}
1.412     raeburn  5703:                 if (!$secmatch) {
                   5704:                     next;
                   5705:                 }
1.419     raeburn  5706:             }
1.420     albertel 5707:             push(@{$seclists{$student}},$section); 
1.275     raeburn  5708:             if (defined($$types{'active'})) {
1.288     raeburn  5709:                 if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275     raeburn  5710:                     push(@{$$users{st}{$student}},'active');
1.288     raeburn  5711:                     $match = 1;
1.275     raeburn  5712:                 }
                   5713:             }
                   5714:             if (defined($$types{'previous'})) {
1.288     raeburn  5715:                 if ($$classlist{$student}[$idx{end}] <= $now) {
1.275     raeburn  5716:                     push(@{$$users{st}{$student}},'previous');
1.288     raeburn  5717:                     $match = 1;
1.275     raeburn  5718:                 }
                   5719:             }
                   5720:             if (defined($$types{'future'})) {
1.288     raeburn  5721:                 if (($$classlist{$student}[$idx{start}] > $now) && ($$classlist{$student}[$idx{end}] > $now) || ($$classlist{$student}[$idx{end}] == 0) || ($$classlist{$student}[$idx{end}] eq '')) {
1.275     raeburn  5722:                     push(@{$$users{st}{$student}},'future');
1.288     raeburn  5723:                     $match = 1;
1.275     raeburn  5724:                 }
                   5725:             }
1.412     raeburn  5726:             if ($match && ref($userdata) eq 'HASH') {
1.288     raeburn  5727:                 $$userdata{$student} = $$classlist{$student};
                   5728:             }
1.275     raeburn  5729:         }
                   5730:     }
1.412     raeburn  5731:     if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439     raeburn  5732:         my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   5733:         my $now = time;
                   5734:         foreach my $person (sort(keys(%coursepersonnel))) {
1.288     raeburn  5735:             my $match = 0;
1.412     raeburn  5736:             my $secmatch = 0;
1.439     raeburn  5737:             my $status;
1.412     raeburn  5738:             my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275     raeburn  5739:             $user =~ s/:$//;
1.439     raeburn  5740:             my ($end,$start) = split(/:/,$coursepersonnel{$person});
                   5741:             if ($end == -1 || $start == -1) {
                   5742:                 next;
                   5743:             }
                   5744:             if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
                   5745:                 (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412     raeburn  5746:                 my ($uname,$udom) = split(/:/,$user);
                   5747:                 if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 5748:                     if (grep(/^all$/,@{$sections})) {
1.412     raeburn  5749:                         $secmatch = 1;
                   5750:                     } elsif ($usec eq '') {
1.420     albertel 5751:                         if (grep(/^none$/,@{$sections})) {
1.412     raeburn  5752:                             $secmatch = 1;
                   5753:                         }
                   5754:                     } else {
                   5755:                         if (grep(/^\Q$usec\E$/,@{$sections})) {
                   5756:                             $secmatch = 1;
                   5757:                         }
                   5758:                     }
                   5759:                     if (!$secmatch) {
                   5760:                         next;
                   5761:                     }
1.288     raeburn  5762:                 }
1.419     raeburn  5763:                 if ($usec eq '') {
                   5764:                     $usec = 'none';
                   5765:                 }
1.275     raeburn  5766:                 if ($uname ne '' && $udom ne '') {
1.503     raeburn  5767:                     if ($end > 0 && $end < $now) {
1.439     raeburn  5768:                         $status = 'previous';
                   5769:                     } elsif ($start > $now) {
                   5770:                         $status = 'future';
                   5771:                     } else {
                   5772:                         $status = 'active';
                   5773:                     }
1.277     albertel 5774:                     foreach my $type (keys(%{$types})) { 
1.275     raeburn  5775:                         if ($status eq $type) {
1.420     albertel 5776:                             if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419     raeburn  5777:                                 push(@{$$users{$role}{$user}},$type);
                   5778:                             }
1.288     raeburn  5779:                             $match = 1;
                   5780:                         }
                   5781:                     }
1.419     raeburn  5782:                     if (($match) && (ref($userdata) eq 'HASH')) {
                   5783:                         if (!exists($$userdata{$uname.':'.$udom})) {
                   5784: 			    &get_user_info($udom,$uname,\%idx,$userdata);
                   5785:                         }
1.420     albertel 5786:                         if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419     raeburn  5787:                             push(@{$seclists{$uname.':'.$udom}},$usec);
                   5788:                         }
1.275     raeburn  5789:                     }
                   5790:                 }
                   5791:             }
                   5792:         }
1.290     albertel 5793:         if (grep(/^ow$/,@{$roles})) {
1.279     raeburn  5794:             if ((defined($cdom)) && (defined($cnum))) {
                   5795:                 my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
                   5796:                 if ( defined($csettings{'internal.courseowner'}) ) {
                   5797:                     my $owner = $csettings{'internal.courseowner'};
1.439     raeburn  5798:                     if ($owner !~ /^[^:]+:[^:]+$/) {
                   5799:                         $owner = $owner.':'.$cdom;
                   5800:                     }
                   5801:                     @{$$users{'ow'}{$owner}} = 'any';
1.290     albertel 5802:                     if (defined($userdata) && 
                   5803: 			!exists($$userdata{$owner.':'.$cdom})) {
                   5804: 			&get_user_info($cdom,$owner,\%idx,$userdata);
1.420     albertel 5805:                         if (!grep(/^none$/,@{$seclists{$owner.':'.$cdom}})) {
1.419     raeburn  5806:                             push(@{$seclists{$owner.':'.$cdom}},'none');
                   5807:                         }
1.290     albertel 5808: 		    }
1.279     raeburn  5809:                 }
                   5810:             }
                   5811:         }
1.419     raeburn  5812:         foreach my $user (keys(%seclists)) {
                   5813:             @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
                   5814:             $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
                   5815:         }
1.275     raeburn  5816:     }
                   5817:     return;
                   5818: }
                   5819: 
1.288     raeburn  5820: sub get_user_info {
                   5821:     my ($udom,$uname,$idx,$userdata) = @_;
1.289     albertel 5822:     $$userdata{$uname.':'.$udom}[$$idx{fullname}] = 
                   5823: 	&plainname($uname,$udom,'lastname');
1.291     albertel 5824:     $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297     raeburn  5825:     $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.288     raeburn  5826:     return;
                   5827: }
1.275     raeburn  5828: 
1.472     raeburn  5829: ###############################################
                   5830: 
                   5831: =pod
                   5832: 
                   5833: =item * &get_user_quota()
                   5834: 
                   5835: Retrieves quota assigned for storage of portfolio files for a user  
                   5836: 
                   5837: Incoming parameters:
                   5838: 1. user's username
                   5839: 2. user's domain
                   5840: 
                   5841: Returns:
1.536     raeburn  5842: 1. Disk quota (in Mb) assigned to student.
                   5843: 2. (Optional) Type of setting: custom or default
                   5844:    (individually assigned or default for user's 
                   5845:    institutional status).
                   5846: 3. (Optional) - User's institutional status (e.g., faculty, staff
                   5847:    or student - types as defined in localenroll::inst_usertypes 
                   5848:    for user's domain, which determines default quota for user.
                   5849: 4. (Optional) - Default quota which would apply to the user.
1.472     raeburn  5850: 
                   5851: If a value has been stored in the user's environment, 
1.536     raeburn  5852: it will return that, otherwise it returns the maximal default
                   5853: defined for the user's instituional status(es) in the domain.
1.472     raeburn  5854: 
                   5855: =cut
                   5856: 
                   5857: ###############################################
                   5858: 
                   5859: 
                   5860: sub get_user_quota {
                   5861:     my ($uname,$udom) = @_;
1.536     raeburn  5862:     my ($quota,$quotatype,$settingstatus,$defquota);
1.472     raeburn  5863:     if (!defined($udom)) {
                   5864:         $udom = $env{'user.domain'};
                   5865:     }
                   5866:     if (!defined($uname)) {
                   5867:         $uname = $env{'user.name'};
                   5868:     }
                   5869:     if (($udom eq '' || $uname eq '') ||
                   5870:         ($udom eq 'public') && ($uname eq 'public')) {
                   5871:         $quota = 0;
1.536     raeburn  5872:         $quotatype = 'default';
                   5873:         $defquota = 0; 
1.472     raeburn  5874:     } else {
1.536     raeburn  5875:         my $inststatus;
1.472     raeburn  5876:         if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
                   5877:             $quota = $env{'environment.portfolioquota'};
1.536     raeburn  5878:             $inststatus = $env{'environment.inststatus'};
1.472     raeburn  5879:         } else {
1.536     raeburn  5880:             my %userenv = 
                   5881:                 &Apache::lonnet::get('environment',['portfolioquota',
                   5882:                                      'inststatus'],$udom,$uname);
1.472     raeburn  5883:             my ($tmp) = keys(%userenv);
                   5884:             if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   5885:                 $quota = $userenv{'portfolioquota'};
1.536     raeburn  5886:                 $inststatus = $userenv{'inststatus'};
1.472     raeburn  5887:             } else {
                   5888:                 undef(%userenv);
                   5889:             }
                   5890:         }
1.536     raeburn  5891:         ($defquota,$settingstatus) = &default_quota($udom,$inststatus);
1.472     raeburn  5892:         if ($quota eq '') {
1.536     raeburn  5893:             $quota = $defquota;
                   5894:             $quotatype = 'default';
                   5895:         } else {
                   5896:             $quotatype = 'custom';
1.472     raeburn  5897:         }
                   5898:     }
1.536     raeburn  5899:     if (wantarray) {
                   5900:         return ($quota,$quotatype,$settingstatus,$defquota);
                   5901:     } else {
                   5902:         return $quota;
                   5903:     }
1.472     raeburn  5904: }
                   5905: 
                   5906: ###############################################
                   5907: 
                   5908: =pod
                   5909: 
                   5910: =item * &default_quota()
                   5911: 
1.536     raeburn  5912: Retrieves default quota assigned for storage of user portfolio files,
                   5913: given an (optional) user's institutional status.
1.472     raeburn  5914: 
                   5915: Incoming parameters:
                   5916: 1. domain
1.536     raeburn  5917: 2. (Optional) institutional status(es).  This is a : separated list of 
                   5918:    status types (e.g., faculty, staff, student etc.)
                   5919:    which apply to the user for whom the default is being retrieved.
                   5920:    If the institutional status string in undefined, the domain
                   5921:    default quota will be returned. 
1.472     raeburn  5922: 
                   5923: Returns:
                   5924: 1. Default disk quota (in Mb) for user portfolios in the domain.
1.536     raeburn  5925: 2. (Optional) institutional type which determined the value of the
                   5926:    default quota.
1.472     raeburn  5927: 
                   5928: If a value has been stored in the domain's configuration db,
                   5929: it will return that, otherwise it returns 20 (for backwards 
                   5930: compatibility with domains which have not set up a configuration
                   5931: db file; the original statically defined portfolio quota was 20 Mb). 
                   5932: 
1.536     raeburn  5933: If the user's status includes multiple types (e.g., staff and student),
                   5934: the largest default quota which applies to the user determines the
                   5935: default quota returned.
                   5936: 
1.472     raeburn  5937: =cut
                   5938: 
                   5939: ###############################################
                   5940: 
                   5941: 
                   5942: sub default_quota {
1.536     raeburn  5943:     my ($udom,$inststatus) = @_;
                   5944:     my ($defquota,$settingstatus);
                   5945:     my %quotahash = &Apache::lonnet::get_dom('configuration',
                   5946:                                             ['quota'],$udom);
                   5947:     if (ref($quotahash{'quota'}) eq 'HASH') {
                   5948:         if ($inststatus ne '') {
                   5949:             my @statuses = split(/:/,$inststatus);
                   5950:             foreach my $item (@statuses) {
                   5951:                 if ($quotahash{'quota'}{$item} ne '') {
                   5952:                     if ($defquota eq '') {
                   5953:                         $defquota = $quotahash{'quota'}{$item};
                   5954:                         $settingstatus = $item;
                   5955:                     } elsif ($quotahash{'quota'}{$item} > $defquota) {
                   5956:                         $defquota = $quotahash{'quota'}{$item};
                   5957:                         $settingstatus = $item;
                   5958:                     }
                   5959:                 }
                   5960:             }
                   5961:         }
                   5962:         if ($defquota eq '') {
                   5963:             $defquota = $quotahash{'quota'}{'default'};
                   5964:             $settingstatus = 'default';
                   5965:         }
                   5966:     } else {
                   5967:         $settingstatus = 'default';
                   5968:         $defquota = 20;
                   5969:     }
                   5970:     if (wantarray) {
                   5971:         return ($defquota,$settingstatus);
1.472     raeburn  5972:     } else {
1.536     raeburn  5973:         return $defquota;
1.472     raeburn  5974:     }
                   5975: }
                   5976: 
1.384     raeburn  5977: sub get_secgrprole_info {
                   5978:     my ($cdom,$cnum,$needroles,$type)  = @_;
                   5979:     my %sections_count = &get_sections($cdom,$cnum);
                   5980:     my @sections =  (sort {$a <=> $b} keys(%sections_count));
                   5981:     my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
                   5982:     my @groups = sort(keys(%curr_groups));
                   5983:     my $allroles = [];
                   5984:     my $rolehash;
                   5985:     my $accesshash = {
                   5986:                      active => 'Currently has access',
                   5987:                      future => 'Will have future access',
                   5988:                      previous => 'Previously had access',
                   5989:                   };
                   5990:     if ($needroles) {
                   5991:         $rolehash = {'all' => 'all'};
1.385     albertel 5992:         my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   5993: 	if (&Apache::lonnet::error(%user_roles)) {
                   5994: 	    undef(%user_roles);
                   5995: 	}
                   5996:         foreach my $item (keys(%user_roles)) {
1.384     raeburn  5997:             my ($role)=split(/\:/,$item,2);
                   5998:             if ($role eq 'cr') { next; }
                   5999:             if ($role =~ /^cr/) {
                   6000:                 $$rolehash{$role} = (split('/',$role))[3];
                   6001:             } else {
                   6002:                 $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
                   6003:             }
                   6004:         }
                   6005:         foreach my $key (sort(keys(%{$rolehash}))) {
                   6006:             push(@{$allroles},$key);
                   6007:         }
                   6008:         push (@{$allroles},'st');
                   6009:         $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
                   6010:     }
                   6011:     return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
                   6012: }
                   6013: 
1.555     raeburn  6014: sub user_picker {
1.570     raeburn  6015:     my ($dom,$srch,$forcenewuser,$caller) = @_;
1.555     raeburn  6016:     my $currdom = $dom;
                   6017:     my %curr_selected = (
                   6018:                         srchin => 'dom',
1.580     raeburn  6019:                         srchby => 'lastname',
1.555     raeburn  6020:                       );
                   6021:     my $srchterm;
                   6022:     if (ref($srch) eq 'HASH') {
                   6023:         if ($srch->{'srchby'} ne '') {
                   6024:             $curr_selected{'srchby'} = $srch->{'srchby'};
                   6025:         }
                   6026:         if ($srch->{'srchin'} ne '') {
                   6027:             $curr_selected{'srchin'} = $srch->{'srchin'};
                   6028:         }
                   6029:         if ($srch->{'srchtype'} ne '') {
                   6030:             $curr_selected{'srchtype'} = $srch->{'srchtype'};
                   6031:         }
                   6032:         if ($srch->{'srchdomain'} ne '') {
                   6033:             $currdom = $srch->{'srchdomain'};
                   6034:         }
                   6035:         $srchterm = $srch->{'srchterm'};
                   6036:     }
                   6037:     my %lt=&Apache::lonlocal::texthash(
1.573     raeburn  6038:                     'usr'       => 'Search criteria',
1.563     raeburn  6039:                     'doma'      => 'Domain/institution to search',
1.558     albertel 6040:                     'uname'     => 'username',
                   6041:                     'lastname'  => 'last name',
1.555     raeburn  6042:                     'lastfirst' => 'last name, first name',
1.558     albertel 6043:                     'crs'       => 'in this course',
1.576     raeburn  6044:                     'dom'       => 'in selected LON-CAPA domain', 
1.558     albertel 6045:                     'alc'       => 'all LON-CAPA',
1.573     raeburn  6046:                     'instd'     => 'in institutional directory for selected domain',
1.558     albertel 6047:                     'exact'     => 'is',
                   6048:                     'contains'  => 'contains',
1.569     raeburn  6049:                     'begins'    => 'begins with',
1.571     raeburn  6050:                     'youm'      => "You must include some text to search for.",
                   6051:                     'thte'      => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
                   6052:                     'thet'      => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
                   6053:                     'yomc'      => "You must choose a domain when using an institutional directory search.",
                   6054:                     'ymcd'      => "You must choose a domain when using a domain search.",
                   6055:                     'whus'      => "When using searching by last,first you must include a comma as separator between last name and first name.",
                   6056:                     'whse'      => "When searching by last,first you must include at least one character in the first name.",
                   6057:                      'thfo'     => "The following need to be corrected before the search can be run:",
1.555     raeburn  6058:                                        );
1.563     raeburn  6059:     my $domform = &select_dom_form($currdom,'srchdomain',1,1);
                   6060:     my $srchinsel = ' <select name="srchin">';
1.555     raeburn  6061: 
                   6062:     my @srchins = ('crs','dom','alc','instd');
                   6063: 
                   6064:     foreach my $option (@srchins) {
                   6065:         # FIXME 'alc' option unavailable until 
                   6066:         #       loncreateuser::print_user_query_page()
                   6067:         #       has been completed.
                   6068:         next if ($option eq 'alc');
                   6069:         next if ($option eq 'crs' && !$env{'request.course.id'});
1.563     raeburn  6070:         if ($curr_selected{'srchin'} eq $option) {
                   6071:             $srchinsel .= ' 
                   6072:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   6073:         } else {
                   6074:             $srchinsel .= '
                   6075:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   6076:         }
1.555     raeburn  6077:     }
1.563     raeburn  6078:     $srchinsel .= "\n  </select>\n";
1.555     raeburn  6079: 
                   6080:     my $srchbysel =  ' <select name="srchby">';
1.580     raeburn  6081:     foreach my $option ('lastname','lastfirst','uname') {
1.555     raeburn  6082:         if ($curr_selected{'srchby'} eq $option) {
                   6083:             $srchbysel .= '
                   6084:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   6085:         } else {
                   6086:             $srchbysel .= '
                   6087:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   6088:          }
                   6089:     }
                   6090:     $srchbysel .= "\n  </select>\n";
                   6091: 
                   6092:     my $srchtypesel = ' <select name="srchtype">';
1.580     raeburn  6093:     foreach my $option ('begins','contains','exact') {
1.555     raeburn  6094:         if ($curr_selected{'srchtype'} eq $option) {
                   6095:             $srchtypesel .= '
                   6096:    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
                   6097:         } else {
                   6098:             $srchtypesel .= '
                   6099:    <option value="'.$option.'">'.$lt{$option}.'</option>';
                   6100:         }
                   6101:     }
                   6102:     $srchtypesel .= "\n  </select>\n";
                   6103: 
1.558     albertel 6104:     my ($newuserscript,$new_user_create);
1.556     raeburn  6105: 
                   6106:     if ($forcenewuser) {
1.576     raeburn  6107:         if (ref($srch) eq 'HASH') {
                   6108:             if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $env{'request.role.domain'}) {
                   6109: 	        $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>';
                   6110:             }
                   6111:         }
                   6112: 
1.556     raeburn  6113:         $newuserscript = <<"ENDSCRIPT";
                   6114: 
1.570     raeburn  6115: function setSearch(createnew,callingForm) {
1.556     raeburn  6116:     if (createnew == 1) {
1.570     raeburn  6117:         for (var i=0; i<callingForm.srchby.length; i++) {
                   6118:             if (callingForm.srchby.options[i].value == 'uname') {
                   6119:                 callingForm.srchby.selectedIndex = i;
1.556     raeburn  6120:             }
                   6121:         }
1.570     raeburn  6122:         for (var i=0; i<callingForm.srchin.length; i++) {
                   6123:             if ( callingForm.srchin.options[i].value == 'dom') {
                   6124: 		callingForm.srchin.selectedIndex = i;
1.556     raeburn  6125:             }
                   6126:         }
1.570     raeburn  6127:         for (var i=0; i<callingForm.srchtype.length; i++) {
                   6128:             if (callingForm.srchtype.options[i].value == 'exact') {
                   6129:                 callingForm.srchtype.selectedIndex = i;
1.556     raeburn  6130:             }
                   6131:         }
1.570     raeburn  6132:         for (var i=0; i<callingForm.srchdomain.length; i++) {
                   6133:             if (callingForm.srchdomain.options[i].value == '$env{'request.role.domain'}') {
                   6134:                 callingForm.srchdomain.selectedIndex = i;
1.556     raeburn  6135:             }
                   6136:         }
                   6137:     }
                   6138: }
                   6139: ENDSCRIPT
1.558     albertel 6140: 
1.556     raeburn  6141:     }
                   6142: 
1.555     raeburn  6143:     my $output = <<"END_BLOCK";
1.556     raeburn  6144: <script type="text/javascript">
1.570     raeburn  6145: function validateEntry(callingForm) {
1.558     albertel 6146: 
1.556     raeburn  6147:     var checkok = 1;
1.558     albertel 6148:     var srchin;
1.570     raeburn  6149:     for (var i=0; i<callingForm.srchin.length; i++) {
                   6150: 	if ( callingForm.srchin[i].checked ) {
                   6151: 	    srchin = callingForm.srchin[i].value;
1.558     albertel 6152: 	}
                   6153:     }
                   6154: 
1.570     raeburn  6155:     var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
                   6156:     var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
                   6157:     var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
                   6158:     var srchterm =  callingForm.srchterm.value;
                   6159:     var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556     raeburn  6160:     var msg = "";
                   6161: 
                   6162:     if (srchterm == "") {
                   6163:         checkok = 0;
1.571     raeburn  6164:         msg += "$lt{'youm'}\\n";
1.556     raeburn  6165:     }
                   6166: 
1.569     raeburn  6167:     if (srchtype== 'begins') {
                   6168:         if (srchterm.length < 2) {
                   6169:             checkok = 0;
1.571     raeburn  6170:             msg += "$lt{'thte'}\\n";
1.569     raeburn  6171:         }
                   6172:     }
                   6173: 
1.556     raeburn  6174:     if (srchtype== 'contains') {
                   6175:         if (srchterm.length < 3) {
                   6176:             checkok = 0;
1.571     raeburn  6177:             msg += "$lt{'thet'}\\n";
1.556     raeburn  6178:         }
                   6179:     }
                   6180:     if (srchin == 'instd') {
                   6181:         if (srchdomain == '') {
                   6182:             checkok = 0;
1.571     raeburn  6183:             msg += "$lt{'yomc'}\\n";
1.556     raeburn  6184:         }
                   6185:     }
                   6186:     if (srchin == 'dom') {
                   6187:         if (srchdomain == '') {
                   6188:             checkok = 0;
1.571     raeburn  6189:             msg += "$lt{'ymcd'}\\n";
1.556     raeburn  6190:         }
                   6191:     }
                   6192:     if (srchby == 'lastfirst') {
                   6193:         if (srchterm.indexOf(",") == -1) {
                   6194:             checkok = 0;
1.571     raeburn  6195:             msg += "$lt{'whus'}\\n";
1.556     raeburn  6196:         }
                   6197:         if (srchterm.indexOf(",") == srchterm.length -1) {
                   6198:             checkok = 0;
1.571     raeburn  6199:             msg += "$lt{'whse'}\\n";
1.556     raeburn  6200:         }
                   6201:     }
                   6202:     if (checkok == 0) {
1.571     raeburn  6203:         alert("$lt{'thfo'}\\n"+msg);
1.556     raeburn  6204:         return;
                   6205:     }
                   6206:     if (checkok == 1) {
1.570     raeburn  6207:         callingForm.submit();
1.556     raeburn  6208:     }
                   6209: }
                   6210: 
                   6211: $newuserscript
                   6212: 
                   6213: </script>
1.558     albertel 6214: 
                   6215: $new_user_create
                   6216: 
1.555     raeburn  6217: <table>
1.558     albertel 6218:  <tr>
1.573     raeburn  6219:   <td>$lt{'doma'}:</td>
                   6220:   <td>$domform</td>
                   6221:   </td>
                   6222:  </tr>
                   6223:  <tr>
                   6224:   <td>$lt{'usr'}:</td>
1.563     raeburn  6225:   <td>$srchbysel
                   6226:       $srchtypesel 
                   6227:       <input type="text" size="15" name="srchterm" value="$srchterm" />
1.564     albertel 6228:       $srchinsel 
1.563     raeburn  6229:   </td>
                   6230:  </tr>
1.555     raeburn  6231: </table>
                   6232: <br />
                   6233: END_BLOCK
1.558     albertel 6234: 
1.555     raeburn  6235:     return $output;
                   6236: }
                   6237: 
1.585     raeburn  6238: sub username_rule_check {
                   6239:     my ($srch,$caller) = @_;
                   6240:     my ($response,@curr_rules,%inst_results,$rulematch);
                   6241:     my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($srch->{'srchdomain'});
                   6242:     if (ref($srch) eq 'HASH') {
                   6243:         (my $inst_response,%inst_results) = 
                   6244:             &Apache::lonnet::get_instuser($srch->{'srchdomain'},
                   6245:                                           $srch->{'srchterm'});
                   6246:         my %domconfig = &Apache::lonnet::get_dom('configuration',
                   6247:                               ['usercreation'],$srch->{'srchdomain'});
                   6248:         if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   6249:             if (ref($domconfig{'usercreation'}{'username_rule'}) eq 'ARRAY') {
                   6250:                 @curr_rules = @{$domconfig{'usercreation'}{'username_rule'}};
                   6251:             }
                   6252:         }
                   6253:         if (@curr_rules > 0) {
                   6254:             my $domdesc = &Apache::lonnet::domain($srch->{'srchdomain'},'description');
                   6255:             my $instuser_reqd;
                   6256:             my %rule_check = &Apache::lonnet::inst_rulecheck($srch->{'srchdomain'},$srch->{'srchterm'},\@curr_rules);
                   6257:             foreach my $rule (@curr_rules) {
                   6258:                 if ($rule_check{$rule}) {
                   6259:                     $rulematch = $rule;
                   6260:                     if ($inst_response eq 'ok') {
                   6261:                         if (keys(%inst_results) == 0) {
                   6262:                             if ($caller eq 'new') {
                   6263:                                 $response = &mt('The username you chose matches the format of usernames defined for <span class="LC_cusr_emph">[_1]</span>, but the user does not exist in the institutional directory.',$domdesc).'<br />'.&mt("You must choose a username with a different format -- one that will not conflict with 'official' institutional usernames.");
                   6264:                             }
                   6265:                         }
                   6266:                     }
                   6267:                     last;
                   6268:                 }
                   6269:             }
                   6270:             if ($response) {
                   6271:                 if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
                   6272:                     if (@{$ruleorder} > 0) {
                   6273:                         $response .= '<br />'.&mt('Usernames with the following format(s) may <span class="LC_cusr_emph">only</span> be used for verified users at [_1]:',$domdesc).' <ul>';
                   6274:                         foreach my $rule (@{$ruleorder}) {
                   6275:                             if (grep(/^\Q$rule\E$/,@curr_rules)) {
                   6276:                                 if (ref($rules->{$rule}) eq 'HASH') {
                   6277:                                     $response .= '<li>'.$rules->{$rule}{'name'}.': '.
                   6278:                                                  $rules->{$rule}{'desc'}.'</li>';
                   6279:                                 }
                   6280:                             }
                   6281:                         }
                   6282:                     }
                   6283:                     $response .= '</ul>';
                   6284:                 }
                   6285:             }
                   6286:         }
                   6287:     }
                   6288:     return ($response,$rulematch,$rules,%inst_results);
                   6289: }
                   6290: 
1.112     bowersj2 6291: =pod
                   6292: 
1.549     albertel 6293: =back
                   6294: 
                   6295: =head1 HTTP Helpers
                   6296: 
                   6297: =over 4
                   6298: 
1.112     bowersj2 6299: =item * get_unprocessed_cgi($query,$possible_names)
                   6300: 
1.258     albertel 6301: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112     bowersj2 6302: $query.  The parameters listed in $possible_names (an array reference),
1.258     albertel 6303: will be set in $env{'form.name'} if they do not already exist.
1.112     bowersj2 6304: 
                   6305: Typically called with $ENV{'QUERY_STRING'} as the first parameter.  
                   6306: $possible_names is an ref to an array of form element names.  As an example:
                   6307: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258     albertel 6308: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112     bowersj2 6309: 
                   6310: =cut
1.1       albertel 6311: 
1.6       albertel 6312: sub get_unprocessed_cgi {
1.25      albertel 6313:   my ($query,$possible_names)= @_;
1.26      matthew  6314:   # $Apache::lonxml::debug=1;
1.356     albertel 6315:   foreach my $pair (split(/&/,$query)) {
                   6316:     my ($name, $value) = split(/=/,$pair);
1.369     www      6317:     $name = &unescape($name);
1.25      albertel 6318:     if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
                   6319:       $value =~ tr/+/ /;
                   6320:       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258     albertel 6321:       unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25      albertel 6322:     }
1.16      harris41 6323:   }
1.6       albertel 6324: }
                   6325: 
1.112     bowersj2 6326: =pod
                   6327: 
                   6328: =item * cacheheader() 
                   6329: 
                   6330: returns cache-controlling header code
                   6331: 
                   6332: =cut
                   6333: 
1.7       albertel 6334: sub cacheheader {
1.258     albertel 6335:     unless ($env{'request.method'} eq 'GET') { return ''; }
1.216     albertel 6336:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
                   6337:     my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7       albertel 6338:                 <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
                   6339:                 <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216     albertel 6340:     return $output;
1.7       albertel 6341: }
                   6342: 
1.112     bowersj2 6343: =pod
                   6344: 
                   6345: =item * no_cache($r) 
                   6346: 
                   6347: specifies header code to not have cache
                   6348: 
                   6349: =cut
                   6350: 
1.9       albertel 6351: sub no_cache {
1.216     albertel 6352:     my ($r) = @_;
                   6353:     if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258     albertel 6354: 	$env{'request.method'} ne 'GET') { return ''; }
1.216     albertel 6355:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
                   6356:     $r->no_cache(1);
                   6357:     $r->header_out("Expires" => $date);
                   6358:     $r->header_out("Pragma" => "no-cache");
1.123     www      6359: }
                   6360: 
                   6361: sub content_type {
1.181     albertel 6362:     my ($r,$type,$charset) = @_;
1.299     foxr     6363:     if ($r) {
                   6364: 	#  Note that printout.pl calls this with undef for $r.
                   6365: 	&no_cache($r);
                   6366:     }
1.258     albertel 6367:     if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181     albertel 6368:     unless ($charset) {
                   6369: 	$charset=&Apache::lonlocal::current_encoding;
                   6370:     }
                   6371:     if ($charset) { $type.='; charset='.$charset; }
                   6372:     if ($r) {
                   6373: 	$r->content_type($type);
                   6374:     } else {
                   6375: 	print("Content-type: $type\n\n");
                   6376:     }
1.9       albertel 6377: }
1.25      albertel 6378: 
1.112     bowersj2 6379: =pod
                   6380: 
                   6381: =item * add_to_env($name,$value) 
                   6382: 
1.258     albertel 6383: adds $name to the %env hash with value
1.112     bowersj2 6384: $value, if $name already exists, the entry is converted to an array
                   6385: reference and $value is added to the array.
                   6386: 
                   6387: =cut
                   6388: 
1.25      albertel 6389: sub add_to_env {
                   6390:   my ($name,$value)=@_;
1.258     albertel 6391:   if (defined($env{$name})) {
                   6392:     if (ref($env{$name})) {
1.25      albertel 6393:       #already have multiple values
1.258     albertel 6394:       push(@{ $env{$name} },$value);
1.25      albertel 6395:     } else {
                   6396:       #first time seeing multiple values, convert hash entry to an arrayref
1.258     albertel 6397:       my $first=$env{$name};
                   6398:       undef($env{$name});
                   6399:       push(@{ $env{$name} },$first,$value);
1.25      albertel 6400:     }
                   6401:   } else {
1.258     albertel 6402:     $env{$name}=$value;
1.25      albertel 6403:   }
1.31      albertel 6404: }
1.149     albertel 6405: 
                   6406: =pod
                   6407: 
                   6408: =item * get_env_multiple($name) 
                   6409: 
1.258     albertel 6410: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149     albertel 6411: values may be defined and end up as an array ref.
                   6412: 
                   6413: returns an array of values
                   6414: 
                   6415: =cut
                   6416: 
                   6417: sub get_env_multiple {
                   6418:     my ($name) = @_;
                   6419:     my @values;
1.258     albertel 6420:     if (defined($env{$name})) {
1.149     albertel 6421:         # exists is it an array
1.258     albertel 6422:         if (ref($env{$name})) {
                   6423:             @values=@{ $env{$name} };
1.149     albertel 6424:         } else {
1.258     albertel 6425:             $values[0]=$env{$name};
1.149     albertel 6426:         }
                   6427:     }
                   6428:     return(@values);
                   6429: }
                   6430: 
1.31      albertel 6431: 
1.41      ng       6432: =pod
1.45      matthew  6433: 
1.464     albertel 6434: =back
1.41      ng       6435: 
1.112     bowersj2 6436: =head1 CSV Upload/Handling functions
1.38      albertel 6437: 
1.41      ng       6438: =over 4
                   6439: 
1.112     bowersj2 6440: =item * upfile_store($r)
1.41      ng       6441: 
                   6442: Store uploaded file, $r should be the HTTP Request object,
1.258     albertel 6443: needs $env{'form.upfile'}
1.41      ng       6444: returns $datatoken to be put into hidden field
                   6445: 
                   6446: =cut
1.31      albertel 6447: 
                   6448: sub upfile_store {
                   6449:     my $r=shift;
1.258     albertel 6450:     $env{'form.upfile'}=~s/\r/\n/gs;
                   6451:     $env{'form.upfile'}=~s/\f/\n/gs;
                   6452:     $env{'form.upfile'}=~s/\n+/\n/gs;
                   6453:     $env{'form.upfile'}=~s/\n+$//gs;
1.31      albertel 6454: 
1.258     albertel 6455:     my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
                   6456: 	'_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31      albertel 6457:     {
1.158     raeburn  6458:         my $datafile = $r->dir_config('lonDaemons').
                   6459:                            '/tmp/'.$datatoken.'.tmp';
                   6460:         if ( open(my $fh,">$datafile") ) {
1.258     albertel 6461:             print $fh $env{'form.upfile'};
1.158     raeburn  6462:             close($fh);
                   6463:         }
1.31      albertel 6464:     }
                   6465:     return $datatoken;
                   6466: }
                   6467: 
1.56      matthew  6468: =pod
                   6469: 
1.112     bowersj2 6470: =item * load_tmp_file($r)
1.41      ng       6471: 
                   6472: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258     albertel 6473: needs $env{'form.datatoken'},
                   6474: sets $env{'form.upfile'} to the contents of the file
1.41      ng       6475: 
                   6476: =cut
1.31      albertel 6477: 
                   6478: sub load_tmp_file {
                   6479:     my $r=shift;
                   6480:     my @studentdata=();
                   6481:     {
1.158     raeburn  6482:         my $studentfile = $r->dir_config('lonDaemons').
1.258     albertel 6483:                               '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158     raeburn  6484:         if ( open(my $fh,"<$studentfile") ) {
                   6485:             @studentdata=<$fh>;
                   6486:             close($fh);
                   6487:         }
1.31      albertel 6488:     }
1.258     albertel 6489:     $env{'form.upfile'}=join('',@studentdata);
1.31      albertel 6490: }
                   6491: 
1.56      matthew  6492: =pod
                   6493: 
1.112     bowersj2 6494: =item * upfile_record_sep()
1.41      ng       6495: 
                   6496: Separate uploaded file into records
                   6497: returns array of records,
1.258     albertel 6498: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41      ng       6499: 
                   6500: =cut
1.31      albertel 6501: 
                   6502: sub upfile_record_sep {
1.258     albertel 6503:     if ($env{'form.upfiletype'} eq 'xml') {
1.31      albertel 6504:     } else {
1.248     albertel 6505: 	my @records;
1.258     albertel 6506: 	foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248     albertel 6507: 	    if ($line=~/^\s*$/) { next; }
                   6508: 	    push(@records,$line);
                   6509: 	}
                   6510: 	return @records;
1.31      albertel 6511:     }
                   6512: }
                   6513: 
1.56      matthew  6514: =pod
                   6515: 
1.112     bowersj2 6516: =item * record_sep($record)
1.41      ng       6517: 
1.258     albertel 6518: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41      ng       6519: 
                   6520: =cut
                   6521: 
1.263     www      6522: sub takeleft {
                   6523:     my $index=shift;
                   6524:     return substr('0000'.$index,-4,4);
                   6525: }
                   6526: 
1.31      albertel 6527: sub record_sep {
                   6528:     my $record=shift;
                   6529:     my %components=();
1.258     albertel 6530:     if ($env{'form.upfiletype'} eq 'xml') {
                   6531:     } elsif ($env{'form.upfiletype'} eq 'space') {
1.31      albertel 6532:         my $i=0;
1.356     albertel 6533:         foreach my $field (split(/\s+/,$record)) {
1.31      albertel 6534:             $field=~s/^(\"|\')//;
                   6535:             $field=~s/(\"|\')$//;
1.263     www      6536:             $components{&takeleft($i)}=$field;
1.31      albertel 6537:             $i++;
                   6538:         }
1.258     albertel 6539:     } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31      albertel 6540:         my $i=0;
1.356     albertel 6541:         foreach my $field (split(/\t/,$record)) {
1.31      albertel 6542:             $field=~s/^(\"|\')//;
                   6543:             $field=~s/(\"|\')$//;
1.263     www      6544:             $components{&takeleft($i)}=$field;
1.31      albertel 6545:             $i++;
                   6546:         }
                   6547:     } else {
1.561     www      6548:         my $separator=',';
1.480     banghart 6549:         if ($env{'form.upfiletype'} eq 'semisv') {
1.561     www      6550:             $separator=';';
1.480     banghart 6551:         }
1.31      albertel 6552:         my $i=0;
1.561     www      6553: # the character we are looking for to indicate the end of a quote or a record 
                   6554:         my $looking_for=$separator;
                   6555: # do not add the characters to the fields
                   6556:         my $ignore=0;
                   6557: # we just encountered a separator (or the beginning of the record)
                   6558:         my $just_found_separator=1;
                   6559: # store the field we are working on here
                   6560:         my $field='';
                   6561: # work our way through all characters in record
                   6562:         foreach my $character ($record=~/(.)/g) {
                   6563:             if ($character eq $looking_for) {
                   6564:                if ($character ne $separator) {
                   6565: # Found the end of a quote, again looking for separator
                   6566:                   $looking_for=$separator;
                   6567:                   $ignore=1;
                   6568:                } else {
                   6569: # Found a separator, store away what we got
                   6570:                   $components{&takeleft($i)}=$field;
                   6571: 	          $i++;
                   6572:                   $just_found_separator=1;
                   6573:                   $ignore=0;
                   6574:                   $field='';
                   6575:                }
                   6576:                next;
                   6577:             }
                   6578: # single or double quotation marks after a separator indicate beginning of a quote
                   6579: # we are now looking for the end of the quote and need to ignore separators
                   6580:             if ((($character eq '"') || ($character eq "'")) && ($just_found_separator))  {
                   6581:                $looking_for=$character;
                   6582:                next;
                   6583:             }
                   6584: # ignore would be true after we reached the end of a quote
                   6585:             if ($ignore) { next; }
                   6586:             if (($just_found_separator) && ($character=~/\s/)) { next; }
                   6587:             $field.=$character;
                   6588:             $just_found_separator=0; 
1.31      albertel 6589:         }
1.561     www      6590: # catch the very last entry, since we never encountered the separator
                   6591:         $components{&takeleft($i)}=$field;
1.31      albertel 6592:     }
                   6593:     return %components;
                   6594: }
                   6595: 
1.144     matthew  6596: ######################################################
                   6597: ######################################################
                   6598: 
1.56      matthew  6599: =pod
                   6600: 
1.112     bowersj2 6601: =item * upfile_select_html()
1.41      ng       6602: 
1.144     matthew  6603: Return HTML code to select a file from the users machine and specify 
                   6604: the file type.
1.41      ng       6605: 
                   6606: =cut
                   6607: 
1.144     matthew  6608: ######################################################
                   6609: ######################################################
1.31      albertel 6610: sub upfile_select_html {
1.144     matthew  6611:     my %Types = (
                   6612:                  csv   => &mt('CSV (comma separated values, spreadsheet)'),
1.480     banghart 6613:                  semisv => &mt('Semicolon separated values'),
1.144     matthew  6614:                  space => &mt('Space separated'),
                   6615:                  tab   => &mt('Tabulator separated'),
                   6616: #                 xml   => &mt('HTML/XML'),
                   6617:                  );
                   6618:     my $Str = '<input type="file" name="upfile" size="50" />'.
                   6619:         '<br />Type: <select name="upfiletype">';
                   6620:     foreach my $type (sort(keys(%Types))) {
                   6621:         $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
                   6622:     }
                   6623:     $Str .= "</select>\n";
                   6624:     return $Str;
1.31      albertel 6625: }
                   6626: 
1.301     albertel 6627: sub get_samples {
                   6628:     my ($records,$toget) = @_;
                   6629:     my @samples=({});
                   6630:     my $got=0;
                   6631:     foreach my $rec (@$records) {
                   6632: 	my %temp = &record_sep($rec);
                   6633: 	if (! grep(/\S/, values(%temp))) { next; }
                   6634: 	if (%temp) {
                   6635: 	    $samples[$got]=\%temp;
                   6636: 	    $got++;
                   6637: 	    if ($got == $toget) { last; }
                   6638: 	}
                   6639:     }
                   6640:     return \@samples;
                   6641: }
                   6642: 
1.144     matthew  6643: ######################################################
                   6644: ######################################################
                   6645: 
1.56      matthew  6646: =pod
                   6647: 
1.112     bowersj2 6648: =item * csv_print_samples($r,$records)
1.41      ng       6649: 
                   6650: Prints a table of sample values from each column uploaded $r is an
                   6651: Apache Request ref, $records is an arrayref from
                   6652: &Apache::loncommon::upfile_record_sep
                   6653: 
                   6654: =cut
                   6655: 
1.144     matthew  6656: ######################################################
                   6657: ######################################################
1.31      albertel 6658: sub csv_print_samples {
                   6659:     my ($r,$records) = @_;
1.301     albertel 6660:     my $samples = &get_samples($records,3);
                   6661: 
1.144     matthew  6662:     $r->print(&mt('Samples').'<br /><table border="2"><tr>');
1.356     albertel 6663:     foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { 
                   6664:         $r->print('<th>'.&mt('Column&nbsp;[_1]',($sample+1)).'</th>'); }
1.31      albertel 6665:     $r->print('</tr>');
1.301     albertel 6666:     foreach my $hash (@$samples) {
1.31      albertel 6667: 	$r->print('<tr>');
1.356     albertel 6668: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31      albertel 6669: 	    $r->print('<td>');
1.356     albertel 6670: 	    if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31      albertel 6671: 	    $r->print('</td>');
                   6672: 	}
                   6673: 	$r->print('</tr>');
                   6674:     }
                   6675:     $r->print('</tr></table><br />'."\n");
                   6676: }
                   6677: 
1.144     matthew  6678: ######################################################
                   6679: ######################################################
                   6680: 
1.56      matthew  6681: =pod
                   6682: 
1.112     bowersj2 6683: =item * csv_print_select_table($r,$records,$d)
1.41      ng       6684: 
                   6685: Prints a table to create associations between values and table columns.
1.144     matthew  6686: 
1.41      ng       6687: $r is an Apache Request ref,
                   6688: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174     matthew  6689: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41      ng       6690: 
                   6691: =cut
                   6692: 
1.144     matthew  6693: ######################################################
                   6694: ######################################################
1.31      albertel 6695: sub csv_print_select_table {
                   6696:     my ($r,$records,$d) = @_;
1.301     albertel 6697:     my $i=0;
                   6698:     my $samples = &get_samples($records,1);
1.144     matthew  6699:     $r->print(&mt('Associate columns with student attributes.')."\n".
                   6700: 	     '<table border="2"><tr>'.
                   6701:               '<th>'.&mt('Attribute').'</th>'.
                   6702:               '<th>'.&mt('Column').'</th></tr>'."\n");
1.356     albertel 6703:     foreach my $array_ref (@$d) {
                   6704: 	my ($value,$display,$defaultcol)=@{ $array_ref };
1.31      albertel 6705: 	$r->print('<tr><td>'.$display.'</td>');
                   6706: 
                   6707: 	$r->print('<td><select name=f'.$i.
1.32      matthew  6708: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 6709: 	$r->print('<option value="none"></option>');
1.356     albertel 6710: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
                   6711: 	    $r->print('<option value="'.$sample.'"'.
                   6712:                       ($sample eq $defaultcol ? ' selected="selected" ' : '').
                   6713:                       '>Column '.($sample+1).'</option>');
1.31      albertel 6714: 	}
                   6715: 	$r->print('</select></td></tr>'."\n");
                   6716: 	$i++;
                   6717:     }
                   6718:     $i--;
                   6719:     return $i;
                   6720: }
1.56      matthew  6721: 
1.144     matthew  6722: ######################################################
                   6723: ######################################################
                   6724: 
1.56      matthew  6725: =pod
1.31      albertel 6726: 
1.112     bowersj2 6727: =item * csv_samples_select_table($r,$records,$d)
1.41      ng       6728: 
                   6729: Prints a table of sample values from the upload and can make associate samples to internal names.
                   6730: 
                   6731: $r is an Apache Request ref,
                   6732: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
                   6733: $d is an array of 2 element arrays (internal name, displayed name)
                   6734: 
                   6735: =cut
                   6736: 
1.144     matthew  6737: ######################################################
                   6738: ######################################################
1.31      albertel 6739: sub csv_samples_select_table {
                   6740:     my ($r,$records,$d) = @_;
                   6741:     my $i=0;
1.144     matthew  6742:     #
1.301     albertel 6743:     my $samples = &get_samples($records,3);
1.144     matthew  6744:     $r->print('<table border=2><tr><th>'.
                   6745:               &mt('Field').'</th><th>'.&mt('Samples').'</th></tr>');
1.301     albertel 6746: 
                   6747:     foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.144     matthew  6748: 	$r->print('<tr><td><select name="f'.$i.'"'.
1.32      matthew  6749: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.301     albertel 6750: 	foreach my $option (@$d) {
                   6751: 	    my ($value,$display,$defaultcol)=@{ $option };
1.174     matthew  6752: 	    $r->print('<option value="'.$value.'"'.
1.253     albertel 6753:                       ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174     matthew  6754:                       $display.'</option>');
1.31      albertel 6755: 	}
                   6756: 	$r->print('</select></td><td>');
1.301     albertel 6757: 	foreach my $line (0..2) {
                   6758: 	    if (defined($samples->[$line]{$key})) { 
                   6759: 		$r->print($samples->[$line]{$key}."<br />\n"); 
                   6760: 	    }
                   6761: 	}
1.31      albertel 6762: 	$r->print('</td></tr>');
                   6763: 	$i++;
                   6764:     }
                   6765:     $i--;
                   6766:     return($i);
1.115     matthew  6767: }
                   6768: 
1.144     matthew  6769: ######################################################
                   6770: ######################################################
                   6771: 
1.115     matthew  6772: =pod
                   6773: 
                   6774: =item clean_excel_name($name)
                   6775: 
                   6776: Returns a replacement for $name which does not contain any illegal characters.
                   6777: 
                   6778: =cut
                   6779: 
1.144     matthew  6780: ######################################################
                   6781: ######################################################
1.115     matthew  6782: sub clean_excel_name {
                   6783:     my ($name) = @_;
                   6784:     $name =~ s/[:\*\?\/\\]//g;
                   6785:     if (length($name) > 31) {
                   6786:         $name = substr($name,0,31);
                   6787:     }
                   6788:     return $name;
1.25      albertel 6789: }
1.84      albertel 6790: 
1.85      albertel 6791: =pod
                   6792: 
1.112     bowersj2 6793: =item * check_if_partid_hidden($id,$symb,$udom,$uname)
1.85      albertel 6794: 
                   6795: Returns either 1 or undef
                   6796: 
                   6797: 1 if the part is to be hidden, undef if it is to be shown
                   6798: 
                   6799: Arguments are:
                   6800: 
                   6801: $id the id of the part to be checked
                   6802: $symb, optional the symb of the resource to check
                   6803: $udom, optional the domain of the user to check for
                   6804: $uname, optional the username of the user to check for
                   6805: 
                   6806: =cut
1.84      albertel 6807: 
                   6808: sub check_if_partid_hidden {
                   6809:     my ($id,$symb,$udom,$uname) = @_;
1.133     albertel 6810:     my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84      albertel 6811: 					 $symb,$udom,$uname);
1.141     albertel 6812:     my $truth=1;
                   6813:     #if the string starts with !, then the list is the list to show not hide
                   6814:     if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84      albertel 6815:     my @hiddenlist=split(/,/,$hiddenparts);
                   6816:     foreach my $checkid (@hiddenlist) {
1.141     albertel 6817: 	if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84      albertel 6818:     }
1.141     albertel 6819:     return !$truth;
1.84      albertel 6820: }
1.127     matthew  6821: 
1.138     matthew  6822: 
                   6823: ############################################################
                   6824: ############################################################
                   6825: 
                   6826: =pod
                   6827: 
1.157     matthew  6828: =back 
                   6829: 
1.138     matthew  6830: =head1 cgi-bin script and graphing routines
                   6831: 
1.157     matthew  6832: =over 4
                   6833: 
1.138     matthew  6834: =item get_cgi_id
                   6835: 
                   6836: Inputs: none
                   6837: 
                   6838: Returns an id which can be used to pass environment variables
                   6839: to various cgi-bin scripts.  These environment variables will
                   6840: be removed from the users environment after a given time by
                   6841: the routine &Apache::lonnet::transfer_profile_to_env.
                   6842: 
                   6843: =cut
                   6844: 
                   6845: ############################################################
                   6846: ############################################################
1.152     albertel 6847: my $uniq=0;
1.136     matthew  6848: sub get_cgi_id {
1.154     albertel 6849:     $uniq=($uniq+1)%100000;
1.280     albertel 6850:     return (time.'_'.$$.'_'.$uniq);
1.136     matthew  6851: }
                   6852: 
1.127     matthew  6853: ############################################################
                   6854: ############################################################
                   6855: 
                   6856: =pod
                   6857: 
1.134     matthew  6858: =item DrawBarGraph
1.127     matthew  6859: 
1.138     matthew  6860: Facilitates the plotting of data in a (stacked) bar graph.
                   6861: Puts plot definition data into the users environment in order for 
                   6862: graph.png to plot it.  Returns an <img> tag for the plot.
                   6863: The bars on the plot are labeled '1','2',...,'n'.
                   6864: 
                   6865: Inputs:
                   6866: 
                   6867: =over 4
                   6868: 
                   6869: =item $Title: string, the title of the plot
                   6870: 
                   6871: =item $xlabel: string, text describing the X-axis of the plot
                   6872: 
                   6873: =item $ylabel: string, text describing the Y-axis of the plot
                   6874: 
                   6875: =item $Max: scalar, the maximum Y value to use in the plot
                   6876: If $Max is < any data point, the graph will not be rendered.
                   6877: 
1.140     matthew  6878: =item $colors: array ref holding the colors to be used for the data sets when
1.138     matthew  6879: they are plotted.  If undefined, default values will be used.
                   6880: 
1.178     matthew  6881: =item $labels: array ref holding the labels to use on the x-axis for the bars.
                   6882: 
1.138     matthew  6883: =item @Values: An array of array references.  Each array reference holds data
                   6884: to be plotted in a stacked bar chart.
                   6885: 
1.239     matthew  6886: =item If the final element of @Values is a hash reference the key/value
                   6887: pairs will be added to the graph definition.
                   6888: 
1.138     matthew  6889: =back
                   6890: 
                   6891: Returns:
                   6892: 
                   6893: An <img> tag which references graph.png and the appropriate identifying
                   6894: information for the plot.
                   6895: 
1.127     matthew  6896: =cut
                   6897: 
                   6898: ############################################################
                   6899: ############################################################
1.134     matthew  6900: sub DrawBarGraph {
1.178     matthew  6901:     my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134     matthew  6902:     #
                   6903:     if (! defined($colors)) {
                   6904:         $colors = ['#33ff00', 
                   6905:                   '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
                   6906:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                   6907:                   ]; 
                   6908:     }
1.228     matthew  6909:     my $extra_settings = {};
                   6910:     if (ref($Values[-1]) eq 'HASH') {
                   6911:         $extra_settings = pop(@Values);
                   6912:     }
1.127     matthew  6913:     #
1.136     matthew  6914:     my $identifier = &get_cgi_id();
                   6915:     my $id = 'cgi.'.$identifier;        
1.129     matthew  6916:     if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127     matthew  6917:         return '';
                   6918:     }
1.225     matthew  6919:     #
                   6920:     my @Labels;
                   6921:     if (defined($labels)) {
                   6922:         @Labels = @$labels;
                   6923:     } else {
                   6924:         for (my $i=0;$i<@{$Values[0]};$i++) {
                   6925:             push (@Labels,$i+1);
                   6926:         }
                   6927:     }
                   6928:     #
1.129     matthew  6929:     my $NumBars = scalar(@{$Values[0]});
1.225     matthew  6930:     if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129     matthew  6931:     my %ValuesHash;
                   6932:     my $NumSets=1;
                   6933:     foreach my $array (@Values) {
                   6934:         next if (! ref($array));
1.136     matthew  6935:         $ValuesHash{$id.'.data.'.$NumSets++} = 
1.132     matthew  6936:             join(',',@$array);
1.129     matthew  6937:     }
1.127     matthew  6938:     #
1.136     matthew  6939:     my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225     matthew  6940:     if ($NumBars < 3) {
                   6941:         $width = 120+$NumBars*32;
1.220     matthew  6942:         $xskip = 1;
1.225     matthew  6943:         $bar_width = 30;
                   6944:     } elsif ($NumBars < 5) {
                   6945:         $width = 120+$NumBars*20;
                   6946:         $xskip = 1;
                   6947:         $bar_width = 20;
1.220     matthew  6948:     } elsif ($NumBars < 10) {
1.136     matthew  6949:         $width = 120+$NumBars*15;
                   6950:         $xskip = 1;
                   6951:         $bar_width = 15;
                   6952:     } elsif ($NumBars <= 25) {
                   6953:         $width = 120+$NumBars*11;
                   6954:         $xskip = 5;
                   6955:         $bar_width = 8;
                   6956:     } elsif ($NumBars <= 50) {
                   6957:         $width = 120+$NumBars*8;
                   6958:         $xskip = 5;
                   6959:         $bar_width = 4;
                   6960:     } else {
                   6961:         $width = 120+$NumBars*8;
                   6962:         $xskip = 5;
                   6963:         $bar_width = 4;
                   6964:     }
                   6965:     #
1.137     matthew  6966:     $Max = 1 if ($Max < 1);
                   6967:     if ( int($Max) < $Max ) {
                   6968:         $Max++;
                   6969:         $Max = int($Max);
                   6970:     }
1.127     matthew  6971:     $Title  = '' if (! defined($Title));
                   6972:     $xlabel = '' if (! defined($xlabel));
                   6973:     $ylabel = '' if (! defined($ylabel));
1.369     www      6974:     $ValuesHash{$id.'.title'}    = &escape($Title);
                   6975:     $ValuesHash{$id.'.xlabel'}   = &escape($xlabel);
                   6976:     $ValuesHash{$id.'.ylabel'}   = &escape($ylabel);
1.137     matthew  6977:     $ValuesHash{$id.'.y_max_value'} = $Max;
1.136     matthew  6978:     $ValuesHash{$id.'.NumBars'}  = $NumBars;
                   6979:     $ValuesHash{$id.'.NumSets'}  = $NumSets;
                   6980:     $ValuesHash{$id.'.PlotType'} = 'bar';
                   6981:     $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   6982:     $ValuesHash{$id.'.height'}   = $height;
                   6983:     $ValuesHash{$id.'.width'}    = $width;
                   6984:     $ValuesHash{$id.'.xskip'}    = $xskip;
                   6985:     $ValuesHash{$id.'.bar_width'} = $bar_width;
                   6986:     $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127     matthew  6987:     #
1.228     matthew  6988:     # Deal with other parameters
                   6989:     while (my ($key,$value) = each(%$extra_settings)) {
                   6990:         $ValuesHash{$id.'.'.$key} = $value;
                   6991:     }
                   6992:     #
1.137     matthew  6993:     &Apache::lonnet::appenv(%ValuesHash);
                   6994:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   6995: }
                   6996: 
                   6997: ############################################################
                   6998: ############################################################
                   6999: 
                   7000: =pod
                   7001: 
                   7002: =item DrawXYGraph
                   7003: 
1.138     matthew  7004: Facilitates the plotting of data in an XY graph.
                   7005: Puts plot definition data into the users environment in order for 
                   7006: graph.png to plot it.  Returns an <img> tag for the plot.
                   7007: 
                   7008: Inputs:
                   7009: 
                   7010: =over 4
                   7011: 
                   7012: =item $Title: string, the title of the plot
                   7013: 
                   7014: =item $xlabel: string, text describing the X-axis of the plot
                   7015: 
                   7016: =item $ylabel: string, text describing the Y-axis of the plot
                   7017: 
                   7018: =item $Max: scalar, the maximum Y value to use in the plot
                   7019: If $Max is < any data point, the graph will not be rendered.
                   7020: 
                   7021: =item $colors: Array ref containing the hex color codes for the data to be 
                   7022: plotted in.  If undefined, default values will be used.
                   7023: 
                   7024: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   7025: 
                   7026: =item $Ydata: Array ref containing Array refs.  
1.185     www      7027: Each of the contained arrays will be plotted as a separate curve.
1.138     matthew  7028: 
                   7029: =item %Values: hash indicating or overriding any default values which are 
                   7030: passed to graph.png.  
                   7031: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   7032: 
                   7033: =back
                   7034: 
                   7035: Returns:
                   7036: 
                   7037: An <img> tag which references graph.png and the appropriate identifying
                   7038: information for the plot.
                   7039: 
1.137     matthew  7040: =cut
                   7041: 
                   7042: ############################################################
                   7043: ############################################################
                   7044: sub DrawXYGraph {
                   7045:     my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
                   7046:     #
                   7047:     # Create the identifier for the graph
                   7048:     my $identifier = &get_cgi_id();
                   7049:     my $id = 'cgi.'.$identifier;
                   7050:     #
                   7051:     $Title  = '' if (! defined($Title));
                   7052:     $xlabel = '' if (! defined($xlabel));
                   7053:     $ylabel = '' if (! defined($ylabel));
                   7054:     my %ValuesHash = 
                   7055:         (
1.369     www      7056:          $id.'.title'  => &escape($Title),
                   7057:          $id.'.xlabel' => &escape($xlabel),
                   7058:          $id.'.ylabel' => &escape($ylabel),
1.137     matthew  7059:          $id.'.y_max_value'=> $Max,
                   7060:          $id.'.labels'     => join(',',@$Xlabels),
                   7061:          $id.'.PlotType'   => 'XY',
                   7062:          );
                   7063:     #
                   7064:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   7065:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   7066:     }
                   7067:     #
                   7068:     if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
                   7069:         return '';
                   7070:     }
                   7071:     my $NumSets=1;
1.138     matthew  7072:     foreach my $array (@{$Ydata}){
1.137     matthew  7073:         next if (! ref($array));
                   7074:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
                   7075:     }
1.138     matthew  7076:     $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137     matthew  7077:     #
                   7078:     # Deal with other parameters
                   7079:     while (my ($key,$value) = each(%Values)) {
                   7080:         $ValuesHash{$id.'.'.$key} = $value;
1.127     matthew  7081:     }
                   7082:     #
1.136     matthew  7083:     &Apache::lonnet::appenv(%ValuesHash);
                   7084:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   7085: }
                   7086: 
                   7087: ############################################################
                   7088: ############################################################
                   7089: 
                   7090: =pod
                   7091: 
1.138     matthew  7092: =item DrawXYYGraph
                   7093: 
                   7094: Facilitates the plotting of data in an XY graph with two Y axes.
                   7095: Puts plot definition data into the users environment in order for 
                   7096: graph.png to plot it.  Returns an <img> tag for the plot.
                   7097: 
                   7098: Inputs:
                   7099: 
                   7100: =over 4
                   7101: 
                   7102: =item $Title: string, the title of the plot
                   7103: 
                   7104: =item $xlabel: string, text describing the X-axis of the plot
                   7105: 
                   7106: =item $ylabel: string, text describing the Y-axis of the plot
                   7107: 
                   7108: =item $colors: Array ref containing the hex color codes for the data to be 
                   7109: plotted in.  If undefined, default values will be used.
                   7110: 
                   7111: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   7112: 
                   7113: =item $Ydata1: The first data set
                   7114: 
                   7115: =item $Min1: The minimum value of the left Y-axis
                   7116: 
                   7117: =item $Max1: The maximum value of the left Y-axis
                   7118: 
                   7119: =item $Ydata2: The second data set
                   7120: 
                   7121: =item $Min2: The minimum value of the right Y-axis
                   7122: 
                   7123: =item $Max2: The maximum value of the left Y-axis
                   7124: 
                   7125: =item %Values: hash indicating or overriding any default values which are 
                   7126: passed to graph.png.  
                   7127: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   7128: 
                   7129: =back
                   7130: 
                   7131: Returns:
                   7132: 
                   7133: An <img> tag which references graph.png and the appropriate identifying
                   7134: information for the plot.
1.136     matthew  7135: 
                   7136: =cut
                   7137: 
                   7138: ############################################################
                   7139: ############################################################
1.137     matthew  7140: sub DrawXYYGraph {
                   7141:     my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
                   7142:                                         $Ydata2,$Min2,$Max2,%Values)=@_;
1.136     matthew  7143:     #
                   7144:     # Create the identifier for the graph
                   7145:     my $identifier = &get_cgi_id();
                   7146:     my $id = 'cgi.'.$identifier;
                   7147:     #
                   7148:     $Title  = '' if (! defined($Title));
                   7149:     $xlabel = '' if (! defined($xlabel));
                   7150:     $ylabel = '' if (! defined($ylabel));
                   7151:     my %ValuesHash = 
                   7152:         (
1.369     www      7153:          $id.'.title'  => &escape($Title),
                   7154:          $id.'.xlabel' => &escape($xlabel),
                   7155:          $id.'.ylabel' => &escape($ylabel),
1.136     matthew  7156:          $id.'.labels' => join(',',@$Xlabels),
                   7157:          $id.'.PlotType' => 'XY',
                   7158:          $id.'.NumSets' => 2,
1.137     matthew  7159:          $id.'.two_axes' => 1,
                   7160:          $id.'.y1_max_value' => $Max1,
                   7161:          $id.'.y1_min_value' => $Min1,
                   7162:          $id.'.y2_max_value' => $Max2,
                   7163:          $id.'.y2_min_value' => $Min2,
1.136     matthew  7164:          );
                   7165:     #
1.137     matthew  7166:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   7167:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   7168:     }
                   7169:     #
                   7170:     if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
                   7171:         ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136     matthew  7172:         return '';
                   7173:     }
                   7174:     my $NumSets=1;
1.137     matthew  7175:     foreach my $array ($Ydata1,$Ydata2){
1.136     matthew  7176:         next if (! ref($array));
                   7177:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137     matthew  7178:     }
                   7179:     #
                   7180:     # Deal with other parameters
                   7181:     while (my ($key,$value) = each(%Values)) {
                   7182:         $ValuesHash{$id.'.'.$key} = $value;
1.136     matthew  7183:     }
                   7184:     #
                   7185:     &Apache::lonnet::appenv(%ValuesHash);
1.130     albertel 7186:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139     matthew  7187: }
                   7188: 
                   7189: ############################################################
                   7190: ############################################################
                   7191: 
                   7192: =pod
                   7193: 
1.157     matthew  7194: =back 
                   7195: 
1.139     matthew  7196: =head1 Statistics helper routines?  
                   7197: 
                   7198: Bad place for them but what the hell.
                   7199: 
1.157     matthew  7200: =over 4
                   7201: 
1.139     matthew  7202: =item &chartlink
                   7203: 
                   7204: Returns a link to the chart for a specific student.  
                   7205: 
                   7206: Inputs:
                   7207: 
                   7208: =over 4
                   7209: 
                   7210: =item $linktext: The text of the link
                   7211: 
                   7212: =item $sname: The students username
                   7213: 
                   7214: =item $sdomain: The students domain
                   7215: 
                   7216: =back
                   7217: 
1.157     matthew  7218: =back
                   7219: 
1.139     matthew  7220: =cut
                   7221: 
                   7222: ############################################################
                   7223: ############################################################
                   7224: sub chartlink {
                   7225:     my ($linktext, $sname, $sdomain) = @_;
                   7226:     my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369     www      7227:         '&amp;SelectedStudent='.&escape($sname.':'.$sdomain).
1.219     albertel 7228:         '&amp;chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139     matthew  7229:        '">'.$linktext.'</a>';
1.153     matthew  7230: }
                   7231: 
                   7232: #######################################################
                   7233: #######################################################
                   7234: 
                   7235: =pod
                   7236: 
                   7237: =head1 Course Environment Routines
1.157     matthew  7238: 
                   7239: =over 4
1.153     matthew  7240: 
                   7241: =item &restore_course_settings 
                   7242: 
                   7243: =item &store_course_settings
                   7244: 
                   7245: Restores/Store indicated form parameters from the course environment.
                   7246: Will not overwrite existing values of the form parameters.
                   7247: 
                   7248: Inputs: 
                   7249: a scalar describing the data (e.g. 'chart', 'problem_analysis')
                   7250: 
                   7251: a hash ref describing the data to be stored.  For example:
                   7252:    
                   7253: %Save_Parameters = ('Status' => 'scalar',
                   7254:     'chartoutputmode' => 'scalar',
                   7255:     'chartoutputdata' => 'scalar',
                   7256:     'Section' => 'array',
1.373     raeburn  7257:     'Group' => 'array',
1.153     matthew  7258:     'StudentData' => 'array',
                   7259:     'Maps' => 'array');
                   7260: 
                   7261: Returns: both routines return nothing
                   7262: 
                   7263: =cut
                   7264: 
                   7265: #######################################################
                   7266: #######################################################
                   7267: sub store_course_settings {
1.496     albertel 7268:     return &store_settings($env{'request.course.id'},@_);
                   7269: }
                   7270: 
                   7271: sub store_settings {
1.153     matthew  7272:     # save to the environment
                   7273:     # appenv the same items, just to be safe
1.300     albertel 7274:     my $udom  = $env{'user.domain'};
                   7275:     my $uname = $env{'user.name'};
1.496     albertel 7276:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  7277:     my %SaveHash;
                   7278:     my %AppHash;
                   7279:     while (my ($setting,$type) = each(%$Settings)) {
1.496     albertel 7280:         my $basename = join('.','internal',$context,$prefix,$setting);
1.300     albertel 7281:         my $envname = 'environment.'.$basename;
1.258     albertel 7282:         if (exists($env{'form.'.$setting})) {
1.153     matthew  7283:             # Save this value away
                   7284:             if ($type eq 'scalar' &&
1.258     albertel 7285:                 (! exists($env{$envname}) || 
                   7286:                  $env{$envname} ne $env{'form.'.$setting})) {
                   7287:                 $SaveHash{$basename} = $env{'form.'.$setting};
                   7288:                 $AppHash{$envname}   = $env{'form.'.$setting};
1.153     matthew  7289:             } elsif ($type eq 'array') {
                   7290:                 my $stored_form;
1.258     albertel 7291:                 if (ref($env{'form.'.$setting})) {
1.153     matthew  7292:                     $stored_form = join(',',
                   7293:                                         map {
1.369     www      7294:                                             &escape($_);
1.258     albertel 7295:                                         } sort(@{$env{'form.'.$setting}}));
1.153     matthew  7296:                 } else {
                   7297:                     $stored_form = 
1.369     www      7298:                         &escape($env{'form.'.$setting});
1.153     matthew  7299:                 }
                   7300:                 # Determine if the array contents are the same.
1.258     albertel 7301:                 if ($stored_form ne $env{$envname}) {
1.153     matthew  7302:                     $SaveHash{$basename} = $stored_form;
                   7303:                     $AppHash{$envname}   = $stored_form;
                   7304:                 }
                   7305:             }
                   7306:         }
                   7307:     }
                   7308:     my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300     albertel 7309:                                           $udom,$uname);
1.153     matthew  7310:     if ($put_result !~ /^(ok|delayed)/) {
                   7311:         &Apache::lonnet::logthis('unable to save form parameters, '.
                   7312:                                  'got error:'.$put_result);
                   7313:     }
                   7314:     # Make sure these settings stick around in this session, too
                   7315:     &Apache::lonnet::appenv(%AppHash);
                   7316:     return;
                   7317: }
                   7318: 
                   7319: sub restore_course_settings {
1.499     albertel 7320:     return &restore_settings($env{'request.course.id'},@_);
1.496     albertel 7321: }
                   7322: 
                   7323: sub restore_settings {
                   7324:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  7325:     while (my ($setting,$type) = each(%$Settings)) {
1.258     albertel 7326:         next if (exists($env{'form.'.$setting}));
1.496     albertel 7327:         my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153     matthew  7328:             '.'.$setting;
1.258     albertel 7329:         if (exists($env{$envname})) {
1.153     matthew  7330:             if ($type eq 'scalar') {
1.258     albertel 7331:                 $env{'form.'.$setting} = $env{$envname};
1.153     matthew  7332:             } elsif ($type eq 'array') {
1.258     albertel 7333:                 $env{'form.'.$setting} = [ 
1.153     matthew  7334:                                            map { 
1.369     www      7335:                                                &unescape($_); 
1.258     albertel 7336:                                            } split(',',$env{$envname})
1.153     matthew  7337:                                            ];
                   7338:             }
                   7339:         }
                   7340:     }
1.127     matthew  7341: }
                   7342: 
                   7343: ############################################################
                   7344: ############################################################
1.154     albertel 7345: 
1.443     albertel 7346: sub commit_customrole {
                   7347:     my ($udom,$uname,$url,$three,$four,$five,$start,$end) = @_;
                   7348:     my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.'@'.$three.' in '.$url.
                   7349:                          ($start?', '.&mt('starting').' '.localtime($start):'').
                   7350:                          ($end?', ending '.localtime($end):'').': <b>'.
                   7351:               &Apache::lonnet::assigncustomrole(
                   7352:                  $udom,$uname,$url,$three,$four,$five,$end,$start).
                   7353:                  '</b><br />';
                   7354:     return $output;
                   7355: }
                   7356: 
                   7357: sub commit_standardrole {
1.541     raeburn  7358:     my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
                   7359:     my ($output,$logmsg,$linefeed);
                   7360:     if ($context eq 'auto') {
                   7361:         $linefeed = "\n";
                   7362:     } else {
                   7363:         $linefeed = "<br />\n";
                   7364:     }  
1.443     albertel 7365:     if ($three eq 'st') {
1.541     raeburn  7366:         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
                   7367:                                          $one,$two,$sec,$context);
                   7368:         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
                   7369:             ($result eq 'unknown_course')) {
1.443     albertel 7370:             $output = "Error: $result\n"; 
                   7371:         } else {
1.541     raeburn  7372:             $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443     albertel 7373:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  7374:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
                   7375:             if ($context eq 'auto') {
                   7376:                 $output .= $result.$linefeed.&mt('Add to classlist').': ok';
                   7377:             } else {
                   7378:                $output .= '<b>'.$result.'</b>'.$linefeed.
                   7379:                &mt('Add to classlist').': <b>ok</b>';
                   7380:             }
                   7381:             $output .= $linefeed;
1.443     albertel 7382:         }
                   7383:     } else {
                   7384:         $output = &mt('Assigning').' '.$three.' in '.$url.
                   7385:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  7386:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
                   7387:         my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start);
                   7388:         if ($context eq 'auto') {
                   7389:             $output .= $result.$linefeed;
                   7390:         } else {
                   7391:             $output .= '<b>'.$result.'</b>'.$linefeed;
                   7392:         }
1.443     albertel 7393:     }
                   7394:     return $output;
                   7395: }
                   7396: 
                   7397: sub commit_studentrole {
1.541     raeburn  7398:     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
                   7399:     my ($result,$linefeed);
                   7400:     if ($context eq 'auto') {
                   7401:         $linefeed = "\n";
                   7402:     } else {
                   7403:         $linefeed = '<br />'."\n";
                   7404:     }
1.443     albertel 7405:     if (defined($one) && defined($two)) {
                   7406:         my $cid=$one.'_'.$two;
                   7407:         my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
                   7408:         my $secchange = 0;
                   7409:         my $expire_role_result;
                   7410:         my $modify_section_result;
                   7411:         unless ($oldsec eq '-1') {
                   7412:             unless ($sec eq $oldsec) {
                   7413:                 $secchange = 1;
                   7414:                 my $uurl='/'.$cid;
                   7415:                 $uurl=~s/\_/\//g;
                   7416:                 if ($oldsec) {
                   7417:                     $uurl.='/'.$oldsec;
                   7418:                 }
                   7419:                 $expire_role_result = &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',time);
                   7420:                 $result = $expire_role_result;
                   7421:             }
                   7422:         }
                   7423:         if (($expire_role_result eq 'ok') || ($secchange == 0)) {
                   7424:             $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid);
                   7425:             if ($modify_section_result =~ /^ok/) {
                   7426:                 if ($secchange == 1) {
                   7427:                     $$logmsg .= "Section for $uname switched from old section: $oldsec to new section: $sec".$linefeed;
                   7428:                 } elsif ($oldsec eq '-1') {
                   7429:                     $$logmsg .= "New student role for $uname in section $sec in course $cid".$linefeed;
                   7430:                 } else {
                   7431:                     $$logmsg .= "Student $uname assigned to unchanged section $sec in course $cid".$linefeed;
                   7432:                 }
                   7433:             } else {
                   7434:                 $$logmsg .= "Error when attempting section change for $uname from old section $oldsec to new section: $sec in course $cid -error: $modify_section_result".$linefeed;
                   7435:             }
                   7436:             $result = $modify_section_result;
                   7437:         } elsif ($secchange == 1) {
                   7438:             $$logmsg .= "Error when attempting to expire role for $uname in old section $oldsec in course $cid -error: $expire_role_result".$linefeed;
                   7439:         }
                   7440:     } else {
                   7441:         $$logmsg .= "Incomplete course id defined.  Addition of user $uname from domain $udom to course $one\_$two, section $sec not completed.$linefeed";
                   7442:         $result = "error: incomplete course id\n";
                   7443:     }
                   7444:     return $result;
                   7445: }
                   7446: 
                   7447: ############################################################
                   7448: ############################################################
                   7449: 
1.566     albertel 7450: sub check_clone {
1.578     raeburn  7451:     my ($args,$linefeed) = @_;
1.566     albertel 7452:     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
                   7453:     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
                   7454:     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
                   7455:     my $clonemsg;
                   7456:     my $can_clone = 0;
                   7457: 
                   7458:     if ($clonehome eq 'no_host') {
1.578     raeburn  7459:         $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 7460:     } else {
                   7461: 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.568     albertel 7462: 	if ($env{'request.role.domain'} eq $args->{'clonedomain'}) {
1.566     albertel 7463: 	    $can_clone = 1;
                   7464: 	} else {
                   7465: 	    my %clonehash = &Apache::lonnet::get('environment',['cloners'],
                   7466: 						 $args->{'clonedomain'},$args->{'clonecourse'});
                   7467: 	    my @cloners = split(/,/,$clonehash{'cloners'});
1.578     raeburn  7468:             if (grep(/^\*$/,@cloners)) {
                   7469:                 $can_clone = 1;
                   7470:             } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
                   7471:                 $can_clone = 1;
                   7472:             } else {
                   7473: 	        my %roleshash =
                   7474: 		    &Apache::lonnet::get_my_roles($args->{'ccuname'},
                   7475: 					 $args->{'ccdomain'},
                   7476:                                          'userroles',['active'],['cc'],
                   7477: 					 [$args->{'clonedomain'}]);
                   7478: 	        if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':cc'}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
                   7479: 		    $can_clone = 1;
                   7480: 	        } else {
                   7481:                     $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'});
                   7482: 	        }
1.566     albertel 7483: 	    }
1.578     raeburn  7484:         }
1.566     albertel 7485:     }
                   7486:     return ($can_clone, $clonemsg, $cloneid, $clonehome);
                   7487: }
                   7488: 
1.444     albertel 7489: sub construct_course {
1.541     raeburn  7490:     my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context) = @_;
1.444     albertel 7491:     my $outcome;
1.541     raeburn  7492:     my $linefeed =  '<br />'."\n";
                   7493:     if ($context eq 'auto') {
                   7494:         $linefeed = "\n";
                   7495:     }
1.566     albertel 7496: 
                   7497: #
                   7498: # Are we cloning?
                   7499: #
                   7500:     my ($can_clone, $clonemsg, $cloneid, $clonehome);
                   7501:     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578     raeburn  7502: 	($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566     albertel 7503: 	if ($context ne 'auto') {
1.578     raeburn  7504:             if ($clonemsg ne '') {
                   7505: 	        $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
                   7506:             }
1.566     albertel 7507: 	}
                   7508: 	$outcome .= $clonemsg.$linefeed;
                   7509: 
                   7510:         if (!$can_clone) {
                   7511: 	    return (0,$outcome);
                   7512: 	}
                   7513:     }
                   7514: 
1.444     albertel 7515: #
                   7516: # Open course
                   7517: #
                   7518:     my $crstype = lc($args->{'crstype'});
                   7519:     my %cenv=();
                   7520:     $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
                   7521:                                              $args->{'cdescr'},
                   7522:                                              $args->{'curl'},
                   7523:                                              $args->{'course_home'},
                   7524:                                              $args->{'nonstandard'},
                   7525:                                              $args->{'crscode'},
                   7526:                                              $args->{'ccuname'}.':'.
                   7527:                                              $args->{'ccdomain'},
                   7528:                                              $args->{'crstype'});
                   7529: 
                   7530:     # Note: The testing routines depend on this being output; see 
                   7531:     # Utils::Course. This needs to at least be output as a comment
                   7532:     # if anyone ever decides to not show this, and Utils::Course::new
                   7533:     # will need to be suitably modified.
1.541     raeburn  7534:     $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
1.444     albertel 7535: #
                   7536: # Check if created correctly
                   7537: #
1.479     albertel 7538:     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444     albertel 7539:     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.541     raeburn  7540:     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566     albertel 7541: 
1.444     albertel 7542: #
1.566     albertel 7543: # Do the cloning
                   7544: #   
                   7545:     if ($can_clone && $cloneid) {
                   7546: 	$clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
                   7547: 	if ($context ne 'auto') {
                   7548: 	    $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
                   7549: 	}
                   7550: 	$outcome .= $clonemsg.$linefeed;
                   7551: 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444     albertel 7552: # Copy all files
1.566     albertel 7553: 	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid);
1.444     albertel 7554: # Restore URL
1.566     albertel 7555: 	$cenv{'url'}=$oldcenv{'url'};
1.444     albertel 7556: # Restore title
1.566     albertel 7557: 	$cenv{'description'}=$oldcenv{'description'};
1.444     albertel 7558: # restore grading mode
1.566     albertel 7559: 	if (defined($oldcenv{'grading'})) {
                   7560: 	    $cenv{'grading'}=$oldcenv{'grading'};
                   7561: 	}
1.444     albertel 7562: # Mark as cloned
1.566     albertel 7563: 	$cenv{'clonedfrom'}=$cloneid;
                   7564: 	delete($cenv{'default_enrollment_start_date'});
                   7565: 	delete($cenv{'default_enrollment_end_date'});
1.444     albertel 7566:     }
1.566     albertel 7567: 
1.444     albertel 7568: #
                   7569: # Set environment (will override cloned, if existing)
                   7570: #
                   7571:     my @sections = ();
                   7572:     my @xlists = ();
                   7573:     if ($args->{'crstype'}) {
                   7574:         $cenv{'type'}=$args->{'crstype'};
                   7575:     }
                   7576:     if ($args->{'crsid'}) {
                   7577:         $cenv{'courseid'}=$args->{'crsid'};
                   7578:     }
                   7579:     if ($args->{'crscode'}) {
                   7580:         $cenv{'internal.coursecode'}=$args->{'crscode'};
                   7581:     }
                   7582:     if ($args->{'crsquota'} ne '') {
                   7583:         $cenv{'internal.coursequota'}=$args->{'crsquota'};
                   7584:     } else {
                   7585:         $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
                   7586:     }
                   7587:     if ($args->{'ccuname'}) {
                   7588:         $cenv{'internal.courseowner'} = $args->{'ccuname'}.
                   7589:                                         ':'.$args->{'ccdomain'};
                   7590:     } else {
                   7591:         $cenv{'internal.courseowner'} = $args->{'curruser'};
                   7592:     }
                   7593: 
                   7594:     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
                   7595:     if ($args->{'crssections'}) {
                   7596:         $cenv{'internal.sectionnums'} = '';
                   7597:         if ($args->{'crssections'} =~ m/,/) {
                   7598:             @sections = split/,/,$args->{'crssections'};
                   7599:         } else {
                   7600:             $sections[0] = $args->{'crssections'};
                   7601:         }
                   7602:         if (@sections > 0) {
                   7603:             foreach my $item (@sections) {
                   7604:                 my ($sec,$gp) = split/:/,$item;
                   7605:                 my $class = $args->{'crscode'}.$sec;
                   7606:                 my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
                   7607:                 $cenv{'internal.sectionnums'} .= $item.',';
                   7608:                 unless ($addcheck eq 'ok') {
                   7609:                     push @badclasses, $class;
                   7610:                 }
                   7611:             }
                   7612:             $cenv{'internal.sectionnums'} =~ s/,$//;
                   7613:         }
                   7614:     }
                   7615: # do not hide course coordinator from staff listing, 
                   7616: # even if privileged
                   7617:     $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   7618: # add crosslistings
                   7619:     if ($args->{'crsxlist'}) {
                   7620:         $cenv{'internal.crosslistings'}='';
                   7621:         if ($args->{'crsxlist'} =~ m/,/) {
                   7622:             @xlists = split/,/,$args->{'crsxlist'};
                   7623:         } else {
                   7624:             $xlists[0] = $args->{'crsxlist'};
                   7625:         }
                   7626:         if (@xlists > 0) {
                   7627:             foreach my $item (@xlists) {
                   7628:                 my ($xl,$gp) = split/:/,$item;
                   7629:                 my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
                   7630:                 $cenv{'internal.crosslistings'} .= $item.',';
                   7631:                 unless ($addcheck eq 'ok') {
                   7632:                     push @badclasses, $xl;
                   7633:                 }
                   7634:             }
                   7635:             $cenv{'internal.crosslistings'} =~ s/,$//;
                   7636:         }
                   7637:     }
                   7638:     if ($args->{'autoadds'}) {
                   7639:         $cenv{'internal.autoadds'}=$args->{'autoadds'};
                   7640:     }
                   7641:     if ($args->{'autodrops'}) {
                   7642:         $cenv{'internal.autodrops'}=$args->{'autodrops'};
                   7643:     }
                   7644: # check for notification of enrollment changes
                   7645:     my @notified = ();
                   7646:     if ($args->{'notify_owner'}) {
                   7647:         if ($args->{'ccuname'} ne '') {
                   7648:             push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
                   7649:         }
                   7650:     }
                   7651:     if ($args->{'notify_dc'}) {
                   7652:         if ($uname ne '') { 
                   7653:             push(@notified,$uname.'@'.$udom);
                   7654:         }
                   7655:     }
                   7656:     if (@notified > 0) {
                   7657:         my $notifylist;
                   7658:         if (@notified > 1) {
                   7659:             $notifylist = join(',',@notified);
                   7660:         } else {
                   7661:             $notifylist = $notified[0];
                   7662:         }
                   7663:         $cenv{'internal.notifylist'} = $notifylist;
                   7664:     }
                   7665:     if (@badclasses > 0) {
                   7666:         my %lt=&Apache::lonlocal::texthash(
                   7667:                 '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',
                   7668:                 'dnhr' => 'does not have rights to access enrollment in these classes',
                   7669:                 'adby' => 'as determined by the policies of your institution on access to official classlists'
                   7670:         );
1.541     raeburn  7671:         my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
                   7672:                            ' ('.$lt{'adby'}.')';
                   7673:         if ($context eq 'auto') {
                   7674:             $outcome .= $badclass_msg.$linefeed;
1.566     albertel 7675:             $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.541     raeburn  7676:             foreach my $item (@badclasses) {
                   7677:                 if ($context eq 'auto') {
                   7678:                     $outcome .= " - $item\n";
                   7679:                 } else {
                   7680:                     $outcome .= "<li>$item</li>\n";
                   7681:                 }
                   7682:             }
                   7683:             if ($context eq 'auto') {
                   7684:                 $outcome .= $linefeed;
                   7685:             } else {
1.566     albertel 7686:                 $outcome .= "</ul><br /><br /></div>\n";
1.541     raeburn  7687:             }
                   7688:         } 
1.444     albertel 7689:     }
                   7690:     if ($args->{'no_end_date'}) {
                   7691:         $args->{'endaccess'} = 0;
                   7692:     }
                   7693:     $cenv{'internal.autostart'}=$args->{'enrollstart'};
                   7694:     $cenv{'internal.autoend'}=$args->{'enrollend'};
                   7695:     $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
                   7696:     $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
                   7697:     if ($args->{'showphotos'}) {
                   7698:       $cenv{'internal.showphotos'}=$args->{'showphotos'};
                   7699:     }
                   7700:     $cenv{'internal.authtype'} = $args->{'authtype'};
                   7701:     $cenv{'internal.autharg'} = $args->{'autharg'}; 
                   7702:     if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
                   7703:         if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'}  eq '') {
1.541     raeburn  7704:             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'); 
                   7705:             if ($context eq 'auto') {
                   7706:                 $outcome .= $krb_msg;
                   7707:             } else {
1.566     albertel 7708:                 $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541     raeburn  7709:             }
                   7710:             $outcome .= $linefeed;
1.444     albertel 7711:         }
                   7712:     }
                   7713:     if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
                   7714:        if ($args->{'setpolicy'}) {
                   7715:            $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   7716:        }
                   7717:        if ($args->{'setcontent'}) {
                   7718:            $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   7719:        }
                   7720:     }
                   7721:     if ($args->{'reshome'}) {
                   7722: 	$cenv{'reshome'}=$args->{'reshome'}.'/';
                   7723: 	$cenv{'reshome'}=~s/\/+$/\//;
                   7724:     }
                   7725: #
                   7726: # course has keyed access
                   7727: #
                   7728:     if ($args->{'setkeys'}) {
                   7729:        $cenv{'keyaccess'}='yes';
                   7730:     }
                   7731: # if specified, key authority is not course, but user
                   7732: # only active if keyaccess is yes
                   7733:     if ($args->{'keyauth'}) {
1.487     albertel 7734: 	my ($user,$domain) = split(':',$args->{'keyauth'});
                   7735: 	$user = &LONCAPA::clean_username($user);
                   7736: 	$domain = &LONCAPA::clean_username($domain);
1.488     foxr     7737: 	if ($user ne '' && $domain ne '') {
1.487     albertel 7738: 	    $cenv{'keyauth'}=$user.':'.$domain;
1.444     albertel 7739: 	}
                   7740:     }
                   7741: 
                   7742:     if ($args->{'disresdis'}) {
                   7743:         $cenv{'pch.roles.denied'}='st';
                   7744:     }
                   7745:     if ($args->{'disablechat'}) {
                   7746:         $cenv{'plc.roles.denied'}='st';
                   7747:     }
                   7748: 
                   7749:     # Record we've not yet viewed the Course Initialization Helper for this 
                   7750:     # course
                   7751:     $cenv{'course.helper.not.run'} = 1;
                   7752:     #
                   7753:     # Use new Randomseed
                   7754:     #
                   7755:     $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
                   7756:     $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
                   7757:     #
                   7758:     # The encryption code and receipt prefix for this course
                   7759:     #
                   7760:     $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
                   7761:     $cenv{'internal.encpref'}=100+int(9*rand(99));
                   7762:     #
                   7763:     # By default, use standard grading
                   7764:     if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
                   7765: 
1.541     raeburn  7766:     $outcome .= $linefeed.&mt('Setting environment').': '.                 
                   7767:           &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 7768: #
                   7769: # Open all assignments
                   7770: #
                   7771:     if ($args->{'openall'}) {
                   7772:        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
                   7773:        my %storecontent = ($storeunder         => time,
                   7774:                            $storeunder.'.type' => 'date_start');
                   7775:        
                   7776:        $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541     raeburn  7777:                  ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 7778:    }
                   7779: #
                   7780: # Set first page
                   7781: #
                   7782:     unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
                   7783: 	    || ($cloneid)) {
1.445     albertel 7784: 	use LONCAPA::map;
1.444     albertel 7785: 	$outcome .= &mt('Setting first resource').': ';
1.445     albertel 7786: 
                   7787: 	my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
                   7788:         my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
                   7789: 
1.444     albertel 7790:         $outcome .= ($fatal?$errtext:'read ok').' - ';
                   7791:         my $title; my $url;
                   7792:         if ($args->{'firstres'} eq 'syl') {
                   7793: 	    $title='Syllabus';
                   7794:             $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
                   7795:         } else {
                   7796:             $title='Navigate Contents';
                   7797:             $url='/adm/navmaps';
                   7798:         }
1.445     albertel 7799: 
                   7800:         $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
                   7801: 	(my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
                   7802: 
                   7803: 	if ($errtext) { $fatal=2; }
1.541     raeburn  7804:         $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444     albertel 7805:     }
1.566     albertel 7806: 
                   7807:     return (1,$outcome);
1.444     albertel 7808: }
                   7809: 
                   7810: ############################################################
                   7811: ############################################################
                   7812: 
1.378     raeburn  7813: sub course_type {
                   7814:     my ($cid) = @_;
                   7815:     if (!defined($cid)) {
                   7816:         $cid = $env{'request.course.id'};
                   7817:     }
1.404     albertel 7818:     if (defined($env{'course.'.$cid.'.type'})) {
                   7819:         return $env{'course.'.$cid.'.type'};
1.378     raeburn  7820:     } else {
                   7821:         return 'Course';
1.377     raeburn  7822:     }
                   7823: }
1.156     albertel 7824: 
1.406     raeburn  7825: sub group_term {
                   7826:     my $crstype = &course_type();
                   7827:     my %names = (
                   7828:                   'Course' => 'group',
                   7829:                   'Group' => 'team',
                   7830:                 );
                   7831:     return $names{$crstype};
                   7832: }
                   7833: 
1.156     albertel 7834: sub icon {
                   7835:     my ($file)=@_;
1.505     albertel 7836:     my $curfext = lc((split(/\./,$file))[-1]);
1.168     albertel 7837:     my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156     albertel 7838:     my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168     albertel 7839:     if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
                   7840: 	if (-e  $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                   7841: 	          $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   7842: 	            $curfext.".gif") {
                   7843: 	    $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   7844: 		$curfext.".gif";
                   7845: 	}
                   7846:     }
1.249     albertel 7847:     return &lonhttpdurl($iconname);
1.154     albertel 7848: } 
1.84      albertel 7849: 
1.575     albertel 7850: sub lonhttpd_port {
1.215     albertel 7851:     my $lonhttpd_port=$Apache::lonnet::perlvar{'lonhttpdPort'};
                   7852:     if (!defined($lonhttpd_port)) { $lonhttpd_port='8080'; }
1.574     albertel 7853:     # IE doesn't like a secure page getting images from a non-secure
                   7854:     # port (when logging we haven't parsed the browser type so default
                   7855:     # back to secure
                   7856:     if ((!exists($env{'browser.type'}) || $env{'browser.type'} eq 'explorer')
                   7857: 	&& $ENV{'SERVER_PORT'} == 443) {
1.575     albertel 7858: 	return 443;
                   7859:     }
                   7860:     return $lonhttpd_port;
                   7861: 
                   7862: }
                   7863: 
                   7864: sub lonhttpdurl {
                   7865:     my ($url)=@_;
                   7866: 
                   7867:     my $lonhttpd_port = &lonhttpd_port();
                   7868:     if ($lonhttpd_port == 443) {
1.574     albertel 7869: 	return 'https://'.$ENV{'SERVER_NAME'}.$url;
                   7870:     }
1.215     albertel 7871:     return 'http://'.$ENV{'SERVER_NAME'}.':'.$lonhttpd_port.$url;
                   7872: }
                   7873: 
1.213     albertel 7874: sub connection_aborted {
                   7875:     my ($r)=@_;
                   7876:     $r->print(" ");$r->rflush();
                   7877:     my $c = $r->connection;
                   7878:     return $c->aborted();
                   7879: }
                   7880: 
1.221     foxr     7881: #    Escapes strings that may have embedded 's that will be put into
1.222     foxr     7882: #    strings as 'strings'.
                   7883: sub escape_single {
1.221     foxr     7884:     my ($input) = @_;
1.223     albertel 7885:     $input =~ s/\\/\\\\/g;	# Escape the \'s..(must be first)>
1.221     foxr     7886:     $input =~ s/\'/\\\'/g;	# Esacpe the 's....
                   7887:     return $input;
                   7888: }
1.223     albertel 7889: 
1.222     foxr     7890: #  Same as escape_single, but escape's "'s  This 
                   7891: #  can be used for  "strings"
                   7892: sub escape_double {
                   7893:     my ($input) = @_;
                   7894:     $input =~ s/\\/\\\\/g;	# Escape the /'s..(must be first)>
                   7895:     $input =~ s/\"/\\\"/g;	# Esacpe the "s....
                   7896:     return $input;
                   7897: }
1.223     albertel 7898:  
1.222     foxr     7899: #   Escapes the last element of a full URL.
                   7900: sub escape_url {
                   7901:     my ($url)   = @_;
1.238     raeburn  7902:     my @urlslices = split(/\//, $url,-1);
1.369     www      7903:     my $lastitem = &escape(pop(@urlslices));
1.223     albertel 7904:     return join('/',@urlslices).'/'.$lastitem;
1.222     foxr     7905: }
1.462     albertel 7906: 
                   7907: # -------------------------------------------------------- Initliaze user login
                   7908: sub init_user_environment {
1.463     albertel 7909:     my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462     albertel 7910:     my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
                   7911: 
                   7912:     my $public=($username eq 'public' && $domain eq 'public');
                   7913: 
                   7914: # See if old ID present, if so, remove
                   7915: 
                   7916:     my ($filename,$cookie,$userroles);
                   7917:     my $now=time;
                   7918: 
                   7919:     if ($public) {
                   7920: 	my $max_public=100;
                   7921: 	my $oldest;
                   7922: 	my $oldest_time=0;
                   7923: 	for(my $next=1;$next<=$max_public;$next++) {
                   7924: 	    if (-e $lonids."/publicuser_$next.id") {
                   7925: 		my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
                   7926: 		if ($mtime<$oldest_time || !$oldest_time) {
                   7927: 		    $oldest_time=$mtime;
                   7928: 		    $oldest=$next;
                   7929: 		}
                   7930: 	    } else {
                   7931: 		$cookie="publicuser_$next";
                   7932: 		last;
                   7933: 	    }
                   7934: 	}
                   7935: 	if (!$cookie) { $cookie="publicuser_$oldest"; }
                   7936:     } else {
1.463     albertel 7937: 	# if this isn't a robot, kill any existing non-robot sessions
                   7938: 	if (!$args->{'robot'}) {
                   7939: 	    opendir(DIR,$lonids);
                   7940: 	    while ($filename=readdir(DIR)) {
                   7941: 		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
                   7942: 		    unlink($lonids.'/'.$filename);
                   7943: 		}
1.462     albertel 7944: 	    }
1.463     albertel 7945: 	    closedir(DIR);
1.462     albertel 7946: 	}
                   7947: # Give them a new cookie
1.463     albertel 7948: 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
                   7949: 		                   : $now);
                   7950: 	$cookie="$username\_$id\_$domain\_$authhost";
1.462     albertel 7951:     
                   7952: # Initialize roles
                   7953: 
                   7954: 	$userroles=&Apache::lonnet::rolesinit($domain,$username,$authhost);
                   7955:     }
                   7956: # ------------------------------------ Check browser type and MathML capability
                   7957: 
                   7958:     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
                   7959:         $clientunicode,$clientos) = &decode_user_agent($r);
                   7960: 
                   7961: # -------------------------------------- Any accessibility options to remember?
                   7962:     if (($form->{'interface'}) && ($form->{'remember'} eq 'true')) {
                   7963: 	foreach my $option ('imagesuppress','appletsuppress',
                   7964: 			    'embedsuppress','fontenhance','blackwhite') {
                   7965: 	    if ($form->{$option} eq 'true') {
                   7966: 		&Apache::lonnet::put('environment',{$option => 'on'},
                   7967: 				     $domain,$username);
                   7968: 	    } else {
                   7969: 		&Apache::lonnet::del('environment',[$option],
                   7970: 				     $domain,$username);
                   7971: 	    }
                   7972: 	}
                   7973:     }
                   7974: # ------------------------------------------------------------- Get environment
                   7975: 
                   7976:     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
                   7977:     my ($tmp) = keys(%userenv);
                   7978:     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   7979: 	# default remote control to off
                   7980: 	if ($userenv{'remote'} ne 'on') { $userenv{'remote'} = 'off'; }
                   7981:     } else {
                   7982: 	undef(%userenv);
                   7983:     }
                   7984:     if (($userenv{'interface'}) && (!$form->{'interface'})) {
                   7985: 	$form->{'interface'}=$userenv{'interface'};
                   7986:     }
                   7987:     $env{'environment.remote'}=$userenv{'remote'};
                   7988:     if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
                   7989: 
                   7990: # --------------- Do not trust query string to be put directly into environment
                   7991:     foreach my $option ('imagesuppress','appletsuppress',
                   7992: 			'embedsuppress','fontenhance','blackwhite',
                   7993: 			'interface','localpath','localres') {
                   7994: 	$form->{$option}=~s/[\n\r\=]//gs;
                   7995:     }
                   7996: # --------------------------------------------------------- Write first profile
                   7997: 
                   7998:     {
                   7999: 	my %initial_env = 
                   8000: 	    ("user.name"          => $username,
                   8001: 	     "user.domain"        => $domain,
                   8002: 	     "user.home"          => $authhost,
                   8003: 	     "browser.type"       => $clientbrowser,
                   8004: 	     "browser.version"    => $clientversion,
                   8005: 	     "browser.mathml"     => $clientmathml,
                   8006: 	     "browser.unicode"    => $clientunicode,
                   8007: 	     "browser.os"         => $clientos,
                   8008: 	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
                   8009: 	     "request.course.fn"  => '',
                   8010: 	     "request.course.uri" => '',
                   8011: 	     "request.course.sec" => '',
                   8012: 	     "request.role"       => 'cm',
                   8013: 	     "request.role.adv"   => $env{'user.adv'},
                   8014: 	     "request.host"       => $ENV{'REMOTE_ADDR'},);
                   8015: 
                   8016:         if ($form->{'localpath'}) {
                   8017: 	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
                   8018: 	    $initial_env{"browser.localres"}   = $form->{'localres'};
                   8019:         }
                   8020: 	
                   8021: 	if ($public) {
                   8022: 	    $initial_env{"environment.remote"} = "off";
                   8023: 	}
                   8024: 	if ($form->{'interface'}) {
                   8025: 	    $form->{'interface'}=~s/\W//gs;
                   8026: 	    $initial_env{"browser.interface"} = $form->{'interface'};
                   8027: 	    $env{'browser.interface'}=$form->{'interface'};
                   8028: 	    foreach my $option ('imagesuppress','appletsuppress',
                   8029: 				'embedsuppress','fontenhance','blackwhite') {
                   8030: 		if (($form->{$option} eq 'true') ||
                   8031: 		    ($userenv{$option} eq 'on')) {
                   8032: 		    $initial_env{"browser.$option"} = "on";
                   8033: 		}
                   8034: 	    }
                   8035: 	}
                   8036: 
                   8037: 	$env{'user.environment'} = "$lonids/$cookie.id";
                   8038: 	
                   8039: 	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
                   8040: 		 &GDBM_WRCREAT(),0640)) {
                   8041: 	    &_add_to_env(\%disk_env,\%initial_env);
                   8042: 	    &_add_to_env(\%disk_env,\%userenv,'environment.');
                   8043: 	    &_add_to_env(\%disk_env,$userroles);
1.463     albertel 8044: 	    if (ref($args->{'extra_env'})) {
                   8045: 		&_add_to_env(\%disk_env,$args->{'extra_env'});
                   8046: 	    }
1.462     albertel 8047: 	    untie(%disk_env);
                   8048: 	} else {
                   8049: 	    &Apache::lonnet::logthis("<font color=\"blue\">WARNING: ".
                   8050: 			   'Could not create environment storage in lonauth: '.$!.'</font>');
                   8051: 	    return 'error: '.$!;
                   8052: 	}
                   8053:     }
                   8054:     $env{'request.role'}='cm';
                   8055:     $env{'request.role.adv'}=$env{'user.adv'};
                   8056:     $env{'browser.type'}=$clientbrowser;
                   8057: 
                   8058:     return $cookie;
                   8059: 
                   8060: }
                   8061: 
                   8062: sub _add_to_env {
                   8063:     my ($idf,$env_data,$prefix) = @_;
                   8064:     while (my ($key,$value) = each(%$env_data)) {
                   8065: 	$idf->{$prefix.$key} = $value;
                   8066: 	$env{$prefix.$key}   = $value;
                   8067:     }
                   8068: }
                   8069: 
                   8070: 
1.41      ng       8071: =pod
                   8072: 
                   8073: =back
                   8074: 
1.112     bowersj2 8075: =cut
1.41      ng       8076: 
1.112     bowersj2 8077: 1;
                   8078: __END__;
1.41      ng       8079: 

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