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

1.10      albertel    1: # The LearningOnline Network with CAPA
1.1       albertel    2: # a pile of common routines
1.10      albertel    3: #
1.1237  ! raeburn     4: # $Id: loncommon.pm,v 1.1236 2016/03/04 21:43:15 raeburn Exp $
1.10      albertel    5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
1.1       albertel   28: 
                     29: # Makes a table out of the previous attempts
1.2       albertel   30: # Inputs result_from_symbread, user, domain, course_id
1.16      harris41   31: # Reads in non-network-related .tab files
1.1       albertel   32: 
1.35      matthew    33: # POD header:
                     34: 
1.45      matthew    35: =pod
                     36: 
1.35      matthew    37: =head1 NAME
                     38: 
                     39: Apache::loncommon - pile of common routines
                     40: 
                     41: =head1 SYNOPSIS
                     42: 
1.112     bowersj2   43: Common routines for manipulating connections, student answers,
                     44:     domains, common Javascript fragments, etc.
1.35      matthew    45: 
1.112     bowersj2   46: =head1 OVERVIEW
1.35      matthew    47: 
1.112     bowersj2   48: A collection of commonly used subroutines that don't have a natural
                     49: home anywhere else. This collection helps remove
1.35      matthew    50: redundancy from other modules and increase efficiency of memory usage.
                     51: 
                     52: =cut 
                     53: 
                     54: # End of POD header
1.1       albertel   55: package Apache::loncommon;
                     56: 
                     57: use strict;
1.258     albertel   58: use Apache::lonnet;
1.46      matthew    59: use GDBM_File;
1.51      www        60: use POSIX qw(strftime mktime);
1.82      www        61: use Apache::lonmenu();
1.498     albertel   62: use Apache::lonenc();
1.117     www        63: use Apache::lonlocal;
1.685     tempelho   64: use Apache::lonnet();
1.139     matthew    65: use HTML::Entities;
1.334     albertel   66: use Apache::lonhtmlcommon();
                     67: use Apache::loncoursedata();
1.344     albertel   68: use Apache::lontexconvert();
1.444     albertel   69: use Apache::lonclonecourse();
1.1108    raeburn    70: use Apache::lonuserutils();
1.1110    raeburn    71: use Apache::lonuserstate();
1.1182    raeburn    72: use Apache::courseclassifier();
1.479     albertel   73: use LONCAPA qw(:DEFAULT :match);
1.657     raeburn    74: use DateTime::TimeZone;
1.687     raeburn    75: use DateTime::Locale::Catalog;
1.1220    raeburn    76: use Encode();
1.1091    foxr       77: use Text::Aspell;
1.1094    raeburn    78: use Authen::Captcha;
                     79: use Captcha::reCAPTCHA;
1.1234    raeburn    80: use JSON::DWIW;
                     81: use LWP::UserAgent;
1.1174    raeburn    82: use Crypt::DES;
                     83: use DynaLoader; # for Crypt::DES version
1.1223    musolffc   84: use MIME::Lite;
                     85: use MIME::Types;
1.117     www        86: 
1.517     raeburn    87: # ---------------------------------------------- Designs
                     88: use vars qw(%defaultdesign);
                     89: 
1.22      www        90: my $readit;
                     91: 
1.517     raeburn    92: 
1.157     matthew    93: ##
                     94: ## Global Variables
                     95: ##
1.46      matthew    96: 
1.643     foxr       97: 
                     98: # ----------------------------------------------- SSI with retries:
                     99: #
                    100: 
                    101: =pod
                    102: 
1.648     raeburn   103: =head1 Server Side include with retries:
1.643     foxr      104: 
                    105: =over 4
                    106: 
1.648     raeburn   107: =item * &ssi_with_retries(resource,retries form)
1.643     foxr      108: 
                    109: Performs an ssi with some number of retries.  Retries continue either
                    110: until the result is ok or until the retry count supplied by the
                    111: caller is exhausted.  
                    112: 
                    113: Inputs:
1.648     raeburn   114: 
                    115: =over 4
                    116: 
1.643     foxr      117: resource   - Identifies the resource to insert.
1.648     raeburn   118: 
1.643     foxr      119: retries    - Count of the number of retries allowed.
1.648     raeburn   120: 
1.643     foxr      121: form       - Hash that identifies the rendering options.
                    122: 
1.648     raeburn   123: =back
                    124: 
                    125: Returns:
                    126: 
                    127: =over 4
                    128: 
1.643     foxr      129: content    - The content of the response.  If retries were exhausted this is empty.
1.648     raeburn   130: 
1.643     foxr      131: response   - The response from the last attempt (which may or may not have been successful.
                    132: 
1.648     raeburn   133: =back
                    134: 
                    135: =back
                    136: 
1.643     foxr      137: =cut
                    138: 
                    139: sub ssi_with_retries {
                    140:     my ($resource, $retries, %form) = @_;
                    141: 
                    142: 
                    143:     my $ok = 0;			# True if we got a good response.
                    144:     my $content;
                    145:     my $response;
                    146: 
                    147:     # Try to get the ssi done. within the retries count:
                    148: 
                    149:     do {
                    150: 	($content, $response) = &Apache::lonnet::ssi($resource, %form);
                    151: 	$ok      = $response->is_success;
1.650     www       152:         if (!$ok) {
                    153:             &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
                    154:         }
1.643     foxr      155: 	$retries--;
                    156:     } while (!$ok && ($retries > 0));
                    157: 
                    158:     if (!$ok) {
                    159: 	$content = '';		# On error return an empty content.
                    160:     }
                    161:     return ($content, $response);
                    162: 
                    163: }
                    164: 
                    165: 
                    166: 
1.20      www       167: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12      harris41  168: my %language;
1.124     www       169: my %supported_language;
1.1088    foxr      170: my %supported_codes;
1.1048    foxr      171: my %latex_language;		# For choosing hyphenation in <transl..>
                    172: my %latex_language_bykey;	# for choosing hyphenation from metadata
1.12      harris41  173: my %cprtag;
1.192     taceyjo1  174: my %scprtag;
1.351     www       175: my %fe; my %fd; my %fm;
1.41      ng        176: my %category_extensions;
1.12      harris41  177: 
1.46      matthew   178: # ---------------------------------------------- Thesaurus variables
1.144     matthew   179: #
                    180: # %Keywords:
                    181: #      A hash used by &keyword to determine if a word is considered a keyword.
                    182: # $thesaurus_db_file 
                    183: #      Scalar containing the full path to the thesaurus database.
1.46      matthew   184: 
                    185: my %Keywords;
                    186: my $thesaurus_db_file;
                    187: 
1.144     matthew   188: #
                    189: # Initialize values from language.tab, copyright.tab, filetypes.tab,
                    190: # thesaurus.tab, and filecategories.tab.
                    191: #
1.18      www       192: BEGIN {
1.46      matthew   193:     # Variable initialization
                    194:     $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
                    195:     #
1.22      www       196:     unless ($readit) {
1.12      harris41  197: # ------------------------------------------------------------------- languages
                    198:     {
1.158     raeburn   199:         my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    200:                                    '/language.tab';
                    201:         if ( open(my $fh,"<$langtabfile") ) {
1.356     albertel  202:             while (my $line = <$fh>) {
                    203:                 next if ($line=~/^\#/);
                    204:                 chomp($line);
1.1088    foxr      205:                 my ($key,$code,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
1.158     raeburn   206:                 $language{$key}=$val.' - '.$enc;
                    207:                 if ($sup) {
                    208:                     $supported_language{$key}=$sup;
1.1088    foxr      209: 		    $supported_codes{$key}   = $code;
1.158     raeburn   210:                 }
1.1048    foxr      211: 		if ($latex) {
                    212: 		    $latex_language_bykey{$key} = $latex;
1.1088    foxr      213: 		    $latex_language{$code} = $latex;
1.1048    foxr      214: 		}
1.158     raeburn   215:             }
                    216:             close($fh);
                    217:         }
1.12      harris41  218:     }
                    219: # ------------------------------------------------------------------ copyrights
                    220:     {
1.158     raeburn   221:         my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    222:                                   '/copyright.tab';
                    223:         if ( open (my $fh,"<$copyrightfile") ) {
1.356     albertel  224:             while (my $line = <$fh>) {
                    225:                 next if ($line=~/^\#/);
                    226:                 chomp($line);
                    227:                 my ($key,$val)=(split(/\s+/,$line,2));
1.158     raeburn   228:                 $cprtag{$key}=$val;
                    229:             }
                    230:             close($fh);
                    231:         }
1.12      harris41  232:     }
1.351     www       233: # ----------------------------------------------------------- source copyrights
1.192     taceyjo1  234:     {
                    235:         my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    236:                                   '/source_copyright.tab';
                    237:         if ( open (my $fh,"<$sourcecopyrightfile") ) {
1.356     albertel  238:             while (my $line = <$fh>) {
                    239:                 next if ($line =~ /^\#/);
                    240:                 chomp($line);
                    241:                 my ($key,$val)=(split(/\s+/,$line,2));
1.192     taceyjo1  242:                 $scprtag{$key}=$val;
                    243:             }
                    244:             close($fh);
                    245:         }
                    246:     }
1.63      www       247: 
1.517     raeburn   248: # -------------------------------------------------------------- default domain designs
1.63      www       249:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517     raeburn   250:     my $designfile = $designdir.'/default.tab';
                    251:     if ( open (my $fh,"<$designfile") ) {
                    252:         while (my $line = <$fh>) {
                    253:             next if ($line =~ /^\#/);
                    254:             chomp($line);
                    255:             my ($key,$val)=(split(/\=/,$line));
                    256:             if ($val) { $defaultdesign{$key}=$val; }
                    257:         }
                    258:         close($fh);
1.63      www       259:     }
                    260: 
1.15      harris41  261: # ------------------------------------------------------------- file categories
                    262:     {
1.158     raeburn   263:         my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    264:                                   '/filecategories.tab';
                    265:         if ( open (my $fh,"<$categoryfile") ) {
1.356     albertel  266: 	    while (my $line = <$fh>) {
                    267: 		next if ($line =~ /^\#/);
                    268: 		chomp($line);
                    269:                 my ($extension,$category)=(split(/\s+/,$line,2));
1.158     raeburn   270:                 push @{$category_extensions{lc($category)}},$extension;
                    271:             }
                    272:             close($fh);
                    273:         }
                    274: 
1.15      harris41  275:     }
1.12      harris41  276: # ------------------------------------------------------------------ file types
                    277:     {
1.158     raeburn   278:         my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    279:                '/filetypes.tab';
                    280:         if ( open (my $fh,"<$typesfile") ) {
1.356     albertel  281:             while (my $line = <$fh>) {
                    282: 		next if ($line =~ /^\#/);
                    283: 		chomp($line);
                    284:                 my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158     raeburn   285:                 if ($descr ne '') {
                    286:                     $fe{$ending}=lc($emb);
                    287:                     $fd{$ending}=$descr;
1.351     www       288:                     if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158     raeburn   289:                 }
                    290:             }
                    291:             close($fh);
                    292:         }
1.12      harris41  293:     }
1.22      www       294:     &Apache::lonnet::logthis(
1.705     tempelho  295:              "<span style='color:yellow;'>INFO: Read file types</span>");
1.22      www       296:     $readit=1;
1.46      matthew   297:     }  # end of unless($readit) 
1.32      matthew   298:     
                    299: }
1.112     bowersj2  300: 
1.42      matthew   301: ###############################################################
                    302: ##           HTML and Javascript Helper Functions            ##
                    303: ###############################################################
                    304: 
                    305: =pod 
                    306: 
1.112     bowersj2  307: =head1 HTML and Javascript Functions
1.42      matthew   308: 
1.112     bowersj2  309: =over 4
                    310: 
1.648     raeburn   311: =item * &browser_and_searcher_javascript()
1.112     bowersj2  312: 
                    313: X<browsing, javascript>X<searching, javascript>Returns a string
                    314: containing javascript with two functions, C<openbrowser> and
                    315: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
                    316: tags.
1.42      matthew   317: 
1.648     raeburn   318: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42      matthew   319: 
                    320: inputs: formname, elementname, only, omit
                    321: 
                    322: formname and elementname indicate the name of the html form and name of
                    323: the element that the results of the browsing selection are to be placed in. 
                    324: 
                    325: Specifying 'only' will restrict the browser to displaying only files
1.185     www       326: with the given extension.  Can be a comma separated list.
1.42      matthew   327: 
                    328: Specifying 'omit' will restrict the browser to NOT displaying files
1.185     www       329: with the given extension.  Can be a comma separated list.
1.42      matthew   330: 
1.648     raeburn   331: =item * &opensearcher(formname,elementname) [javascript]
1.42      matthew   332: 
                    333: Inputs: formname, elementname
                    334: 
                    335: formname and elementname specify the name of the html form and the name
                    336: of the element the selection from the search results will be placed in.
1.542     raeburn   337: 
1.42      matthew   338: =cut
                    339: 
                    340: sub browser_and_searcher_javascript {
1.199     albertel  341:     my ($mode)=@_;
                    342:     if (!defined($mode)) { $mode='edit'; }
1.453     albertel  343:     my $resurl=&escape_single(&lastresurl());
1.42      matthew   344:     return <<END;
1.219     albertel  345: // <!-- BEGIN LON-CAPA Internal
1.50      matthew   346:     var editbrowser = null;
1.135     albertel  347:     function openbrowser(formname,elementname,only,omit,titleelement) {
1.170     www       348:         var url = '$resurl/?';
1.42      matthew   349:         if (editbrowser == null) {
                    350:             url += 'launch=1&';
                    351:         }
                    352:         url += 'catalogmode=interactive&';
1.199     albertel  353:         url += 'mode=$mode&';
1.611     albertel  354:         url += 'inhibitmenu=yes&';
1.42      matthew   355:         url += 'form=' + formname + '&';
                    356:         if (only != null) {
                    357:             url += 'only=' + only + '&';
1.217     albertel  358:         } else {
                    359:             url += 'only=&';
                    360: 	}
1.42      matthew   361:         if (omit != null) {
                    362:             url += 'omit=' + omit + '&';
1.217     albertel  363:         } else {
                    364:             url += 'omit=&';
                    365: 	}
1.135     albertel  366:         if (titleelement != null) {
                    367:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  368:         } else {
                    369: 	    url += 'titleelement=&';
                    370: 	}
1.42      matthew   371:         url += 'element=' + elementname + '';
                    372:         var title = 'Browser';
1.435     albertel  373:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   374:         options += ',width=700,height=600';
                    375:         editbrowser = open(url,title,options,'1');
                    376:         editbrowser.focus();
                    377:     }
                    378:     var editsearcher;
1.135     albertel  379:     function opensearcher(formname,elementname,titleelement) {
1.42      matthew   380:         var url = '/adm/searchcat?';
                    381:         if (editsearcher == null) {
                    382:             url += 'launch=1&';
                    383:         }
                    384:         url += 'catalogmode=interactive&';
1.199     albertel  385:         url += 'mode=$mode&';
1.42      matthew   386:         url += 'form=' + formname + '&';
1.135     albertel  387:         if (titleelement != null) {
                    388:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  389:         } else {
                    390: 	    url += 'titleelement=&';
                    391: 	}
1.42      matthew   392:         url += 'element=' + elementname + '';
                    393:         var title = 'Search';
1.435     albertel  394:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   395:         options += ',width=700,height=600';
                    396:         editsearcher = open(url,title,options,'1');
                    397:         editsearcher.focus();
                    398:     }
1.219     albertel  399: // END LON-CAPA Internal -->
1.42      matthew   400: END
1.170     www       401: }
                    402: 
                    403: sub lastresurl {
1.258     albertel  404:     if ($env{'environment.lastresurl'}) {
                    405: 	return $env{'environment.lastresurl'}
1.170     www       406:     } else {
                    407: 	return '/res';
                    408:     }
                    409: }
                    410: 
                    411: sub storeresurl {
                    412:     my $resurl=&Apache::lonnet::clutter(shift);
                    413:     unless ($resurl=~/^\/res/) { return 0; }
                    414:     $resurl=~s/\/$//;
                    415:     &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646     raeburn   416:     &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170     www       417:     return 1;
1.42      matthew   418: }
                    419: 
1.74      www       420: sub studentbrowser_javascript {
1.111     www       421:    unless (
1.258     albertel  422:             (($env{'request.course.id'}) && 
1.302     albertel  423:              (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    424: 	      || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    425: 					  '/'.$env{'request.course.sec'})
                    426: 	      ))
1.258     albertel  427:          || ($env{'request.role'}=~/^(au|dc|su)/)
1.111     www       428:           ) { return ''; }  
1.74      www       429:    return (<<'ENDSTDBRW');
1.776     bisitz    430: <script type="text/javascript" language="Javascript">
1.824     bisitz    431: // <![CDATA[
1.74      www       432:     var stdeditbrowser;
1.999     www       433:     function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadvonly) {
1.74      www       434:         var url = '/adm/pickstudent?';
                    435:         var filter;
1.558     albertel  436: 	if (!ignorefilter) {
                    437: 	    eval('filter=document.'+formname+'.'+uname+'.value;');
                    438: 	}
1.74      www       439:         if (filter != null) {
                    440:            if (filter != '') {
                    441:                url += 'filter='+filter+'&';
                    442: 	   }
                    443:         }
                    444:         url += 'form=' + formname + '&unameelement='+uname+
1.999     www       445:                                     '&udomelement='+udom+
                    446:                                     '&clicker='+clicker;
1.111     www       447: 	if (roleflag) { url+="&roles=1"; }
1.793     raeburn   448:         if (courseadvonly) { url+="&courseadvonly=1"; }
1.102     www       449:         var title = 'Student_Browser';
1.74      www       450:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    451:         options += ',width=700,height=600';
                    452:         stdeditbrowser = open(url,title,options,'1');
                    453:         stdeditbrowser.focus();
                    454:     }
1.824     bisitz    455: // ]]>
1.74      www       456: </script>
                    457: ENDSTDBRW
                    458: }
1.42      matthew   459: 
1.1003    www       460: sub resourcebrowser_javascript {
                    461:    unless ($env{'request.course.id'}) { return ''; }
1.1004    www       462:    return (<<'ENDRESBRW');
1.1003    www       463: <script type="text/javascript" language="Javascript">
                    464: // <![CDATA[
                    465:     var reseditbrowser;
1.1004    www       466:     function openresbrowser(formname,reslink) {
1.1005    www       467:         var url = '/adm/pickresource?form='+formname+'&reslink='+reslink;
1.1003    www       468:         var title = 'Resource_Browser';
                    469:         var options = 'scrollbars=1,resizable=1,menubar=0';
1.1005    www       470:         options += ',width=700,height=500';
1.1004    www       471:         reseditbrowser = open(url,title,options,'1');
                    472:         reseditbrowser.focus();
1.1003    www       473:     }
                    474: // ]]>
                    475: </script>
1.1004    www       476: ENDRESBRW
1.1003    www       477: }
                    478: 
1.74      www       479: sub selectstudent_link {
1.999     www       480:    my ($form,$unameele,$udomele,$courseadvonly,$clickerid)=@_;
                    481:    my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
                    482:                       &Apache::lonhtmlcommon::entity_encode($unameele)."','".
                    483:                       &Apache::lonhtmlcommon::entity_encode($udomele)."'";
1.258     albertel  484:    if ($env{'request.course.id'}) {  
1.302     albertel  485:        if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    486: 	   && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    487: 					'/'.$env{'request.course.sec'})) {
1.111     www       488: 	   return '';
                    489:        }
1.999     www       490:        $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
1.793     raeburn   491:        if ($courseadvonly)  {
                    492:            $callargs .= ",'',1,1";
                    493:        }
                    494:        return '<span class="LC_nobreak">'.
                    495:               '<a href="javascript:openstdbrowser('.$callargs.');">'.
                    496:               &mt('Select User').'</a></span>';
1.74      www       497:    }
1.258     albertel  498:    if ($env{'request.role'}=~/^(au|dc|su)/) {
1.1012    www       499:        $callargs .= ",'',1"; 
1.793     raeburn   500:        return '<span class="LC_nobreak">'.
                    501:               '<a href="javascript:openstdbrowser('.$callargs.');">'.
                    502:               &mt('Select User').'</a></span>';
1.111     www       503:    }
                    504:    return '';
1.91      www       505: }
                    506: 
1.1004    www       507: sub selectresource_link {
                    508:    my ($form,$reslink,$arg)=@_;
                    509:    
                    510:    my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
                    511:                       &Apache::lonhtmlcommon::entity_encode($reslink)."'";
                    512:    unless ($env{'request.course.id'}) { return $arg; }
                    513:    return '<span class="LC_nobreak">'.
                    514:               '<a href="javascript:openresbrowser('.$callargs.');">'.
                    515:               $arg.'</a></span>';
                    516: }
                    517: 
                    518: 
                    519: 
1.653     raeburn   520: sub authorbrowser_javascript {
                    521:     return <<"ENDAUTHORBRW";
1.776     bisitz    522: <script type="text/javascript" language="JavaScript">
1.824     bisitz    523: // <![CDATA[
1.653     raeburn   524: var stdeditbrowser;
                    525: 
                    526: function openauthorbrowser(formname,udom) {
                    527:     var url = '/adm/pickauthor?';
                    528:     url += 'form='+formname+'&roledom='+udom;
                    529:     var title = 'Author_Browser';
                    530:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    531:     options += ',width=700,height=600';
                    532:     stdeditbrowser = open(url,title,options,'1');
                    533:     stdeditbrowser.focus();
                    534: }
                    535: 
1.824     bisitz    536: // ]]>
1.653     raeburn   537: </script>
                    538: ENDAUTHORBRW
                    539: }
                    540: 
1.91      www       541: sub coursebrowser_javascript {
1.1116    raeburn   542:     my ($domainfilter,$sec_element,$formname,$role_element,$crstype,
1.1221    raeburn   543:         $credits_element,$instcode) = @_;
1.932     raeburn   544:     my $wintitle = 'Course_Browser';
1.931     raeburn   545:     if ($crstype eq 'Community') {
1.932     raeburn   546:         $wintitle = 'Community_Browser';
1.909     raeburn   547:     }
1.876     raeburn   548:     my $id_functions = &javascript_index_functions();
                    549:     my $output = '
1.776     bisitz    550: <script type="text/javascript" language="JavaScript">
1.824     bisitz    551: // <![CDATA[
1.468     raeburn   552:     var stdeditbrowser;'."\n";
1.876     raeburn   553: 
                    554:     $output .= <<"ENDSTDBRW";
1.909     raeburn   555:     function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,type,type_elem) {
1.91      www       556:         var url = '/adm/pickcourse?';
1.895     raeburn   557:         var formid = getFormIdByName(formname);
1.876     raeburn   558:         var domainfilter = getDomainFromSelectbox(formname,udom);
1.128     albertel  559:         if (domainfilter != null) {
                    560:            if (domainfilter != '') {
                    561:                url += 'domainfilter='+domainfilter+'&';
                    562: 	   }
                    563:         }
1.91      www       564:         url += 'form=' + formname + '&cnumelement='+uname+
1.187     albertel  565: 	                            '&cdomelement='+udom+
                    566:                                     '&cnameelement='+desc;
1.468     raeburn   567:         if (extra_element !=null && extra_element != '') {
1.594     raeburn   568:             if (formname == 'rolechoice' || formname == 'studentform') {
1.468     raeburn   569:                 url += '&roleelement='+extra_element;
                    570:                 if (domainfilter == null || domainfilter == '') {
                    571:                     url += '&domainfilter='+extra_element;
                    572:                 }
1.234     raeburn   573:             }
1.468     raeburn   574:             else {
                    575:                 if (formname == 'portform') {
                    576:                     url += '&setroles='+extra_element;
1.800     raeburn   577:                 } else {
                    578:                     if (formname == 'rules') {
                    579:                         url += '&fixeddom='+extra_element; 
                    580:                     }
1.468     raeburn   581:                 }
                    582:             }     
1.230     raeburn   583:         }
1.909     raeburn   584:         if (type != null && type != '') {
                    585:             url += '&type='+type;
                    586:         }
                    587:         if (type_elem != null && type_elem != '') {
                    588:             url += '&typeelement='+type_elem;
                    589:         }
1.872     raeburn   590:         if (formname == 'ccrs') {
                    591:             var ownername = document.forms[formid].ccuname.value;
                    592:             var ownerdom =  document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
1.1221    raeburn   593:             url += '&cloner='+ownername+':'+ownerdom+'&crscode='+document.forms[formid].crscode.value;
                    594:         }
                    595:         if (formname == 'requestcrs') {
                    596:             url += '&crsdom=$domainfilter&crscode=$instcode';
1.872     raeburn   597:         }
1.293     raeburn   598:         if (multflag !=null && multflag != '') {
                    599:             url += '&multiple='+multflag;
                    600:         }
1.909     raeburn   601:         var title = '$wintitle';
1.91      www       602:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    603:         options += ',width=700,height=600';
                    604:         stdeditbrowser = open(url,title,options,'1');
                    605:         stdeditbrowser.focus();
                    606:     }
1.876     raeburn   607: $id_functions
                    608: ENDSTDBRW
1.1116    raeburn   609:     if (($sec_element ne '') || ($role_element ne '') || ($credits_element ne '')) {
                    610:         $output .= &setsec_javascript($sec_element,$formname,$role_element,
                    611:                                       $credits_element);
1.876     raeburn   612:     }
                    613:     $output .= '
                    614: // ]]>
                    615: </script>';
                    616:     return $output;
                    617: }
                    618: 
                    619: sub javascript_index_functions {
                    620:     return <<"ENDJS";
                    621: 
                    622: function getFormIdByName(formname) {
                    623:     for (var i=0;i<document.forms.length;i++) {
                    624:         if (document.forms[i].name == formname) {
                    625:             return i;
                    626:         }
                    627:     }
                    628:     return -1;
                    629: }
                    630: 
                    631: function getIndexByName(formid,item) {
                    632:     for (var i=0;i<document.forms[formid].elements.length;i++) {
                    633:         if (document.forms[formid].elements[i].name == item) {
                    634:             return i;
                    635:         }
                    636:     }
                    637:     return -1;
                    638: }
1.468     raeburn   639: 
1.876     raeburn   640: function getDomainFromSelectbox(formname,udom) {
                    641:     var userdom;
                    642:     var formid = getFormIdByName(formname);
                    643:     if (formid > -1) {
                    644:         var domid = getIndexByName(formid,udom);
                    645:         if (domid > -1) {
                    646:             if (document.forms[formid].elements[domid].type == 'select-one') {
                    647:                 userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
                    648:             }
                    649:             if (document.forms[formid].elements[domid].type == 'hidden') {
                    650:                 userdom=document.forms[formid].elements[domid].value;
1.468     raeburn   651:             }
                    652:         }
                    653:     }
1.876     raeburn   654:     return userdom;
                    655: }
                    656: 
                    657: ENDJS
1.468     raeburn   658: 
1.876     raeburn   659: }
                    660: 
1.1017    raeburn   661: sub javascript_array_indexof {
1.1018    raeburn   662:     return <<ENDJS;
1.1017    raeburn   663: <script type="text/javascript" language="JavaScript">
                    664: // <![CDATA[
                    665: 
                    666: if (!Array.prototype.indexOf) {
                    667:     Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
                    668:         "use strict";
                    669:         if (this === void 0 || this === null) {
                    670:             throw new TypeError();
                    671:         }
                    672:         var t = Object(this);
                    673:         var len = t.length >>> 0;
                    674:         if (len === 0) {
                    675:             return -1;
                    676:         }
                    677:         var n = 0;
                    678:         if (arguments.length > 0) {
                    679:             n = Number(arguments[1]);
1.1088    foxr      680:             if (n !== n) { // shortcut for verifying if it is NaN
1.1017    raeburn   681:                 n = 0;
                    682:             } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
                    683:                 n = (n > 0 || -1) * Math.floor(Math.abs(n));
                    684:             }
                    685:         }
                    686:         if (n >= len) {
                    687:             return -1;
                    688:         }
                    689:         var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
                    690:         for (; k < len; k++) {
                    691:             if (k in t && t[k] === searchElement) {
                    692:                 return k;
                    693:             }
                    694:         }
                    695:         return -1;
                    696:     }
                    697: }
                    698: 
                    699: // ]]>
                    700: </script>
                    701: 
                    702: ENDJS
                    703: 
                    704: }
                    705: 
1.876     raeburn   706: sub userbrowser_javascript {
                    707:     my $id_functions = &javascript_index_functions();
                    708:     return <<"ENDUSERBRW";
                    709: 
1.888     raeburn   710: function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
1.876     raeburn   711:     var url = '/adm/pickuser?';
                    712:     var userdom = getDomainFromSelectbox(formname,udom);
                    713:     if (userdom != null) {
                    714:        if (userdom != '') {
                    715:            url += 'srchdom='+userdom+'&';
                    716:        }
                    717:     }
                    718:     url += 'form=' + formname + '&unameelement='+uname+
                    719:                                 '&udomelement='+udom+
                    720:                                 '&ulastelement='+ulast+
                    721:                                 '&ufirstelement='+ufirst+
                    722:                                 '&uemailelement='+uemail+
1.881     raeburn   723:                                 '&hideudomelement='+hideudom+
                    724:                                 '&coursedom='+crsdom;
1.888     raeburn   725:     if ((caller != null) && (caller != undefined)) {
                    726:         url += '&caller='+caller;
                    727:     }
1.876     raeburn   728:     var title = 'User_Browser';
                    729:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    730:     options += ',width=700,height=600';
                    731:     var stdeditbrowser = open(url,title,options,'1');
                    732:     stdeditbrowser.focus();
                    733: }
                    734: 
1.888     raeburn   735: function fix_domain (formname,udom,origdom,uname) {
1.876     raeburn   736:     var formid = getFormIdByName(formname);
                    737:     if (formid > -1) {
1.888     raeburn   738:         var unameid = getIndexByName(formid,uname);
1.876     raeburn   739:         var domid = getIndexByName(formid,udom);
                    740:         var hidedomid = getIndexByName(formid,origdom);
                    741:         if (hidedomid > -1) {
                    742:             var fixeddom = document.forms[formid].elements[hidedomid].value;
1.888     raeburn   743:             var unameval = document.forms[formid].elements[unameid].value;
                    744:             if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
                    745:                 if (domid > -1) {
                    746:                     var slct = document.forms[formid].elements[domid];
                    747:                     if (slct.type == 'select-one') {
                    748:                         var i;
                    749:                         for (i=0;i<slct.length;i++) {
                    750:                             if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
                    751:                         }
                    752:                     }
                    753:                     if (slct.type == 'hidden') {
                    754:                         slct.value = fixeddom;
1.876     raeburn   755:                     }
                    756:                 }
1.468     raeburn   757:             }
                    758:         }
                    759:     }
1.876     raeburn   760:     return;
                    761: }
                    762: 
                    763: $id_functions
                    764: ENDUSERBRW
1.468     raeburn   765: }
                    766: 
                    767: sub setsec_javascript {
1.1116    raeburn   768:     my ($sec_element,$formname,$role_element,$credits_element) = @_;
1.905     raeburn   769:     my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
                    770:         $communityrolestr);
                    771:     if ($role_element ne '') {
                    772:         my @allroles = ('st','ta','ep','in','ad');
                    773:         foreach my $crstype ('Course','Community') {
                    774:             if ($crstype eq 'Community') {
                    775:                 foreach my $role (@allroles) {
                    776:                     push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
                    777:                 }
                    778:                 push(@communityrolenames,&Apache::lonnet::plaintext('co'));
                    779:             } else {
                    780:                 foreach my $role (@allroles) {
                    781:                     push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
                    782:                 }
                    783:                 push(@courserolenames,&Apache::lonnet::plaintext('cc'));
                    784:             }
                    785:         }
                    786:         $rolestr = '"'.join('","',@allroles).'"';
                    787:         $courserolestr = '"'.join('","',@courserolenames).'"';
                    788:         $communityrolestr = '"'.join('","',@communityrolenames).'"';
                    789:     }
1.468     raeburn   790:     my $setsections = qq|
                    791: function setSect(sectionlist) {
1.629     raeburn   792:     var sectionsArray = new Array();
                    793:     if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
                    794:         sectionsArray = sectionlist.split(",");
                    795:     }
1.468     raeburn   796:     var numSections = sectionsArray.length;
                    797:     document.$formname.$sec_element.length = 0;
                    798:     if (numSections == 0) {
                    799:         document.$formname.$sec_element.multiple=false;
                    800:         document.$formname.$sec_element.size=1;
                    801:         document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
                    802:     } else {
                    803:         if (numSections == 1) {
                    804:             document.$formname.$sec_element.multiple=false;
                    805:             document.$formname.$sec_element.size=1;
                    806:             document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
                    807:             document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
                    808:             document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
                    809:         } else {
                    810:             for (var i=0; i<numSections; i++) {
                    811:                 document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
                    812:             }
                    813:             document.$formname.$sec_element.multiple=true
                    814:             if (numSections < 3) {
                    815:                 document.$formname.$sec_element.size=numSections;
                    816:             } else {
                    817:                 document.$formname.$sec_element.size=3;
                    818:             }
                    819:             document.$formname.$sec_element.options[0].selected = false
                    820:         }
                    821:     }
1.91      www       822: }
1.905     raeburn   823: 
                    824: function setRole(crstype) {
1.468     raeburn   825: |;
1.905     raeburn   826:     if ($role_element eq '') {
                    827:         $setsections .= '    return;
                    828: }
                    829: ';
                    830:     } else {
                    831:         $setsections .= qq|
                    832:     var elementLength = document.$formname.$role_element.length;
                    833:     var allroles = Array($rolestr);
                    834:     var courserolenames = Array($courserolestr);
                    835:     var communityrolenames = Array($communityrolestr);
                    836:     if (elementLength != undefined) {
                    837:         if (document.$formname.$role_element.options[5].value == 'cc') {
                    838:             if (crstype == 'Course') {
                    839:                 return;
                    840:             } else {
                    841:                 allroles[5] = 'co';
                    842:                 for (var i=0; i<6; i++) {
                    843:                     document.$formname.$role_element.options[i].value = allroles[i];
                    844:                     document.$formname.$role_element.options[i].text = communityrolenames[i];
                    845:                 }
                    846:             }
                    847:         } else {
                    848:             if (crstype == 'Community') {
                    849:                 return;
                    850:             } else {
                    851:                 allroles[5] = 'cc';
                    852:                 for (var i=0; i<6; i++) {
                    853:                     document.$formname.$role_element.options[i].value = allroles[i];
                    854:                     document.$formname.$role_element.options[i].text = courserolenames[i];
                    855:                 }
                    856:             }
                    857:         }
                    858:     }
                    859:     return;
                    860: }
                    861: |;
                    862:     }
1.1116    raeburn   863:     if ($credits_element) {
                    864:         $setsections .= qq|
                    865: function setCredits(defaultcredits) {
                    866:     document.$formname.$credits_element.value = defaultcredits;
                    867:     return;
                    868: }
                    869: |;
                    870:     }
1.468     raeburn   871:     return $setsections;
                    872: }
                    873: 
1.91      www       874: sub selectcourse_link {
1.909     raeburn   875:    my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype,
                    876:        $typeelement) = @_;
                    877:    my $type = $selecttype;
1.871     raeburn   878:    my $linktext = &mt('Select Course');
                    879:    if ($selecttype eq 'Community') {
1.909     raeburn   880:        $linktext = &mt('Select Community');
1.906     raeburn   881:    } elsif ($selecttype eq 'Course/Community') {
                    882:        $linktext = &mt('Select Course/Community');
1.909     raeburn   883:        $type = '';
1.1019    raeburn   884:    } elsif ($selecttype eq 'Select') {
                    885:        $linktext = &mt('Select');
                    886:        $type = '';
1.871     raeburn   887:    }
1.787     bisitz    888:    return '<span class="LC_nobreak">'
                    889:          ."<a href='"
                    890:          .'javascript:opencrsbrowser("'.$form.'","'.$unameele
                    891:          .'","'.$udomele.'","'.$desc.'","'.$extra_element
1.909     raeburn   892:          .'","'.$multflag.'","'.$type.'","'.$typeelement.'");'
1.871     raeburn   893:          ."'>".$linktext.'</a>'
1.787     bisitz    894:          .'</span>';
1.74      www       895: }
1.42      matthew   896: 
1.653     raeburn   897: sub selectauthor_link {
                    898:    my ($form,$udom)=@_;
                    899:    return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
                    900:           &mt('Select Author').'</a>';
                    901: }
                    902: 
1.876     raeburn   903: sub selectuser_link {
1.881     raeburn   904:     my ($form,$unameelem,$domelem,$lastelem,$firstelem,$emailelem,$hdomelem,
1.888     raeburn   905:         $coursedom,$linktext,$caller) = @_;
1.876     raeburn   906:     return '<a href="javascript:openuserbrowser('."'$form','$unameelem','$domelem',".
1.888     raeburn   907:            "'$lastelem','$firstelem','$emailelem','$hdomelem','$coursedom','$caller'".
1.881     raeburn   908:            ');">'.$linktext.'</a>';
1.876     raeburn   909: }
                    910: 
1.273     raeburn   911: sub check_uncheck_jscript {
                    912:     my $jscript = <<"ENDSCRT";
                    913: function checkAll(field) {
                    914:     if (field.length > 0) {
                    915:         for (i = 0; i < field.length; i++) {
1.1093    raeburn   916:             if (!field[i].disabled) { 
                    917:                 field[i].checked = true;
                    918:             }
1.273     raeburn   919:         }
                    920:     } else {
1.1093    raeburn   921:         if (!field.disabled) { 
                    922:             field.checked = true;
                    923:         }
1.273     raeburn   924:     }
                    925: }
                    926:  
                    927: function uncheckAll(field) {
                    928:     if (field.length > 0) {
                    929:         for (i = 0; i < field.length; i++) {
                    930:             field[i].checked = false ;
1.543     albertel  931:         }
                    932:     } else {
1.273     raeburn   933:         field.checked = false ;
                    934:     }
                    935: }
                    936: ENDSCRT
                    937:     return $jscript;
                    938: }
                    939: 
1.656     www       940: sub select_timezone {
1.659     raeburn   941:    my ($name,$selected,$onchange,$includeempty)=@_;
                    942:    my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
                    943:    if ($includeempty) {
                    944:        $output .= '<option value=""';
                    945:        if (($selected eq '') || ($selected eq 'local')) {
                    946:            $output .= ' selected="selected" ';
                    947:        }
                    948:        $output .= '> </option>';
                    949:    }
1.657     raeburn   950:    my @timezones = DateTime::TimeZone->all_names;
                    951:    foreach my $tzone (@timezones) {
                    952:        $output.= '<option value="'.$tzone.'"';
                    953:        if ($tzone eq $selected) {
                    954:            $output.=' selected="selected"';
                    955:        }
                    956:        $output.=">$tzone</option>\n";
1.656     www       957:    }
                    958:    $output.="</select>";
                    959:    return $output;
                    960: }
1.273     raeburn   961: 
1.687     raeburn   962: sub select_datelocale {
                    963:     my ($name,$selected,$onchange,$includeempty)=@_;
                    964:     my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
                    965:     if ($includeempty) {
                    966:         $output .= '<option value=""';
                    967:         if ($selected eq '') {
                    968:             $output .= ' selected="selected" ';
                    969:         }
                    970:         $output .= '> </option>';
                    971:     }
                    972:     my (@possibles,%locale_names);
                    973:     my @locales = DateTime::Locale::Catalog::Locales;
                    974:     foreach my $locale (@locales) {
                    975:         if (ref($locale) eq 'HASH') {
                    976:             my $id = $locale->{'id'};
                    977:             if ($id ne '') {
                    978:                 my $en_terr = $locale->{'en_territory'};
                    979:                 my $native_terr = $locale->{'native_territory'};
1.695     raeburn   980:                 my @languages = &Apache::lonlocal::preferred_languages();
1.687     raeburn   981:                 if (grep(/^en$/,@languages) || !@languages) {
                    982:                     if ($en_terr ne '') {
                    983:                         $locale_names{$id} = '('.$en_terr.')';
                    984:                     } elsif ($native_terr ne '') {
                    985:                         $locale_names{$id} = $native_terr;
                    986:                     }
                    987:                 } else {
                    988:                     if ($native_terr ne '') {
                    989:                         $locale_names{$id} = $native_terr.' ';
                    990:                     } elsif ($en_terr ne '') {
                    991:                         $locale_names{$id} = '('.$en_terr.')';
                    992:                     }
                    993:                 }
1.1220    raeburn   994:                 $locale_names{$id} = Encode::encode('UTF-8',$locale_names{$id});
1.687     raeburn   995:                 push (@possibles,$id);
                    996:             }
                    997:         }
                    998:     }
                    999:     foreach my $item (sort(@possibles)) {
                   1000:         $output.= '<option value="'.$item.'"';
                   1001:         if ($item eq $selected) {
                   1002:             $output.=' selected="selected"';
                   1003:         }
                   1004:         $output.=">$item";
                   1005:         if ($locale_names{$item} ne '') {
1.1220    raeburn  1006:             $output.='  '.$locale_names{$item};
1.687     raeburn  1007:         }
                   1008:         $output.="</option>\n";
                   1009:     }
                   1010:     $output.="</select>";
                   1011:     return $output;
                   1012: }
                   1013: 
1.792     raeburn  1014: sub select_language {
                   1015:     my ($name,$selected,$includeempty) = @_;
                   1016:     my %langchoices;
                   1017:     if ($includeempty) {
1.1117    raeburn  1018:         %langchoices = ('' => 'No language preference');
1.792     raeburn  1019:     }
                   1020:     foreach my $id (&languageids()) {
                   1021:         my $code = &supportedlanguagecode($id);
                   1022:         if ($code) {
                   1023:             $langchoices{$code} = &plainlanguagedescription($id);
                   1024:         }
                   1025:     }
1.1117    raeburn  1026:     %langchoices = &Apache::lonlocal::texthash(%langchoices);
1.970     raeburn  1027:     return &select_form($selected,$name,\%langchoices);
1.792     raeburn  1028: }
                   1029: 
1.42      matthew  1030: =pod
1.36      matthew  1031: 
1.1088    foxr     1032: 
                   1033: =item * &list_languages()
                   1034: 
                   1035: Returns an array reference that is suitable for use in language prompters.
                   1036: Each array element is itself a two element array.  The first element
                   1037: is the language code.  The second element a descsriptiuon of the 
                   1038: language itself.  This is suitable for use in e.g.
                   1039: &Apache::edit::select_arg (once dereferenced that is).
                   1040: 
                   1041: =cut 
                   1042: 
                   1043: sub list_languages {
                   1044:     my @lang_choices;
                   1045: 
                   1046:     foreach my $id (&languageids()) {
                   1047: 	my $code = &supportedlanguagecode($id);
                   1048: 	if ($code) {
                   1049: 	    my $selector    = $supported_codes{$id};
                   1050: 	    my $description = &plainlanguagedescription($id);
                   1051: 	    push (@lang_choices, [$selector, $description]);
                   1052: 	}
                   1053:     }
                   1054:     return \@lang_choices;
                   1055: }
                   1056: 
                   1057: =pod
                   1058: 
1.648     raeburn  1059: =item * &linked_select_forms(...)
1.36      matthew  1060: 
                   1061: linked_select_forms returns a string containing a <script></script> block
                   1062: and html for two <select> menus.  The select menus will be linked in that
                   1063: changing the value of the first menu will result in new values being placed
                   1064: in the second menu.  The values in the select menu will appear in alphabetical
1.609     raeburn  1065: order unless a defined order is provided.
1.36      matthew  1066: 
                   1067: linked_select_forms takes the following ordered inputs:
                   1068: 
                   1069: =over 4
                   1070: 
1.112     bowersj2 1071: =item * $formname, the name of the <form> tag
1.36      matthew  1072: 
1.112     bowersj2 1073: =item * $middletext, the text which appears between the <select> tags
1.36      matthew  1074: 
1.112     bowersj2 1075: =item * $firstdefault, the default value for the first menu
1.36      matthew  1076: 
1.112     bowersj2 1077: =item * $firstselectname, the name of the first <select> tag
1.36      matthew  1078: 
1.112     bowersj2 1079: =item * $secondselectname, the name of the second <select> tag
1.36      matthew  1080: 
1.112     bowersj2 1081: =item * $hashref, a reference to a hash containing the data for the menus.
1.36      matthew  1082: 
1.609     raeburn  1083: =item * $menuorder, the order of values in the first menu
                   1084: 
1.1115    raeburn  1085: =item * $onchangefirst, additional javascript call to execute for an onchange
                   1086:         event for the first <select> tag
                   1087: 
                   1088: =item * $onchangesecond, additional javascript call to execute for an onchange
                   1089:         event for the second <select> tag
                   1090: 
1.41      ng       1091: =back 
                   1092: 
1.36      matthew  1093: Below is an example of such a hash.  Only the 'text', 'default', and 
                   1094: 'select2' keys must appear as stated.  keys(%menu) are the possible 
                   1095: values for the first select menu.  The text that coincides with the 
1.41      ng       1096: first menu value is given in $menu{$choice1}->{'text'}.  The values 
1.36      matthew  1097: and text for the second menu are given in the hash pointed to by 
                   1098: $menu{$choice1}->{'select2'}.  
                   1099: 
1.112     bowersj2 1100:  my %menu = ( A1 => { text =>"Choice A1" ,
                   1101:                        default => "B3",
                   1102:                        select2 => { 
                   1103:                            B1 => "Choice B1",
                   1104:                            B2 => "Choice B2",
                   1105:                            B3 => "Choice B3",
                   1106:                            B4 => "Choice B4"
1.609     raeburn  1107:                            },
                   1108:                        order => ['B4','B3','B1','B2'],
1.112     bowersj2 1109:                    },
                   1110:                A2 => { text =>"Choice A2" ,
                   1111:                        default => "C2",
                   1112:                        select2 => { 
                   1113:                            C1 => "Choice C1",
                   1114:                            C2 => "Choice C2",
                   1115:                            C3 => "Choice C3"
1.609     raeburn  1116:                            },
                   1117:                        order => ['C2','C1','C3'],
1.112     bowersj2 1118:                    },
                   1119:                A3 => { text =>"Choice A3" ,
                   1120:                        default => "D6",
                   1121:                        select2 => { 
                   1122:                            D1 => "Choice D1",
                   1123:                            D2 => "Choice D2",
                   1124:                            D3 => "Choice D3",
                   1125:                            D4 => "Choice D4",
                   1126:                            D5 => "Choice D5",
                   1127:                            D6 => "Choice D6",
                   1128:                            D7 => "Choice D7"
1.609     raeburn  1129:                            },
                   1130:                        order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112     bowersj2 1131:                    }
                   1132:                );
1.36      matthew  1133: 
                   1134: =cut
                   1135: 
                   1136: sub linked_select_forms {
                   1137:     my ($formname,
                   1138:         $middletext,
                   1139:         $firstdefault,
                   1140:         $firstselectname,
                   1141:         $secondselectname, 
1.609     raeburn  1142:         $hashref,
                   1143:         $menuorder,
1.1115    raeburn  1144:         $onchangefirst,
                   1145:         $onchangesecond
1.36      matthew  1146:         ) = @_;
                   1147:     my $second = "document.$formname.$secondselectname";
                   1148:     my $first = "document.$formname.$firstselectname";
                   1149:     # output the javascript to do the changing
                   1150:     my $result = '';
1.776     bisitz   1151:     $result.='<script type="text/javascript" language="JavaScript">'."\n";
1.824     bisitz   1152:     $result.="// <![CDATA[\n";
1.36      matthew  1153:     $result.="var select2data = new Object();\n";
                   1154:     $" = '","';
                   1155:     my $debug = '';
                   1156:     foreach my $s1 (sort(keys(%$hashref))) {
                   1157:         $result.="select2data.d_$s1 = new Object();\n";        
                   1158:         $result.="select2data.d_$s1.def = new String('".
                   1159:             $hashref->{$s1}->{'default'}."');\n";
1.609     raeburn  1160:         $result.="select2data.d_$s1.values = new Array(";
1.36      matthew  1161:         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609     raeburn  1162:         if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
                   1163:             @s2values = @{$hashref->{$s1}->{'order'}};
                   1164:         }
1.36      matthew  1165:         $result.="\"@s2values\");\n";
                   1166:         $result.="select2data.d_$s1.texts = new Array(";        
                   1167:         my @s2texts;
                   1168:         foreach my $value (@s2values) {
                   1169:             push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
                   1170:         }
                   1171:         $result.="\"@s2texts\");\n";
                   1172:     }
                   1173:     $"=' ';
                   1174:     $result.= <<"END";
                   1175: 
                   1176: function select1_changed() {
                   1177:     // Determine new choice
                   1178:     var newvalue = "d_" + $first.value;
                   1179:     // update select2
                   1180:     var values     = select2data[newvalue].values;
                   1181:     var texts      = select2data[newvalue].texts;
                   1182:     var select2def = select2data[newvalue].def;
                   1183:     var i;
                   1184:     // out with the old
                   1185:     for (i = 0; i < $second.options.length; i++) {
                   1186:         $second.options[i] = null;
                   1187:     }
                   1188:     // in with the nuclear
                   1189:     for (i=0;i<values.length; i++) {
                   1190:         $second.options[i] = new Option(values[i]);
1.143     matthew  1191:         $second.options[i].value = values[i];
1.36      matthew  1192:         $second.options[i].text = texts[i];
                   1193:         if (values[i] == select2def) {
                   1194:             $second.options[i].selected = true;
                   1195:         }
                   1196:     }
                   1197: }
1.824     bisitz   1198: // ]]>
1.36      matthew  1199: </script>
                   1200: END
                   1201:     # output the initial values for the selection lists
1.1115    raeburn  1202:     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed();$onchangefirst\">\n";
1.609     raeburn  1203:     my @order = sort(keys(%{$hashref}));
                   1204:     if (ref($menuorder) eq 'ARRAY') {
                   1205:         @order = @{$menuorder};
                   1206:     }
                   1207:     foreach my $value (@order) {
1.36      matthew  1208:         $result.="    <option value=\"$value\" ";
1.253     albertel 1209:         $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119     www      1210:         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36      matthew  1211:     }
                   1212:     $result .= "</select>\n";
                   1213:     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
                   1214:     $result .= $middletext;
1.1115    raeburn  1215:     $result .= "<select size=\"1\" name=\"$secondselectname\"";
                   1216:     if ($onchangesecond) {
                   1217:         $result .= ' onchange="'.$onchangesecond.'"';
                   1218:     }
                   1219:     $result .= ">\n";
1.36      matthew  1220:     my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609     raeburn  1221:     
                   1222:     my @secondorder = sort(keys(%select2));
                   1223:     if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
                   1224:         @secondorder = @{$hashref->{$firstdefault}->{'order'}};
                   1225:     }
                   1226:     foreach my $value (@secondorder) {
1.36      matthew  1227:         $result.="    <option value=\"$value\" ";        
1.253     albertel 1228:         $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119     www      1229:         $result.=">".&mt($select2{$value})."</option>\n";
1.36      matthew  1230:     }
                   1231:     $result .= "</select>\n";
                   1232:     #    return $debug;
                   1233:     return $result;
                   1234: }   #  end of sub linked_select_forms {
                   1235: 
1.45      matthew  1236: =pod
1.44      bowersj2 1237: 
1.973     raeburn  1238: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid)
1.44      bowersj2 1239: 
1.112     bowersj2 1240: Returns a string corresponding to an HTML link to the given help
                   1241: $topic, where $topic corresponds to the name of a .tex file in
                   1242: /home/httpd/html/adm/help/tex, with underscores replaced by
                   1243: spaces. 
                   1244: 
                   1245: $text will optionally be linked to the same topic, allowing you to
                   1246: link text in addition to the graphic. If you do not want to link
                   1247: text, but wish to specify one of the later parameters, pass an
                   1248: empty string. 
                   1249: 
                   1250: $stayOnPage is a value that will be interpreted as a boolean. If true,
                   1251: the link will not open a new window. If false, the link will open
                   1252: a new window using Javascript. (Default is false.) 
                   1253: 
                   1254: $width and $height are optional numerical parameters that will
                   1255: override the width and height of the popped up window, which may
1.973     raeburn  1256: be useful for certain help topics with big pictures included.
                   1257: 
                   1258: $imgid is the id of the img tag used for the help icon. This may be
                   1259: used in a javascript call to switch the image src.  See 
                   1260: lonhtmlcommon::htmlareaselectactive() for an example.
1.44      bowersj2 1261: 
                   1262: =cut
                   1263: 
                   1264: sub help_open_topic {
1.973     raeburn  1265:     my ($topic, $text, $stayOnPage, $width, $height, $imgid) = @_;
1.48      bowersj2 1266:     $text = "" if (not defined $text);
1.44      bowersj2 1267:     $stayOnPage = 0 if (not defined $stayOnPage);
1.1033    www      1268:     $width = 500 if (not defined $width);
1.44      bowersj2 1269:     $height = 400 if (not defined $height);
                   1270:     my $filename = $topic;
                   1271:     $filename =~ s/ /_/g;
                   1272: 
1.48      bowersj2 1273:     my $template = "";
                   1274:     my $link;
1.572     banghart 1275:     
1.159     www      1276:     $topic=~s/\W/\_/g;
1.44      bowersj2 1277: 
1.572     banghart 1278:     if (!$stayOnPage) {
1.1033    www      1279: 	$link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
1.1037    www      1280:     } elsif ($stayOnPage eq 'popup') {
                   1281:         $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 1282:     } else {
1.48      bowersj2 1283: 	$link = "/adm/help/${filename}.hlp";
                   1284:     }
                   1285: 
                   1286:     # Add the text
1.755     neumanie 1287:     if ($text ne "") {	
1.763     bisitz   1288: 	$template.='<span class="LC_help_open_topic">'
                   1289:                   .'<a target="_top" href="'.$link.'">'
                   1290:                   .$text.'</a>';
1.48      bowersj2 1291:     }
                   1292: 
1.763     bisitz   1293:     # (Always) Add the graphic
1.179     matthew  1294:     my $title = &mt('Online Help');
1.667     raeburn  1295:     my $helpicon=&lonhttpdurl("/adm/help/help.png");
1.973     raeburn  1296:     if ($imgid ne '') {
                   1297:         $imgid = ' id="'.$imgid.'"';
                   1298:     }
1.763     bisitz   1299:     $template.=' <a target="_top" href="'.$link.'" title="'.$title.'">'
                   1300:               .'<img src="'.$helpicon.'" border="0"'
                   1301:               .' alt="'.&mt('Help: [_1]',$topic).'"'
1.973     raeburn  1302:               .' title="'.$title.'" style="vertical-align:middle;"'.$imgid 
1.763     bisitz   1303:               .' /></a>';
                   1304:     if ($text ne "") {	
                   1305:         $template.='</span>';
                   1306:     }
1.44      bowersj2 1307:     return $template;
                   1308: 
1.106     bowersj2 1309: }
                   1310: 
                   1311: # This is a quicky function for Latex cheatsheet editing, since it 
                   1312: # appears in at least four places
                   1313: sub helpLatexCheatsheet {
1.1037    www      1314:     my ($topic,$text,$not_author,$stayOnPage) = @_;
1.732     raeburn  1315:     my $out;
1.106     bowersj2 1316:     my $addOther = '';
1.732     raeburn  1317:     if ($topic) {
1.1037    www      1318: 	$addOther = '<span>'.&help_open_topic($topic,&mt($text),$stayOnPage, undef, 600).'</span> ';
1.763     bisitz   1319:     }
                   1320:     $out = '<span>' # Start cheatsheet
                   1321: 	  .$addOther
                   1322:           .'<span>'
1.1037    www      1323: 	  .&help_open_topic('Greek_Symbols',&mt('Greek Symbols'),$stayOnPage,undef,600)
1.763     bisitz   1324: 	  .'</span> <span>'
1.1037    www      1325: 	  .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
1.763     bisitz   1326: 	  .'</span>';
1.732     raeburn  1327:     unless ($not_author) {
1.1186    kruse    1328:         $out .= '<span>'
                   1329:                .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
                   1330:                .'</span> <span>'
                   1331:                .&help_open_topic('Authoring_Multilingual_Problems',&mt('How to create problems in different languages'),$stayOnPage,undef,600)
1.763     bisitz   1332: 	       .'</span>';
1.732     raeburn  1333:     }
1.763     bisitz   1334:     $out .= '</span>'; # End cheatsheet
1.732     raeburn  1335:     return $out;
1.172     www      1336: }
                   1337: 
1.430     albertel 1338: sub general_help {
                   1339:     my $helptopic='Student_Intro';
                   1340:     if ($env{'request.role'}=~/^(ca|au)/) {
                   1341: 	$helptopic='Authoring_Intro';
1.907     raeburn  1342:     } elsif ($env{'request.role'}=~/^(cc|co)/) {
1.430     albertel 1343: 	$helptopic='Course_Coordination_Intro';
1.672     raeburn  1344:     } elsif ($env{'request.role'}=~/^dc/) {
                   1345:         $helptopic='Domain_Coordination_Intro';
1.430     albertel 1346:     }
                   1347:     return $helptopic;
                   1348: }
                   1349: 
                   1350: sub update_help_link {
                   1351:     my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
                   1352:     my $origurl = $ENV{'REQUEST_URI'};
                   1353:     $origurl=~s|^/~|/priv/|;
                   1354:     my $timestamp = time;
                   1355:     foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
                   1356:         $$datum = &escape($$datum);
                   1357:     }
                   1358: 
                   1359:     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";
                   1360:     my $output .= <<"ENDOUTPUT";
                   1361: <script type="text/javascript">
1.824     bisitz   1362: // <![CDATA[
1.430     albertel 1363: banner_link = '$banner_link';
1.824     bisitz   1364: // ]]>
1.430     albertel 1365: </script>
                   1366: ENDOUTPUT
                   1367:     return $output;
                   1368: }
                   1369: 
                   1370: # now just updates the help link and generates a blue icon
1.193     raeburn  1371: sub help_open_menu {
1.430     albertel 1372:     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text) 
1.552     banghart 1373: 	= @_;    
1.949     droeschl 1374:     $stayOnPage = 1;
1.430     albertel 1375:     my $output;
                   1376:     if ($component_help) {
                   1377: 	if (!$text) {
                   1378: 	    $output=&help_open_topic($component_help,undef,$stayOnPage,
                   1379: 				       $width,$height);
                   1380: 	} else {
                   1381: 	    my $help_text;
                   1382: 	    $help_text=&unescape($topic);
                   1383: 	    $output='<table><tr><td>'.
                   1384: 		&help_open_topic($component_help,$help_text,$stayOnPage,
                   1385: 				 $width,$height).'</td></tr></table>';
                   1386: 	}
                   1387:     }
                   1388:     my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
                   1389:     return $output.$banner_link;
                   1390: }
                   1391: 
                   1392: sub top_nav_help {
                   1393:     my ($text) = @_;
1.436     albertel 1394:     $text = &mt($text);
1.949     droeschl 1395:     my $stay_on_page = 1;
                   1396: 
1.1168    raeburn  1397:     my ($link,$banner_link);
                   1398:     unless ($env{'request.noversionuri'} =~ m{^/adm/helpmenu}) {
                   1399:         $link = ($stay_on_page) ? "javascript:helpMenu('display')"
                   1400: 	                         : "javascript:helpMenu('open')";
                   1401:         $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
                   1402:     }
1.201     raeburn  1403:     my $title = &mt('Get help');
1.1168    raeburn  1404:     if ($link) {
                   1405:         return <<"END";
1.436     albertel 1406: $banner_link
1.1159    raeburn  1407: <a href="$link" title="$title">$text</a>
1.436     albertel 1408: END
1.1168    raeburn  1409:     } else {
                   1410:         return '&nbsp;'.$text.'&nbsp;';
                   1411:     }
1.436     albertel 1412: }
                   1413: 
                   1414: sub help_menu_js {
1.1154    raeburn  1415:     my ($httphost) = @_;
1.949     droeschl 1416:     my $stayOnPage = 1;
1.436     albertel 1417:     my $width = 620;
                   1418:     my $height = 600;
1.430     albertel 1419:     my $helptopic=&general_help();
1.1154    raeburn  1420:     my $details_link = $httphost.'/adm/help/'.$helptopic.'.hlp';
1.261     albertel 1421:     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331     albertel 1422:     my $start_page =
                   1423:         &Apache::loncommon::start_page('Help Menu', undef,
                   1424: 				       {'frameset'    => 1,
                   1425: 					'js_ready'    => 1,
1.1154    raeburn  1426:                                         'use_absolute' => $httphost,
1.331     albertel 1427: 					'add_entries' => {
1.1168    raeburn  1428: 					    'border' => '0', 
1.579     raeburn  1429: 					    'rows'   => "110,*",},});
1.331     albertel 1430:     my $end_page =
                   1431:         &Apache::loncommon::end_page({'frameset' => 1,
                   1432: 				      'js_ready' => 1,});
                   1433: 
1.436     albertel 1434:     my $template .= <<"ENDTEMPLATE";
                   1435: <script type="text/javascript">
1.877     bisitz   1436: // <![CDATA[
1.253     albertel 1437: // <!-- BEGIN LON-CAPA Internal
1.430     albertel 1438: var banner_link = '';
1.243     raeburn  1439: function helpMenu(target) {
                   1440:     var caller = this;
                   1441:     if (target == 'open') {
                   1442:         var newWindow = null;
                   1443:         try {
1.262     albertel 1444:             newWindow =  window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243     raeburn  1445:         }
                   1446:         catch(error) {
                   1447:             writeHelp(caller);
                   1448:             return;
                   1449:         }
                   1450:         if (newWindow) {
                   1451:             caller = newWindow;
                   1452:         }
1.193     raeburn  1453:     }
1.243     raeburn  1454:     writeHelp(caller);
                   1455:     return;
                   1456: }
                   1457: function writeHelp(caller) {
1.1168    raeburn  1458:     caller.document.writeln('$start_page\\n<frame name="bannerframe" src="'+banner_link+'" marginwidth="0" marginheight="0" frameborder="0">\\n');
                   1459:     caller.document.writeln('<frame name="bodyframe" src="$details_link" marginwidth="0" marginheight="0" frameborder="0">\\n$end_page');
                   1460:     caller.document.close();
                   1461:     caller.focus();
1.193     raeburn  1462: }
1.877     bisitz   1463: // END LON-CAPA Internal -->
1.253     albertel 1464: // ]]>
1.436     albertel 1465: </script>
1.193     raeburn  1466: ENDTEMPLATE
                   1467:     return $template;
                   1468: }
                   1469: 
1.172     www      1470: sub help_open_bug {
                   1471:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1472:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1473:     unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
                   1474:     $text = "" if (not defined $text);
                   1475: 	$stayOnPage=1;
1.184     albertel 1476:     $width = 600 if (not defined $width);
                   1477:     $height = 600 if (not defined $height);
1.172     www      1478: 
                   1479:     $topic=~s/\W+/\+/g;
                   1480:     my $link='';
                   1481:     my $template='';
1.379     albertel 1482:     my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&amp;bug_file_loc='.
                   1483: 	&escape($ENV{'REQUEST_URI'}).'&amp;component='.$topic;
1.172     www      1484:     if (!$stayOnPage)
                   1485:     {
                   1486: 	$link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1487:     }
                   1488:     else
                   1489:     {
                   1490: 	$link = $url;
                   1491:     }
                   1492:     # Add the text
                   1493:     if ($text ne "")
                   1494:     {
                   1495: 	$template .= 
                   1496:   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705     tempelho 1497:   "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
1.172     www      1498:     }
                   1499: 
                   1500:     # Add the graphic
1.179     matthew  1501:     my $title = &mt('Report a Bug');
1.215     albertel 1502:     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172     www      1503:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1504:  <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172     www      1505: ENDTEMPLATE
                   1506:     if ($text ne '') { $template.='</td></tr></table>' };
                   1507:     return $template;
                   1508: 
                   1509: }
                   1510: 
                   1511: sub help_open_faq {
                   1512:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1513:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1514:     unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
                   1515:     $text = "" if (not defined $text);
                   1516: 	$stayOnPage=1;
                   1517:     $width = 350 if (not defined $width);
                   1518:     $height = 400 if (not defined $height);
                   1519: 
                   1520:     $topic=~s/\W+/\+/g;
                   1521:     my $link='';
                   1522:     my $template='';
                   1523:     my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
                   1524:     if (!$stayOnPage)
                   1525:     {
                   1526: 	$link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1527:     }
                   1528:     else
                   1529:     {
                   1530: 	$link = $url;
                   1531:     }
                   1532: 
                   1533:     # Add the text
                   1534:     if ($text ne "")
                   1535:     {
                   1536: 	$template .= 
1.173     www      1537:   "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705     tempelho 1538:   "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>";
1.172     www      1539:     }
                   1540: 
                   1541:     # Add the graphic
1.179     matthew  1542:     my $title = &mt('View the FAQ');
1.215     albertel 1543:     my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172     www      1544:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1545:  <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172     www      1546: ENDTEMPLATE
                   1547:     if ($text ne '') { $template.='</td></tr></table>' };
                   1548:     return $template;
                   1549: 
1.44      bowersj2 1550: }
1.37      matthew  1551: 
1.180     matthew  1552: ###############################################################
                   1553: ###############################################################
                   1554: 
1.45      matthew  1555: =pod
                   1556: 
1.648     raeburn  1557: =item * &change_content_javascript():
1.256     matthew  1558: 
                   1559: This and the next function allow you to create small sections of an
                   1560: otherwise static HTML page that you can update on the fly with
                   1561: Javascript, even in Netscape 4.
                   1562: 
                   1563: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
                   1564: must be written to the HTML page once. It will prove the Javascript
                   1565: function "change(name, content)". Calling the change function with the
                   1566: name of the section 
                   1567: you want to update, matching the name passed to C<changable_area>, and
                   1568: the new content you want to put in there, will put the content into
                   1569: that area.
                   1570: 
                   1571: B<Note>: Netscape 4 only reserves enough space for the changable area
                   1572: to contain room for the original contents. You need to "make space"
                   1573: for whatever changes you wish to make, and be B<sure> to check your
                   1574: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
                   1575: it's adequate for updating a one-line status display, but little more.
                   1576: This script will set the space to 100% width, so you only need to
                   1577: worry about height in Netscape 4.
                   1578: 
                   1579: Modern browsers are much less limiting, and if you can commit to the
                   1580: user not using Netscape 4, this feature may be used freely with
                   1581: pretty much any HTML.
                   1582: 
                   1583: =cut
                   1584: 
                   1585: sub change_content_javascript {
                   1586:     # If we're on Netscape 4, we need to use Layer-based code
1.258     albertel 1587:     if ($env{'browser.type'} eq 'netscape' &&
                   1588: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1589: 	return (<<NETSCAPE4);
                   1590: 	function change(name, content) {
                   1591: 	    doc = document.layers[name+"___escape"].layers[0].document;
                   1592: 	    doc.open();
                   1593: 	    doc.write(content);
                   1594: 	    doc.close();
                   1595: 	}
                   1596: NETSCAPE4
                   1597:     } else {
                   1598: 	# Otherwise, we need to use semi-standards-compliant code
                   1599: 	# (technically, "innerHTML" isn't standard but the equivalent
                   1600: 	# is really scary, and every useful browser supports it
                   1601: 	return (<<DOMBASED);
                   1602: 	function change(name, content) {
                   1603: 	    element = document.getElementById(name);
                   1604: 	    element.innerHTML = content;
                   1605: 	}
                   1606: DOMBASED
                   1607:     }
                   1608: }
                   1609: 
                   1610: =pod
                   1611: 
1.648     raeburn  1612: =item * &changable_area($name,$origContent):
1.256     matthew  1613: 
                   1614: This provides a "changable area" that can be modified on the fly via
                   1615: the Javascript code provided in C<change_content_javascript>. $name is
                   1616: the name you will use to reference the area later; do not repeat the
                   1617: same name on a given HTML page more then once. $origContent is what
                   1618: the area will originally contain, which can be left blank.
                   1619: 
                   1620: =cut
                   1621: 
                   1622: sub changable_area {
                   1623:     my ($name, $origContent) = @_;
                   1624: 
1.258     albertel 1625:     if ($env{'browser.type'} eq 'netscape' &&
                   1626: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1627: 	# If this is netscape 4, we need to use the Layer tag
                   1628: 	return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
                   1629:     } else {
                   1630: 	return "<span id='$name'>$origContent</span>";
                   1631:     }
                   1632: }
                   1633: 
                   1634: =pod
                   1635: 
1.648     raeburn  1636: =item * &viewport_geometry_js 
1.590     raeburn  1637: 
                   1638: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
                   1639: 
                   1640: =cut
                   1641: 
                   1642: 
                   1643: sub viewport_geometry_js { 
                   1644:     return <<"GEOMETRY";
                   1645: var Geometry = {};
                   1646: function init_geometry() {
                   1647:     if (Geometry.init) { return };
                   1648:     Geometry.init=1;
                   1649:     if (window.innerHeight) {
                   1650:         Geometry.getViewportHeight   = function() { return window.innerHeight; };
                   1651:         Geometry.getViewportWidth   = function() { return window.innerWidth; };
                   1652:         Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
                   1653:         Geometry.getVerticalScroll   = function() { return window.pageYOffset; };
                   1654:     }
                   1655:     else if (document.documentElement && document.documentElement.clientHeight) {
                   1656:         Geometry.getViewportHeight =
                   1657:             function() { return document.documentElement.clientHeight; };
                   1658:         Geometry.getViewportWidth =
                   1659:             function() { return document.documentElement.clientWidth; };
                   1660: 
                   1661:         Geometry.getHorizontalScroll =
                   1662:             function() { return document.documentElement.scrollLeft; };
                   1663:         Geometry.getVerticalScroll =
                   1664:             function() { return document.documentElement.scrollTop; };
                   1665:     }
                   1666:     else if (document.body.clientHeight) {
                   1667:         Geometry.getViewportHeight =
                   1668:             function() { return document.body.clientHeight; };
                   1669:         Geometry.getViewportWidth =
                   1670:             function() { return document.body.clientWidth; };
                   1671:         Geometry.getHorizontalScroll =
                   1672:             function() { return document.body.scrollLeft; };
                   1673:         Geometry.getVerticalScroll =
                   1674:             function() { return document.body.scrollTop; };
                   1675:     }
                   1676: }
                   1677: 
                   1678: GEOMETRY
                   1679: }
                   1680: 
                   1681: =pod
                   1682: 
1.648     raeburn  1683: =item * &viewport_size_js()
1.590     raeburn  1684: 
                   1685: 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. 
                   1686: 
                   1687: =cut
                   1688: 
                   1689: sub viewport_size_js {
                   1690:     my $geometry = &viewport_geometry_js();
                   1691:     return <<"DIMS";
                   1692: 
                   1693: $geometry
                   1694: 
                   1695: function getViewportDims(width,height) {
                   1696:     init_geometry();
                   1697:     width.value = Geometry.getViewportWidth();
                   1698:     height.value = Geometry.getViewportHeight();
                   1699:     return;
                   1700: }
                   1701: 
                   1702: DIMS
                   1703: }
                   1704: 
                   1705: =pod
                   1706: 
1.648     raeburn  1707: =item * &resize_textarea_js()
1.565     albertel 1708: 
                   1709: emits the needed javascript to resize a textarea to be as big as possible
                   1710: 
                   1711: creates a function resize_textrea that takes two IDs first should be
                   1712: the id of the element to resize, second should be the id of a div that
                   1713: surrounds everything that comes after the textarea, this routine needs
                   1714: to be attached to the <body> for the onload and onresize events.
                   1715: 
1.648     raeburn  1716: =back
1.565     albertel 1717: 
                   1718: =cut
                   1719: 
                   1720: sub resize_textarea_js {
1.590     raeburn  1721:     my $geometry = &viewport_geometry_js();
1.565     albertel 1722:     return <<"RESIZE";
                   1723:     <script type="text/javascript">
1.824     bisitz   1724: // <![CDATA[
1.590     raeburn  1725: $geometry
1.565     albertel 1726: 
1.588     albertel 1727: function getX(element) {
                   1728:     var x = 0;
                   1729:     while (element) {
                   1730: 	x += element.offsetLeft;
                   1731: 	element = element.offsetParent;
                   1732:     }
                   1733:     return x;
                   1734: }
                   1735: function getY(element) {
                   1736:     var y = 0;
                   1737:     while (element) {
                   1738: 	y += element.offsetTop;
                   1739: 	element = element.offsetParent;
                   1740:     }
                   1741:     return y;
                   1742: }
                   1743: 
                   1744: 
1.565     albertel 1745: function resize_textarea(textarea_id,bottom_id) {
                   1746:     init_geometry();
                   1747:     var textarea        = document.getElementById(textarea_id);
                   1748:     //alert(textarea);
                   1749: 
1.588     albertel 1750:     var textarea_top    = getY(textarea);
1.565     albertel 1751:     var textarea_height = textarea.offsetHeight;
                   1752:     var bottom          = document.getElementById(bottom_id);
1.588     albertel 1753:     var bottom_top      = getY(bottom);
1.565     albertel 1754:     var bottom_height   = bottom.offsetHeight;
                   1755:     var window_height   = Geometry.getViewportHeight();
1.588     albertel 1756:     var fudge           = 23;
1.565     albertel 1757:     var new_height      = window_height-fudge-textarea_top-bottom_height;
                   1758:     if (new_height < 300) {
                   1759: 	new_height = 300;
                   1760:     }
                   1761:     textarea.style.height=new_height+'px';
                   1762: }
1.824     bisitz   1763: // ]]>
1.565     albertel 1764: </script>
                   1765: RESIZE
                   1766: 
                   1767: }
                   1768: 
1.1205    golterma 1769: sub colorfuleditor_js {
                   1770:     return <<"COLORFULEDIT"
                   1771: <script type="text/javascript">
                   1772: // <![CDATA[>
                   1773:     function fold_box(curDepth, lastresource){
                   1774: 
                   1775:     // we need a list because there can be several blocks you need to fold in one tag
                   1776:         var block = document.getElementsByName('foldblock_'+curDepth);
                   1777:     // but there is only one folding button per tag
                   1778:         var foldbutton = document.getElementById('folding_btn_'+curDepth);
                   1779: 
                   1780:         if(block.item(0).style.display == 'none'){
                   1781: 
                   1782:             foldbutton.value = '@{[&mt("Hide")]}';
                   1783:             for (i = 0; i < block.length; i++){
                   1784:                 block.item(i).style.display = '';
                   1785:             }
                   1786:         }else{
                   1787: 
                   1788:             foldbutton.value = '@{[&mt("Show")]}';
                   1789:             for (i = 0; i < block.length; i++){
                   1790:                 // block.item(i).style.visibility = 'collapse';
                   1791:                 block.item(i).style.display = 'none';
                   1792:             }
                   1793:         };
                   1794:         saveState(lastresource);
                   1795:     }
                   1796: 
                   1797:     function saveState (lastresource) {
                   1798: 
                   1799:         var tag_list = getTagList();
                   1800:         if(tag_list != null){
                   1801:             var timestamp = new Date().getTime();
                   1802:             var key = lastresource;
                   1803: 
                   1804:             // the value pattern is: 'time;key1,value1;key2,value2; ... '
                   1805:             // starting with timestamp
                   1806:             var value = timestamp+';';
                   1807: 
                   1808:             // building the list of key-value pairs
                   1809:             for(var i = 0; i < tag_list.length; i++){
                   1810:                 value += tag_list[i]+',';
                   1811:                 value += document.getElementsByName(tag_list[i])[0].style.display+';';
                   1812:             }
                   1813: 
                   1814:             // only iterate whole storage if nothing to override
                   1815:             if(localStorage.getItem(key) == null){        
                   1816: 
                   1817:                 // prevent storage from growing large
                   1818:                 if(localStorage.length > 50){
                   1819:                     var regex_getTimestamp = /^(?:\d)+;/;
                   1820:                     var oldest_timestamp = regex_getTimestamp.exec(localStorage.key(0));
                   1821:                     var oldest_key;
                   1822:                     
                   1823:                     for(var i = 1; i < localStorage.length; i++){
                   1824:                         if (regex_getTimestamp.exec(localStorage.key(i)) < oldest_timestamp) {
                   1825:                             oldest_key = localStorage.key(i);
                   1826:                             oldest_timestamp = regex_getTimestamp.exec(oldest_key);
                   1827:                         }
                   1828:                     }
                   1829:                     localStorage.removeItem(oldest_key);
                   1830:                 }
                   1831:             }
                   1832:             localStorage.setItem(key,value);
                   1833:         }
                   1834:     }
                   1835: 
                   1836:     // restore folding status of blocks (on page load)
                   1837:     function restoreState (lastresource) {
                   1838:         if(localStorage.getItem(lastresource) != null){
                   1839:             var key = lastresource;
                   1840:             var value = localStorage.getItem(key);
                   1841:             var regex_delTimestamp = /^\d+;/;
                   1842: 
                   1843:             value.replace(regex_delTimestamp, '');
                   1844: 
                   1845:             var valueArr = value.split(';');
                   1846:             var pairs;
                   1847:             var elements;
                   1848:             for (var i = 0; i < valueArr.length; i++){
                   1849:                 pairs = valueArr[i].split(',');
                   1850:                 elements = document.getElementsByName(pairs[0]);
                   1851: 
                   1852:                 for (var j = 0; j < elements.length; j++){  
                   1853:                     elements[j].style.display = pairs[1];
                   1854:                     if (pairs[1] == "none"){
                   1855:                         var regex_id = /([_\\d]+)\$/;
                   1856:                         regex_id.exec(pairs[0]);
                   1857:                         document.getElementById("folding_btn"+RegExp.\$1).value = "Show";
                   1858:                     }
                   1859:                 }
                   1860:             }
                   1861:         }
                   1862:     }
                   1863: 
                   1864:     function getTagList () {
                   1865:         
                   1866:         var stringToSearch = document.lonhomework.innerHTML;
                   1867: 
                   1868:         var ret = new Array();
                   1869:         var regex_findBlock = /(foldblock_.*?)"/g;
                   1870:         var tag_list = stringToSearch.match(regex_findBlock);
                   1871: 
                   1872:         if(tag_list != null){
                   1873:             for(var i = 0; i < tag_list.length; i++){            
                   1874:                 ret.push(tag_list[i].replace(/"/, ''));
                   1875:             }
                   1876:         }
                   1877:         return ret;
                   1878:     }
                   1879: 
                   1880:     function saveScrollPosition (resource) {
                   1881:         var tag_list = getTagList();
                   1882: 
                   1883:         // we dont always want to jump to the first block
                   1884:         // 170 is roughly above the "Problem Editing" header. we just want to save if the user scrolled down further than this
                   1885:         if(\$(window).scrollTop() > 170){
                   1886:             if(tag_list != null){
                   1887:                 var result;
                   1888:                 for(var i = 0; i < tag_list.length; i++){
                   1889:                     if(isElementInViewport(tag_list[i])){
                   1890:                         result += tag_list[i]+';';
                   1891:                     }
                   1892:                 }
                   1893:                 sessionStorage.setItem('anchor_'+resource, result);
                   1894:             }
                   1895:         } else {
                   1896:             // we dont need to save zero, just delete the item to leave everything tidy
                   1897:             sessionStorage.removeItem('anchor_'+resource);
                   1898:         }
                   1899:     }
                   1900: 
                   1901:     function restoreScrollPosition(resource){
                   1902: 
                   1903:         var elem = sessionStorage.getItem('anchor_'+resource);
                   1904:         if(elem != null){
                   1905:             var tag_list = elem.split(';');
                   1906:             var elem_list;
                   1907: 
                   1908:             for(var i = 0; i < tag_list.length; i++){
                   1909:                 elem_list = document.getElementsByName(tag_list[i]);
                   1910:                 
                   1911:                 if(elem_list.length > 0){
                   1912:                     elem = elem_list[0];
                   1913:                     break;
                   1914:                 }
                   1915:             }
                   1916:             elem.scrollIntoView();
                   1917:         }
                   1918:     }
                   1919: 
                   1920:     function isElementInViewport(el) {
                   1921: 
                   1922:         // change to last element instead of first
                   1923:         var elem = document.getElementsByName(el);
                   1924:         var rect = elem[0].getBoundingClientRect();
                   1925: 
                   1926:         return (
                   1927:             rect.top >= 0 &&
                   1928:             rect.left >= 0 &&
                   1929:             rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
                   1930:             rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
                   1931:         );
                   1932:     }
                   1933:     
                   1934:     function autosize(depth){
                   1935:         var cmInst = window['cm'+depth];
                   1936:         var fitsizeButton = document.getElementById('fitsize'+depth);
                   1937: 
                   1938:         // is fixed size, switching to dynamic
                   1939:         if (sessionStorage.getItem("autosized_"+depth) == null) {
                   1940:             cmInst.setSize("","auto");
                   1941:             fitsizeButton.value = "@{[&mt('Fixed size')]}";
                   1942:             sessionStorage.setItem("autosized_"+depth, "yes");
                   1943: 
                   1944:         // is dynamic size, switching to fixed
                   1945:         } else {
                   1946:             cmInst.setSize("","300px");
                   1947:             fitsizeButton.value = "@{[&mt('Dynamic size')]}";
                   1948:             sessionStorage.removeItem("autosized_"+depth);
                   1949:         }
                   1950:     }
                   1951: 
                   1952: 
                   1953: 
                   1954: // ]]>
                   1955: </script>
                   1956: COLORFULEDIT
                   1957: }
                   1958: 
                   1959: sub xmleditor_js {
                   1960:     return <<XMLEDIT
                   1961: <script type="text/javascript" src="/adm/jQuery/addons/jquery-scrolltofixed.js"></script>
                   1962: <script type="text/javascript">
                   1963: // <![CDATA[>
                   1964: 
                   1965:     function saveScrollPosition (resource) {
                   1966: 
                   1967:         var scrollPos = \$(window).scrollTop();
                   1968:         sessionStorage.setItem(resource,scrollPos);
                   1969:     }
                   1970: 
                   1971:     function restoreScrollPosition(resource){
                   1972: 
                   1973:         var scrollPos = sessionStorage.getItem(resource);
                   1974:         \$(window).scrollTop(scrollPos);
                   1975:     }
                   1976: 
                   1977:     // unless internet explorer
                   1978:     if (!(window.navigator.appName == "Microsoft Internet Explorer" && (document.documentMode || document.compatMode))){
                   1979: 
                   1980:         \$(document).ready(function() {
                   1981:              \$(".LC_edit_actionbar").scrollToFixed(\{zIndex: 100\});
                   1982:         });
                   1983:     }
                   1984: 
                   1985:     // inserts text at cursor position into codemirror (xml editor only)
                   1986:     function insertText(text){
                   1987:         cm.focus();
                   1988:         var curPos = cm.getCursor();
                   1989:         cm.replaceRange(text.replace(/ESCAPEDSCRIPT/g,'script'), {line: curPos.line,ch: curPos.ch});
                   1990:     }
                   1991: // ]]>
                   1992: </script>
                   1993: XMLEDIT
                   1994: }
                   1995: 
                   1996: sub insert_folding_button {
                   1997:     my $curDepth = $Apache::lonxml::curdepth;
                   1998:     my $lastresource = $env{'request.ambiguous'};
                   1999: 
                   2000:     return "<input type=\"button\" id=\"folding_btn_$curDepth\" 
                   2001:             value=\"".&mt('Hide')."\" onclick=\"fold_box('$curDepth','$lastresource')\">";
                   2002: }
                   2003: 
1.565     albertel 2004: =pod
                   2005: 
1.256     matthew  2006: =head1 Excel and CSV file utility routines
                   2007: 
                   2008: =cut
                   2009: 
                   2010: ###############################################################
                   2011: ###############################################################
                   2012: 
                   2013: =pod
                   2014: 
1.1162    raeburn  2015: =over 4
                   2016: 
1.648     raeburn  2017: =item * &csv_translate($text) 
1.37      matthew  2018: 
1.185     www      2019: Translate $text to allow it to be output as a 'comma separated values' 
1.37      matthew  2020: format.
                   2021: 
                   2022: =cut
                   2023: 
1.180     matthew  2024: ###############################################################
                   2025: ###############################################################
1.37      matthew  2026: sub csv_translate {
                   2027:     my $text = shift;
                   2028:     $text =~ s/\"/\"\"/g;
1.209     albertel 2029:     $text =~ s/\n/ /g;
1.37      matthew  2030:     return $text;
                   2031: }
1.180     matthew  2032: 
                   2033: ###############################################################
                   2034: ###############################################################
                   2035: 
                   2036: =pod
                   2037: 
1.648     raeburn  2038: =item * &define_excel_formats()
1.180     matthew  2039: 
                   2040: Define some commonly used Excel cell formats.
                   2041: 
                   2042: Currently supported formats:
                   2043: 
                   2044: =over 4
                   2045: 
                   2046: =item header
                   2047: 
                   2048: =item bold
                   2049: 
                   2050: =item h1
                   2051: 
                   2052: =item h2
                   2053: 
                   2054: =item h3
                   2055: 
1.256     matthew  2056: =item h4
                   2057: 
                   2058: =item i
                   2059: 
1.180     matthew  2060: =item date
                   2061: 
                   2062: =back
                   2063: 
                   2064: Inputs: $workbook
                   2065: 
                   2066: Returns: $format, a hash reference.
                   2067: 
1.1057    foxr     2068: 
1.180     matthew  2069: =cut
                   2070: 
                   2071: ###############################################################
                   2072: ###############################################################
                   2073: sub define_excel_formats {
                   2074:     my ($workbook) = @_;
                   2075:     my $format;
                   2076:     $format->{'header'} = $workbook->add_format(bold      => 1, 
                   2077:                                                 bottom    => 1,
                   2078:                                                 align     => 'center');
                   2079:     $format->{'bold'} = $workbook->add_format(bold=>1);
                   2080:     $format->{'h1'}   = $workbook->add_format(bold=>1, size=>18);
                   2081:     $format->{'h2'}   = $workbook->add_format(bold=>1, size=>16);
                   2082:     $format->{'h3'}   = $workbook->add_format(bold=>1, size=>14);
1.255     matthew  2083:     $format->{'h4'}   = $workbook->add_format(bold=>1, size=>12);
1.246     matthew  2084:     $format->{'i'}    = $workbook->add_format(italic=>1);
1.180     matthew  2085:     $format->{'date'} = $workbook->add_format(num_format=>
1.207     matthew  2086:                                             'mm/dd/yyyy hh:mm:ss');
1.180     matthew  2087:     return $format;
                   2088: }
                   2089: 
                   2090: ###############################################################
                   2091: ###############################################################
1.113     bowersj2 2092: 
                   2093: =pod
                   2094: 
1.648     raeburn  2095: =item * &create_workbook()
1.255     matthew  2096: 
                   2097: Create an Excel worksheet.  If it fails, output message on the
                   2098: request object and return undefs.
                   2099: 
                   2100: Inputs: Apache request object
                   2101: 
                   2102: Returns (undef) on failure, 
                   2103:     Excel worksheet object, scalar with filename, and formats 
                   2104:     from &Apache::loncommon::define_excel_formats on success
                   2105: 
                   2106: =cut
                   2107: 
                   2108: ###############################################################
                   2109: ###############################################################
                   2110: sub create_workbook {
                   2111:     my ($r) = @_;
                   2112:         #
                   2113:     # Create the excel spreadsheet
                   2114:     my $filename = '/prtspool/'.
1.258     albertel 2115:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255     matthew  2116:         time.'_'.rand(1000000000).'.xls';
                   2117:     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
                   2118:     if (! defined($workbook)) {
                   2119:         $r->log_error("Error creating excel spreadsheet $filename: $!");
1.928     bisitz   2120:         $r->print(
                   2121:             '<p class="LC_error">'
                   2122:            .&mt('Problems occurred in creating the new Excel file.')
                   2123:            .' '.&mt('This error has been logged.')
                   2124:            .' '.&mt('Please alert your LON-CAPA administrator.')
                   2125:            .'</p>'
                   2126:         );
1.255     matthew  2127:         return (undef);
                   2128:     }
                   2129:     #
1.1014    foxr     2130:     $workbook->set_tempdir(LONCAPA::tempdir());
1.255     matthew  2131:     #
                   2132:     my $format = &Apache::loncommon::define_excel_formats($workbook);
                   2133:     return ($workbook,$filename,$format);
                   2134: }
                   2135: 
                   2136: ###############################################################
                   2137: ###############################################################
                   2138: 
                   2139: =pod
                   2140: 
1.648     raeburn  2141: =item * &create_text_file()
1.113     bowersj2 2142: 
1.542     raeburn  2143: Create a file to write to and eventually make available to the user.
1.256     matthew  2144: If file creation fails, outputs an error message on the request object and 
                   2145: return undefs.
1.113     bowersj2 2146: 
1.256     matthew  2147: Inputs: Apache request object, and file suffix
1.113     bowersj2 2148: 
1.256     matthew  2149: Returns (undef) on failure, 
                   2150:     Filehandle and filename on success.
1.113     bowersj2 2151: 
                   2152: =cut
                   2153: 
1.256     matthew  2154: ###############################################################
                   2155: ###############################################################
                   2156: sub create_text_file {
                   2157:     my ($r,$suffix) = @_;
                   2158:     if (! defined($suffix)) { $suffix = 'txt'; };
                   2159:     my $fh;
                   2160:     my $filename = '/prtspool/'.
1.258     albertel 2161:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256     matthew  2162:         time.'_'.rand(1000000000).'.'.$suffix;
                   2163:     $fh = Apache::File->new('>/home/httpd'.$filename);
                   2164:     if (! defined($fh)) {
                   2165:         $r->log_error("Couldn't open $filename for output $!");
1.928     bisitz   2166:         $r->print(
                   2167:             '<p class="LC_error">'
                   2168:            .&mt('Problems occurred in creating the output file.')
                   2169:            .' '.&mt('This error has been logged.')
                   2170:            .' '.&mt('Please alert your LON-CAPA administrator.')
                   2171:            .'</p>'
                   2172:         );
1.113     bowersj2 2173:     }
1.256     matthew  2174:     return ($fh,$filename)
1.113     bowersj2 2175: }
                   2176: 
                   2177: 
1.256     matthew  2178: =pod 
1.113     bowersj2 2179: 
                   2180: =back
                   2181: 
                   2182: =cut
1.37      matthew  2183: 
                   2184: ###############################################################
1.33      matthew  2185: ##        Home server <option> list generating code          ##
                   2186: ###############################################################
1.35      matthew  2187: 
1.169     www      2188: # ------------------------------------------
                   2189: 
                   2190: sub domain_select {
                   2191:     my ($name,$value,$multiple)=@_;
                   2192:     my %domains=map { 
1.514     albertel 2193: 	$_ => $_.' '. &Apache::lonnet::domain($_,'description') 
1.512     albertel 2194:     } &Apache::lonnet::all_domains();
1.169     www      2195:     if ($multiple) {
                   2196: 	$domains{''}=&mt('Any domain');
1.550     albertel 2197: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287     albertel 2198: 	return &multiple_select_form($name,$value,4,\%domains);
1.169     www      2199:     } else {
1.550     albertel 2200: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.970     raeburn  2201: 	return &select_form($name,$value,\%domains);
1.169     www      2202:     }
                   2203: }
                   2204: 
1.282     albertel 2205: #-------------------------------------------
                   2206: 
                   2207: =pod
                   2208: 
1.519     raeburn  2209: =head1 Routines for form select boxes
                   2210: 
                   2211: =over 4
                   2212: 
1.648     raeburn  2213: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282     albertel 2214: 
                   2215: Returns a string containing a <select> element int multiple mode
                   2216: 
                   2217: 
                   2218: Args:
                   2219:   $name - name of the <select> element
1.506     raeburn  2220:   $value - scalar or array ref of values that should already be selected
1.282     albertel 2221:   $size - number of rows long the select element is
1.283     albertel 2222:   $hash - the elements should be 'option' => 'shown text'
1.282     albertel 2223:           (shown text should already have been &mt())
1.506     raeburn  2224:   $order - (optional) array ref of the order to show the elements in
1.283     albertel 2225: 
1.282     albertel 2226: =cut
                   2227: 
                   2228: #-------------------------------------------
1.169     www      2229: sub multiple_select_form {
1.284     albertel 2230:     my ($name,$value,$size,$hash,$order)=@_;
1.169     www      2231:     my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
                   2232:     my $output='';
1.191     matthew  2233:     if (! defined($size)) {
                   2234:         $size = 4;
1.283     albertel 2235:         if (scalar(keys(%$hash))<4) {
                   2236:             $size = scalar(keys(%$hash));
1.191     matthew  2237:         }
                   2238:     }
1.734     bisitz   2239:     $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple">';
1.501     banghart 2240:     my @order;
1.506     raeburn  2241:     if (ref($order) eq 'ARRAY')  {
                   2242:         @order = @{$order};
                   2243:     } else {
                   2244:         @order = sort(keys(%$hash));
1.501     banghart 2245:     }
                   2246:     if (exists($$hash{'select_form_order'})) {
                   2247:         @order = @{$$hash{'select_form_order'}};
                   2248:     }
                   2249:         
1.284     albertel 2250:     foreach my $key (@order) {
1.356     albertel 2251:         $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284     albertel 2252:         $output.='selected="selected" ' if ($selected{$key});
                   2253:         $output.='>'.$hash->{$key}."</option>\n";
1.169     www      2254:     }
                   2255:     $output.="</select>\n";
                   2256:     return $output;
                   2257: }
                   2258: 
1.88      www      2259: #-------------------------------------------
                   2260: 
                   2261: =pod
                   2262: 
1.970     raeburn  2263: =item * &select_form($defdom,$name,$hashref,$onchange)
1.88      www      2264: 
                   2265: Returns a string containing a <select name='$name' size='1'> form to 
1.970     raeburn  2266: allow a user to select options from a ref to a hash containing:
                   2267: option_name => displayed text. An optional $onchange can include
                   2268: a javascript onchange item, e.g., onchange="this.form.submit();"  
                   2269: 
1.88      www      2270: See lonrights.pm for an example invocation and use.
                   2271: 
                   2272: =cut
                   2273: 
                   2274: #-------------------------------------------
                   2275: sub select_form {
1.1228    raeburn  2276:     my ($def,$name,$hashref,$onchange,$readonly) = @_;
1.970     raeburn  2277:     return unless (ref($hashref) eq 'HASH');
                   2278:     if ($onchange) {
                   2279:         $onchange = ' onchange="'.$onchange.'"';
                   2280:     }
1.1228    raeburn  2281:     my $disabled;
                   2282:     if ($readonly) {
                   2283:         $disabled = ' disabled="disabled"';
                   2284:     }
                   2285:     my $selectform = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";
1.128     albertel 2286:     my @keys;
1.970     raeburn  2287:     if (exists($hashref->{'select_form_order'})) {
                   2288: 	@keys=@{$hashref->{'select_form_order'}};
1.128     albertel 2289:     } else {
1.970     raeburn  2290: 	@keys=sort(keys(%{$hashref}));
1.128     albertel 2291:     }
1.356     albertel 2292:     foreach my $key (@keys) {
                   2293:         $selectform.=
                   2294: 	    '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
                   2295:             ($key eq $def ? 'selected="selected" ' : '').
1.970     raeburn  2296:                 ">".$hashref->{$key}."</option>\n";
1.88      www      2297:     }
                   2298:     $selectform.="</select>";
                   2299:     return $selectform;
                   2300: }
                   2301: 
1.475     www      2302: # For display filters
                   2303: 
                   2304: sub display_filter {
1.1074    raeburn  2305:     my ($context) = @_;
1.475     www      2306:     if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477     www      2307:     if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.1074    raeburn  2308:     my $phraseinput = 'hidden';
                   2309:     my $includeinput = 'hidden';
                   2310:     my ($checked,$includetypestext);
                   2311:     if ($env{'form.displayfilter'} eq 'containing') {
                   2312:         $phraseinput = 'text'; 
                   2313:         if ($context eq 'parmslog') {
                   2314:             $includeinput = 'checkbox';
                   2315:             if ($env{'form.includetypes'}) {
                   2316:                 $checked = ' checked="checked"';
                   2317:             }
                   2318:             $includetypestext = &mt('Include parameter types');
                   2319:         }
                   2320:     } else {
                   2321:         $includetypestext = '&nbsp;';
                   2322:     }
                   2323:     my ($additional,$secondid,$thirdid);
                   2324:     if ($context eq 'parmslog') {
                   2325:         $additional = 
                   2326:             '<label><input type="'.$includeinput.'" name="includetypes"'. 
                   2327:             $checked.' name="includetypes" value="1" id="includetypes" />'.
                   2328:             '&nbsp;<span id="includetypestext">'.$includetypestext.'</span>'.
                   2329:             '</label>';
                   2330:         $secondid = 'includetypes';
                   2331:         $thirdid = 'includetypestext';
                   2332:     }
                   2333:     my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
                   2334:                                                     '$secondid','$thirdid')";
                   2335:     return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',
1.475     www      2336: 			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
                   2337: 							   (&mt('all'),10,20,50,100,1000,10000))).
1.714     bisitz   2338: 	   '</label></span> <span class="LC_nobreak">'.
1.1074    raeburn  2339:            &mt('Filter: [_1]',
1.477     www      2340: 	   &select_form($env{'form.displayfilter'},
                   2341: 			'displayfilter',
1.970     raeburn  2342: 			{'currentfolder' => 'Current folder/page',
1.477     www      2343: 			 'containing' => 'Containing phrase',
1.1074    raeburn  2344: 			 'none' => 'None'},$onchange)).'&nbsp;'.
                   2345: 			 '<input type="'.$phraseinput.'" name="containingphrase" id="containingphrase" size="30" value="'.
                   2346:                          &HTML::Entities::encode($env{'form.containingphrase'}).
                   2347:                          '" />'.$additional;
                   2348: }
                   2349: 
                   2350: sub display_filter_js {
                   2351:     my $includetext = &mt('Include parameter types');
                   2352:     return <<"ENDJS";
                   2353:   
                   2354: function toggleHistoryOptions(setter,firstid,context,secondid,thirdid) {
                   2355:     var firstType = 'hidden';
                   2356:     if (setter.options[setter.selectedIndex].value == 'containing') {
                   2357:         firstType = 'text';
                   2358:     }
                   2359:     firstObject = document.getElementById(firstid);
                   2360:     if (typeof(firstObject) == 'object') {
                   2361:         if (firstObject.type != firstType) {
                   2362:             changeInputType(firstObject,firstType);
                   2363:         }
                   2364:     }
                   2365:     if (context == 'parmslog') {
                   2366:         var secondType = 'hidden';
                   2367:         if (firstType == 'text') {
                   2368:             secondType = 'checkbox';
                   2369:         }
                   2370:         secondObject = document.getElementById(secondid);  
                   2371:         if (typeof(secondObject) == 'object') {
                   2372:             if (secondObject.type != secondType) {
                   2373:                 changeInputType(secondObject,secondType);
                   2374:             }
                   2375:         }
                   2376:         var textItem = document.getElementById(thirdid);
                   2377:         var currtext = textItem.innerHTML;
                   2378:         var newtext;
                   2379:         if (firstType == 'text') {
                   2380:             newtext = '$includetext';
                   2381:         } else {
                   2382:             newtext = '&nbsp;';
                   2383:         }
                   2384:         if (currtext != newtext) {
                   2385:             textItem.innerHTML = newtext;
                   2386:         }
                   2387:     }
                   2388:     return;
                   2389: }
                   2390: 
                   2391: function changeInputType(oldObject,newType) {
                   2392:     var newObject = document.createElement('input');
                   2393:     newObject.type = newType;
                   2394:     if (oldObject.size) {
                   2395:         newObject.size = oldObject.size;
                   2396:     }
                   2397:     if (oldObject.value) {
                   2398:         newObject.value = oldObject.value;
                   2399:     }
                   2400:     if (oldObject.name) {
                   2401:         newObject.name = oldObject.name;
                   2402:     }
                   2403:     if (oldObject.id) {
                   2404:         newObject.id = oldObject.id;
                   2405:     }
                   2406:     oldObject.parentNode.replaceChild(newObject,oldObject);
                   2407:     return;
                   2408: }
                   2409: 
                   2410: ENDJS
1.475     www      2411: }
                   2412: 
1.167     www      2413: sub gradeleveldescription {
                   2414:     my $gradelevel=shift;
                   2415:     my %gradelevels=(0 => 'Not specified',
                   2416: 		     1 => 'Grade 1',
                   2417: 		     2 => 'Grade 2',
                   2418: 		     3 => 'Grade 3',
                   2419: 		     4 => 'Grade 4',
                   2420: 		     5 => 'Grade 5',
                   2421: 		     6 => 'Grade 6',
                   2422: 		     7 => 'Grade 7',
                   2423: 		     8 => 'Grade 8',
                   2424: 		     9 => 'Grade 9',
                   2425: 		     10 => 'Grade 10',
                   2426: 		     11 => 'Grade 11',
                   2427: 		     12 => 'Grade 12',
                   2428: 		     13 => 'Grade 13',
                   2429: 		     14 => '100 Level',
                   2430: 		     15 => '200 Level',
                   2431: 		     16 => '300 Level',
                   2432: 		     17 => '400 Level',
                   2433: 		     18 => 'Graduate Level');
                   2434:     return &mt($gradelevels{$gradelevel});
                   2435: }
                   2436: 
1.163     www      2437: sub select_level_form {
                   2438:     my ($deflevel,$name)=@_;
                   2439:     unless ($deflevel) { $deflevel=0; }
1.167     www      2440:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
                   2441:     for (my $i=0; $i<=18; $i++) {
                   2442:         $selectform.="<option value=\"$i\" ".
1.253     albertel 2443:             ($i==$deflevel ? 'selected="selected" ' : '').
1.167     www      2444:                 ">".&gradeleveldescription($i)."</option>\n";
                   2445:     }
                   2446:     $selectform.="</select>";
                   2447:     return $selectform;
1.163     www      2448: }
1.167     www      2449: 
1.35      matthew  2450: #-------------------------------------------
                   2451: 
1.45      matthew  2452: =pod
                   2453: 
1.1121    raeburn  2454: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms)
1.35      matthew  2455: 
                   2456: Returns a string containing a <select name='$name' size='1'> form to 
                   2457: allow a user to select the domain to preform an operation in.  
                   2458: See loncreateuser.pm for an example invocation and use.
                   2459: 
1.90      www      2460: If the $includeempty flag is set, it also includes an empty choice ("no domain
                   2461: selected");
                   2462: 
1.743     raeburn  2463: If the $showdomdesc flag is set, the domain name is followed by the domain description.
                   2464: 
1.910     raeburn  2465: The optional $onchange argument specifies what should occur if the domain selector is changed, e.g., 'this.form.submit()' if the form is to be automatically submitted.
                   2466: 
1.1121    raeburn  2467: The optional $incdoms is a reference to an array of domains which will be the only available options.
                   2468: 
                   2469: The optional $excdoms is a reference to an array of domains which will be excluded from the available options.
1.563     raeburn  2470: 
1.35      matthew  2471: =cut
                   2472: 
                   2473: #-------------------------------------------
1.34      matthew  2474: sub select_dom_form {
1.1121    raeburn  2475:     my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms) = @_;
1.872     raeburn  2476:     if ($onchange) {
1.874     raeburn  2477:         $onchange = ' onchange="'.$onchange.'"';
1.743     raeburn  2478:     }
1.1121    raeburn  2479:     my (@domains,%exclude);
1.910     raeburn  2480:     if (ref($incdoms) eq 'ARRAY') {
                   2481:         @domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
                   2482:     } else {
                   2483:         @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
                   2484:     }
1.90      www      2485:     if ($includeempty) { @domains=('',@domains); }
1.1121    raeburn  2486:     if (ref($excdoms) eq 'ARRAY') {
                   2487:         map { $exclude{$_} = 1; } @{$excdoms}; 
                   2488:     }
1.743     raeburn  2489:     my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange>\n";
1.356     albertel 2490:     foreach my $dom (@domains) {
1.1121    raeburn  2491:         next if ($exclude{$dom});
1.356     albertel 2492:         $selectdomain.="<option value=\"$dom\" ".
1.563     raeburn  2493:             ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
                   2494:         if ($showdomdesc) {
                   2495:             if ($dom ne '') {
                   2496:                 my $domdesc = &Apache::lonnet::domain($dom,'description');
                   2497:                 if ($domdesc ne '') {
                   2498:                     $selectdomain .= ' ('.$domdesc.')';
                   2499:                 }
                   2500:             } 
                   2501:         }
                   2502:         $selectdomain .= "</option>\n";
1.34      matthew  2503:     }
                   2504:     $selectdomain.="</select>";
                   2505:     return $selectdomain;
                   2506: }
                   2507: 
1.35      matthew  2508: #-------------------------------------------
                   2509: 
1.45      matthew  2510: =pod
                   2511: 
1.648     raeburn  2512: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35      matthew  2513: 
1.586     raeburn  2514: input: 4 arguments (two required, two optional) - 
                   2515:     $domain - domain of new user
                   2516:     $name - name of form element
                   2517:     $default - Value of 'default' causes a default item to be first 
                   2518:                             option, and selected by default. 
                   2519:     $hide - Value of 'hide' causes hiding of the name of the server, 
                   2520:                             if 1 server found, or default, if 0 found.
1.594     raeburn  2521: output: returns 2 items: 
1.586     raeburn  2522: (a) form element which contains either:
                   2523:    (i) <select name="$name">
                   2524:         <option value="$hostid1">$hostid $servers{$hostid}</option>
                   2525:         <option value="$hostid2">$hostid $servers{$hostid}</option>       
                   2526:        </select>
                   2527:        form item if there are multiple library servers in $domain, or
                   2528:    (ii) an <input type="hidden" name="$name" value="$hostid" /> form item 
                   2529:        if there is only one library server in $domain.
                   2530: 
                   2531: (b) number of library servers found.
                   2532: 
                   2533: See loncreateuser.pm for example of use.
1.35      matthew  2534: 
                   2535: =cut
                   2536: 
                   2537: #-------------------------------------------
1.586     raeburn  2538: sub home_server_form_item {
                   2539:     my ($domain,$name,$default,$hide) = @_;
1.513     albertel 2540:     my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586     raeburn  2541:     my $result;
                   2542:     my $numlib = keys(%servers);
                   2543:     if ($numlib > 1) {
                   2544:         $result .= '<select name="'.$name.'" />'."\n";
                   2545:         if ($default) {
1.804     bisitz   2546:             $result .= '<option value="default" selected="selected">'.&mt('default').
1.586     raeburn  2547:                        '</option>'."\n";
                   2548:         }
                   2549:         foreach my $hostid (sort(keys(%servers))) {
                   2550:             $result.= '<option value="'.$hostid.'">'.
                   2551: 	              $hostid.' '.$servers{$hostid}."</option>\n";
                   2552:         }
                   2553:         $result .= '</select>'."\n";
                   2554:     } elsif ($numlib == 1) {
                   2555:         my $hostid;
                   2556:         foreach my $item (keys(%servers)) {
                   2557:             $hostid = $item;
                   2558:         }
                   2559:         $result .= '<input type="hidden" name="'.$name.'" value="'.
                   2560:                    $hostid.'" />';
                   2561:                    if (!$hide) {
                   2562:                        $result .= $hostid.' '.$servers{$hostid};
                   2563:                    }
                   2564:                    $result .= "\n";
                   2565:     } elsif ($default) {
                   2566:         $result .= '<input type="hidden" name="'.$name.
                   2567:                    '" value="default" />';
                   2568:                    if (!$hide) {
                   2569:                        $result .= &mt('default');
                   2570:                    }
                   2571:                    $result .= "\n";
1.33      matthew  2572:     }
1.586     raeburn  2573:     return ($result,$numlib);
1.33      matthew  2574: }
1.112     bowersj2 2575: 
                   2576: =pod
                   2577: 
1.534     albertel 2578: =back 
                   2579: 
1.112     bowersj2 2580: =cut
1.87      matthew  2581: 
                   2582: ###############################################################
1.112     bowersj2 2583: ##                  Decoding User Agent                      ##
1.87      matthew  2584: ###############################################################
                   2585: 
                   2586: =pod
                   2587: 
1.112     bowersj2 2588: =head1 Decoding the User Agent
                   2589: 
                   2590: =over 4
                   2591: 
                   2592: =item * &decode_user_agent()
1.87      matthew  2593: 
                   2594: Inputs: $r
                   2595: 
                   2596: Outputs:
                   2597: 
                   2598: =over 4
                   2599: 
1.112     bowersj2 2600: =item * $httpbrowser
1.87      matthew  2601: 
1.112     bowersj2 2602: =item * $clientbrowser
1.87      matthew  2603: 
1.112     bowersj2 2604: =item * $clientversion
1.87      matthew  2605: 
1.112     bowersj2 2606: =item * $clientmathml
1.87      matthew  2607: 
1.112     bowersj2 2608: =item * $clientunicode
1.87      matthew  2609: 
1.112     bowersj2 2610: =item * $clientos
1.87      matthew  2611: 
1.1137    raeburn  2612: =item * $clientmobile
                   2613: 
1.1141    raeburn  2614: =item * $clientinfo
                   2615: 
1.1194    raeburn  2616: =item * $clientosversion
                   2617: 
1.87      matthew  2618: =back
                   2619: 
1.157     matthew  2620: =back 
                   2621: 
1.87      matthew  2622: =cut
                   2623: 
                   2624: ###############################################################
                   2625: ###############################################################
                   2626: sub decode_user_agent {
1.247     albertel 2627:     my ($r)=@_;
1.87      matthew  2628:     my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
                   2629:     my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
                   2630:     my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247     albertel 2631:     if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87      matthew  2632:     my $clientbrowser='unknown';
                   2633:     my $clientversion='0';
                   2634:     my $clientmathml='';
                   2635:     my $clientunicode='0';
1.1137    raeburn  2636:     my $clientmobile=0;
1.1194    raeburn  2637:     my $clientosversion='';
1.87      matthew  2638:     for (my $i=0;$i<=$#browsertype;$i++) {
1.1193    raeburn  2639:         my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\%/,$browsertype[$i]);
1.87      matthew  2640: 	if (($httpbrowser=~/$match/i)  && ($httpbrowser!~/$notmatch/i)) {
                   2641: 	    $clientbrowser=$bname;
                   2642:             $httpbrowser=~/$vreg/i;
                   2643: 	    $clientversion=$1;
                   2644:             $clientmathml=($clientversion>=$minv);
                   2645:             $clientunicode=($clientversion>=$univ);
                   2646: 	}
                   2647:     }
                   2648:     my $clientos='unknown';
1.1141    raeburn  2649:     my $clientinfo;
1.87      matthew  2650:     if (($httpbrowser=~/linux/i) ||
                   2651:         ($httpbrowser=~/unix/i) ||
                   2652:         ($httpbrowser=~/ux/i) ||
                   2653:         ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
                   2654:     if (($httpbrowser=~/vax/i) ||
                   2655:         ($httpbrowser=~/vms/i)) { $clientos='vms'; }
                   2656:     if ($httpbrowser=~/next/i) { $clientos='next'; }
                   2657:     if (($httpbrowser=~/mac/i) ||
                   2658:         ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
1.1194    raeburn  2659:     if ($httpbrowser=~/win/i) {
                   2660:         $clientos='win';
                   2661:         if ($httpbrowser =~/Windows\s+NT\s+(\d+\.\d+)/i) {
                   2662:             $clientosversion = $1;
                   2663:         }
                   2664:     }
1.87      matthew  2665:     if ($httpbrowser=~/embed/i) { $clientos='pda'; }
1.1137    raeburn  2666:     if ($httpbrowser=~/(Android|iPod|iPad|iPhone|webOS|Blackberry|Windows Phone|Opera m(?:ob|in)|Fennec)/i) {
                   2667:         $clientmobile=lc($1);
                   2668:     }
1.1141    raeburn  2669:     if ($httpbrowser=~ m{Firefox/(\d+\.\d+)}) {
                   2670:         $clientinfo = 'firefox-'.$1;
                   2671:     } elsif ($httpbrowser=~ m{chromeframe/(\d+\.\d+)\.}) {
                   2672:         $clientinfo = 'chromeframe-'.$1;
                   2673:     }
1.87      matthew  2674:     return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
1.1194    raeburn  2675:             $clientunicode,$clientos,$clientmobile,$clientinfo,
                   2676:             $clientosversion);
1.87      matthew  2677: }
                   2678: 
1.32      matthew  2679: ###############################################################
                   2680: ##    Authentication changing form generation subroutines    ##
                   2681: ###############################################################
                   2682: ##
                   2683: ## All of the authform_xxxxxxx subroutines take their inputs in a
                   2684: ## hash, and have reasonable default values.
                   2685: ##
                   2686: ##    formname = the name given in the <form> tag.
1.35      matthew  2687: #-------------------------------------------
                   2688: 
1.45      matthew  2689: =pod
                   2690: 
1.112     bowersj2 2691: =head1 Authentication Routines
                   2692: 
                   2693: =over 4
                   2694: 
1.648     raeburn  2695: =item * &authform_xxxxxx()
1.35      matthew  2696: 
                   2697: The authform_xxxxxx subroutines provide javascript and html forms which 
                   2698: handle some of the conveniences required for authentication forms.  
                   2699: This is not an optimal method, but it works.  
                   2700: 
                   2701: =over 4
                   2702: 
1.112     bowersj2 2703: =item * authform_header
1.35      matthew  2704: 
1.112     bowersj2 2705: =item * authform_authorwarning
1.35      matthew  2706: 
1.112     bowersj2 2707: =item * authform_nochange
1.35      matthew  2708: 
1.112     bowersj2 2709: =item * authform_kerberos
1.35      matthew  2710: 
1.112     bowersj2 2711: =item * authform_internal
1.35      matthew  2712: 
1.112     bowersj2 2713: =item * authform_filesystem
1.35      matthew  2714: 
                   2715: =back
                   2716: 
1.648     raeburn  2717: See loncreateuser.pm for invocation and use examples.
1.157     matthew  2718: 
1.35      matthew  2719: =cut
                   2720: 
                   2721: #-------------------------------------------
1.32      matthew  2722: sub authform_header{  
                   2723:     my %in = (
                   2724:         formname => 'cu',
1.80      albertel 2725:         kerb_def_dom => '',
1.32      matthew  2726:         @_,
                   2727:     );
                   2728:     $in{'formname'} = 'document.' . $in{'formname'};
                   2729:     my $result='';
1.80      albertel 2730: 
                   2731: #---------------------------------------------- Code for upper case translation
                   2732:     my $Javascript_toUpperCase;
                   2733:     unless ($in{kerb_def_dom}) {
                   2734:         $Javascript_toUpperCase =<<"END";
                   2735:         switch (choice) {
                   2736:            case 'krb': currentform.elements[choicearg].value =
                   2737:                currentform.elements[choicearg].value.toUpperCase();
                   2738:                break;
                   2739:            default:
                   2740:         }
                   2741: END
                   2742:     } else {
                   2743:         $Javascript_toUpperCase = "";
                   2744:     }
                   2745: 
1.165     raeburn  2746:     my $radioval = "'nochange'";
1.591     raeburn  2747:     if (defined($in{'curr_authtype'})) {
                   2748:         if ($in{'curr_authtype'} ne '') {
                   2749:             $radioval = "'".$in{'curr_authtype'}."arg'";
                   2750:         }
1.174     matthew  2751:     }
1.165     raeburn  2752:     my $argfield = 'null';
1.591     raeburn  2753:     if (defined($in{'mode'})) {
1.165     raeburn  2754:         if ($in{'mode'} eq 'modifycourse')  {
1.591     raeburn  2755:             if (defined($in{'curr_autharg'})) {
                   2756:                 if ($in{'curr_autharg'} ne '') {
1.165     raeburn  2757:                     $argfield = "'$in{'curr_autharg'}'";
                   2758:                 }
                   2759:             }
                   2760:         }
                   2761:     }
                   2762: 
1.32      matthew  2763:     $result.=<<"END";
                   2764: var current = new Object();
1.165     raeburn  2765: current.radiovalue = $radioval;
                   2766: current.argfield = $argfield;
1.32      matthew  2767: 
                   2768: function changed_radio(choice,currentform) {
                   2769:     var choicearg = choice + 'arg';
                   2770:     // If a radio button in changed, we need to change the argfield
                   2771:     if (current.radiovalue != choice) {
                   2772:         current.radiovalue = choice;
                   2773:         if (current.argfield != null) {
                   2774:             currentform.elements[current.argfield].value = '';
                   2775:         }
                   2776:         if (choice == 'nochange') {
                   2777:             current.argfield = null;
                   2778:         } else {
                   2779:             current.argfield = choicearg;
                   2780:             switch(choice) {
                   2781:                 case 'krb': 
                   2782:                     currentform.elements[current.argfield].value = 
                   2783:                         "$in{'kerb_def_dom'}";
                   2784:                 break;
                   2785:               default:
                   2786:                 break;
                   2787:             }
                   2788:         }
                   2789:     }
                   2790:     return;
                   2791: }
1.22      www      2792: 
1.32      matthew  2793: function changed_text(choice,currentform) {
                   2794:     var choicearg = choice + 'arg';
                   2795:     if (currentform.elements[choicearg].value !='') {
1.80      albertel 2796:         $Javascript_toUpperCase
1.32      matthew  2797:         // clear old field
                   2798:         if ((current.argfield != choicearg) && (current.argfield != null)) {
                   2799:             currentform.elements[current.argfield].value = '';
                   2800:         }
                   2801:         current.argfield = choicearg;
                   2802:     }
                   2803:     set_auth_radio_buttons(choice,currentform);
                   2804:     return;
1.20      www      2805: }
1.32      matthew  2806: 
                   2807: function set_auth_radio_buttons(newvalue,currentform) {
1.986     raeburn  2808:     var numauthchoices = currentform.login.length;
                   2809:     if (typeof numauthchoices  == "undefined") {
                   2810:         return;
                   2811:     } 
1.32      matthew  2812:     var i=0;
1.986     raeburn  2813:     while (i < numauthchoices) {
1.32      matthew  2814:         if (currentform.login[i].value == newvalue) { break; }
                   2815:         i++;
                   2816:     }
1.986     raeburn  2817:     if (i == numauthchoices) {
1.32      matthew  2818:         return;
                   2819:     }
                   2820:     current.radiovalue = newvalue;
                   2821:     currentform.login[i].checked = true;
                   2822:     return;
                   2823: }
                   2824: END
                   2825:     return $result;
                   2826: }
                   2827: 
1.1106    raeburn  2828: sub authform_authorwarning {
1.32      matthew  2829:     my $result='';
1.144     matthew  2830:     $result='<i>'.
                   2831:         &mt('As a general rule, only authors or co-authors should be '.
                   2832:             'filesystem authenticated '.
                   2833:             '(which allows access to the server filesystem).')."</i>\n";
1.32      matthew  2834:     return $result;
                   2835: }
                   2836: 
1.1106    raeburn  2837: sub authform_nochange {
1.32      matthew  2838:     my %in = (
                   2839:               formname => 'document.cu',
                   2840:               kerb_def_dom => 'MSU.EDU',
                   2841:               @_,
                   2842:           );
1.1106    raeburn  2843:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.586     raeburn  2844:     my $result;
1.1104    raeburn  2845:     if (!$authnum) {
1.1105    raeburn  2846:         $result = &mt('Under your current role you are not permitted to change login settings for this user');
1.586     raeburn  2847:     } else {
                   2848:         $result = '<label>'.&mt('[_1] Do not change login data',
                   2849:                   '<input type="radio" name="login" value="nochange" '.
                   2850:                   'checked="checked" onclick="'.
1.281     albertel 2851:             "javascript:changed_radio('nochange',$in{'formname'});".'" />').
                   2852: 	    '</label>';
1.586     raeburn  2853:     }
1.32      matthew  2854:     return $result;
                   2855: }
                   2856: 
1.591     raeburn  2857: sub authform_kerberos {
1.32      matthew  2858:     my %in = (
                   2859:               formname => 'document.cu',
                   2860:               kerb_def_dom => 'MSU.EDU',
1.80      albertel 2861:               kerb_def_auth => 'krb4',
1.32      matthew  2862:               @_,
                   2863:               );
1.586     raeburn  2864:     my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
                   2865:         $autharg,$jscall);
1.1106    raeburn  2866:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.80      albertel 2867:     if ($in{'kerb_def_auth'} eq 'krb5') {
1.772     bisitz   2868:        $check5 = ' checked="checked"';
1.80      albertel 2869:     } else {
1.772     bisitz   2870:        $check4 = ' checked="checked"';
1.80      albertel 2871:     }
1.165     raeburn  2872:     $krbarg = $in{'kerb_def_dom'};
1.591     raeburn  2873:     if (defined($in{'curr_authtype'})) {
                   2874:         if ($in{'curr_authtype'} eq 'krb') {
1.772     bisitz   2875:             $krbcheck = ' checked="checked"';
1.623     raeburn  2876:             if (defined($in{'mode'})) {
                   2877:                 if ($in{'mode'} eq 'modifyuser') {
                   2878:                     $krbcheck = '';
                   2879:                 }
                   2880:             }
1.591     raeburn  2881:             if (defined($in{'curr_kerb_ver'})) {
                   2882:                 if ($in{'curr_krb_ver'} eq '5') {
1.772     bisitz   2883:                     $check5 = ' checked="checked"';
1.591     raeburn  2884:                     $check4 = '';
                   2885:                 } else {
1.772     bisitz   2886:                     $check4 = ' checked="checked"';
1.591     raeburn  2887:                     $check5 = '';
                   2888:                 }
1.586     raeburn  2889:             }
1.591     raeburn  2890:             if (defined($in{'curr_autharg'})) {
1.165     raeburn  2891:                 $krbarg = $in{'curr_autharg'};
                   2892:             }
1.586     raeburn  2893:             if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591     raeburn  2894:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2895:                     $result = 
                   2896:     &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
                   2897:         $in{'curr_autharg'},$krbver);
                   2898:                 } else {
                   2899:                     $result =
                   2900:     &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
                   2901:                 }
                   2902:                 return $result; 
                   2903:             }
                   2904:         }
                   2905:     } else {
                   2906:         if ($authnum == 1) {
1.784     bisitz   2907:             $authtype = '<input type="hidden" name="login" value="krb" />';
1.165     raeburn  2908:         }
                   2909:     }
1.586     raeburn  2910:     if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
                   2911:         return;
1.587     raeburn  2912:     } elsif ($authtype eq '') {
1.591     raeburn  2913:         if (defined($in{'mode'})) {
1.587     raeburn  2914:             if ($in{'mode'} eq 'modifycourse') {
                   2915:                 if ($authnum == 1) {
1.1104    raeburn  2916:                     $authtype = '<input type="radio" name="login" value="krb" />';
1.587     raeburn  2917:                 }
                   2918:             }
                   2919:         }
1.586     raeburn  2920:     }
                   2921:     $jscall = "javascript:changed_radio('krb',$in{'formname'});";
                   2922:     if ($authtype eq '') {
                   2923:         $authtype = '<input type="radio" name="login" value="krb" '.
                   2924:                     'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
                   2925:                     $krbcheck.' />';
                   2926:     }
                   2927:     if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
1.1106    raeburn  2928:         ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
1.586     raeburn  2929:          $in{'curr_authtype'} eq 'krb5') ||
1.1106    raeburn  2930:         (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
1.586     raeburn  2931:          $in{'curr_authtype'} eq 'krb4')) {
                   2932:         $result .= &mt
1.144     matthew  2933:         ('[_1] Kerberos authenticated with domain [_2] '.
1.281     albertel 2934:          '[_3] Version 4 [_4] Version 5 [_5]',
1.586     raeburn  2935:          '<label>'.$authtype,
1.281     albertel 2936:          '</label><input type="text" size="10" name="krbarg" '.
1.165     raeburn  2937:              'value="'.$krbarg.'" '.
1.144     matthew  2938:              'onchange="'.$jscall.'" />',
1.281     albertel 2939:          '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
                   2940:          '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
                   2941: 	 '</label>');
1.586     raeburn  2942:     } elsif ($can_assign{'krb4'}) {
                   2943:         $result .= &mt
                   2944:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2945:          '[_3] Version 4 [_4]',
                   2946:          '<label>'.$authtype,
                   2947:          '</label><input type="text" size="10" name="krbarg" '.
                   2948:              'value="'.$krbarg.'" '.
                   2949:              'onchange="'.$jscall.'" />',
                   2950:          '<label><input type="hidden" name="krbver" value="4" />',
                   2951:          '</label>');
                   2952:     } elsif ($can_assign{'krb5'}) {
                   2953:         $result .= &mt
                   2954:         ('[_1] Kerberos authenticated with domain [_2] '.
                   2955:          '[_3] Version 5 [_4]',
                   2956:          '<label>'.$authtype,
                   2957:          '</label><input type="text" size="10" name="krbarg" '.
                   2958:              'value="'.$krbarg.'" '.
                   2959:              'onchange="'.$jscall.'" />',
                   2960:          '<label><input type="hidden" name="krbver" value="5" />',
                   2961:          '</label>');
                   2962:     }
1.32      matthew  2963:     return $result;
                   2964: }
                   2965: 
1.1106    raeburn  2966: sub authform_internal {
1.586     raeburn  2967:     my %in = (
1.32      matthew  2968:                 formname => 'document.cu',
                   2969:                 kerb_def_dom => 'MSU.EDU',
                   2970:                 @_,
                   2971:                 );
1.586     raeburn  2972:     my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
1.1106    raeburn  2973:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591     raeburn  2974:     if (defined($in{'curr_authtype'})) {
                   2975:         if ($in{'curr_authtype'} eq 'int') {
1.586     raeburn  2976:             if ($can_assign{'int'}) {
1.772     bisitz   2977:                 $intcheck = 'checked="checked" ';
1.623     raeburn  2978:                 if (defined($in{'mode'})) {
                   2979:                     if ($in{'mode'} eq 'modifyuser') {
                   2980:                         $intcheck = '';
                   2981:                     }
                   2982:                 }
1.591     raeburn  2983:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  2984:                     $intarg = $in{'curr_autharg'};
                   2985:                 }
                   2986:             } else {
                   2987:                 $result = &mt('Currently internally authenticated.');
                   2988:                 return $result;
1.165     raeburn  2989:             }
                   2990:         }
1.586     raeburn  2991:     } else {
                   2992:         if ($authnum == 1) {
1.784     bisitz   2993:             $authtype = '<input type="hidden" name="login" value="int" />';
1.586     raeburn  2994:         }
                   2995:     }
                   2996:     if (!$can_assign{'int'}) {
                   2997:         return;
1.587     raeburn  2998:     } elsif ($authtype eq '') {
1.591     raeburn  2999:         if (defined($in{'mode'})) {
1.587     raeburn  3000:             if ($in{'mode'} eq 'modifycourse') {
                   3001:                 if ($authnum == 1) {
1.1104    raeburn  3002:                     $authtype = '<input type="radio" name="login" value="int" />';
1.587     raeburn  3003:                 }
                   3004:             }
                   3005:         }
1.165     raeburn  3006:     }
1.586     raeburn  3007:     $jscall = "javascript:changed_radio('int',$in{'formname'});";
                   3008:     if ($authtype eq '') {
                   3009:         $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
                   3010:                     ' onchange="'.$jscall.'" onclick="'.$jscall.'" />';
                   3011:     }
1.605     bisitz   3012:     $autharg = '<input type="password" size="10" name="intarg" value="'.
1.586     raeburn  3013:                $intarg.'" onchange="'.$jscall.'" />';
                   3014:     $result = &mt
1.144     matthew  3015:         ('[_1] Internally authenticated (with initial password [_2])',
1.586     raeburn  3016:          '<label>'.$authtype,'</label>'.$autharg);
1.824     bisitz   3017:     $result.="<label><input type=\"checkbox\" name=\"visible\" onclick='if (this.checked) { this.form.intarg.type=\"text\" } else { this.form.intarg.type=\"password\" }' />".&mt('Visible input').'</label>';
1.32      matthew  3018:     return $result;
                   3019: }
                   3020: 
1.1104    raeburn  3021: sub authform_local {
1.32      matthew  3022:     my %in = (
                   3023:               formname => 'document.cu',
                   3024:               kerb_def_dom => 'MSU.EDU',
                   3025:               @_,
                   3026:               );
1.586     raeburn  3027:     my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
1.1106    raeburn  3028:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591     raeburn  3029:     if (defined($in{'curr_authtype'})) {
                   3030:         if ($in{'curr_authtype'} eq 'loc') {
1.586     raeburn  3031:             if ($can_assign{'loc'}) {
1.772     bisitz   3032:                 $loccheck = 'checked="checked" ';
1.623     raeburn  3033:                 if (defined($in{'mode'})) {
                   3034:                     if ($in{'mode'} eq 'modifyuser') {
                   3035:                         $loccheck = '';
                   3036:                     }
                   3037:                 }
1.591     raeburn  3038:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  3039:                     $locarg = $in{'curr_autharg'};
                   3040:                 }
                   3041:             } else {
                   3042:                 $result = &mt('Currently using local (institutional) authentication.');
                   3043:                 return $result;
1.165     raeburn  3044:             }
                   3045:         }
1.586     raeburn  3046:     } else {
                   3047:         if ($authnum == 1) {
1.784     bisitz   3048:             $authtype = '<input type="hidden" name="login" value="loc" />';
1.586     raeburn  3049:         }
                   3050:     }
                   3051:     if (!$can_assign{'loc'}) {
                   3052:         return;
1.587     raeburn  3053:     } elsif ($authtype eq '') {
1.591     raeburn  3054:         if (defined($in{'mode'})) {
1.587     raeburn  3055:             if ($in{'mode'} eq 'modifycourse') {
                   3056:                 if ($authnum == 1) {
1.1104    raeburn  3057:                     $authtype = '<input type="radio" name="login" value="loc" />';
1.587     raeburn  3058:                 }
                   3059:             }
                   3060:         }
1.165     raeburn  3061:     }
1.586     raeburn  3062:     $jscall = "javascript:changed_radio('loc',$in{'formname'});";
                   3063:     if ($authtype eq '') {
                   3064:         $authtype = '<input type="radio" name="login" value="loc" '.
                   3065:                     $loccheck.' onchange="'.$jscall.'" onclick="'.
                   3066:                     $jscall.'" />';
                   3067:     }
                   3068:     $autharg = '<input type="text" size="10" name="locarg" value="'.
                   3069:                $locarg.'" onchange="'.$jscall.'" />';
                   3070:     $result = &mt('[_1] Local Authentication with argument [_2]',
                   3071:                   '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  3072:     return $result;
                   3073: }
                   3074: 
1.1106    raeburn  3075: sub authform_filesystem {
1.32      matthew  3076:     my %in = (
                   3077:               formname => 'document.cu',
                   3078:               kerb_def_dom => 'MSU.EDU',
                   3079:               @_,
                   3080:               );
1.586     raeburn  3081:     my ($fsyscheck,$result,$authtype,$autharg,$jscall);
1.1106    raeburn  3082:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591     raeburn  3083:     if (defined($in{'curr_authtype'})) {
                   3084:         if ($in{'curr_authtype'} eq 'fsys') {
1.586     raeburn  3085:             if ($can_assign{'fsys'}) {
1.772     bisitz   3086:                 $fsyscheck = 'checked="checked" ';
1.623     raeburn  3087:                 if (defined($in{'mode'})) {
                   3088:                     if ($in{'mode'} eq 'modifyuser') {
                   3089:                         $fsyscheck = '';
                   3090:                     }
                   3091:                 }
1.586     raeburn  3092:             } else {
                   3093:                 $result = &mt('Currently Filesystem Authenticated.');
                   3094:                 return $result;
                   3095:             }           
                   3096:         }
                   3097:     } else {
                   3098:         if ($authnum == 1) {
1.784     bisitz   3099:             $authtype = '<input type="hidden" name="login" value="fsys" />';
1.586     raeburn  3100:         }
                   3101:     }
                   3102:     if (!$can_assign{'fsys'}) {
                   3103:         return;
1.587     raeburn  3104:     } elsif ($authtype eq '') {
1.591     raeburn  3105:         if (defined($in{'mode'})) {
1.587     raeburn  3106:             if ($in{'mode'} eq 'modifycourse') {
                   3107:                 if ($authnum == 1) {
1.1104    raeburn  3108:                     $authtype = '<input type="radio" name="login" value="fsys" />';
1.587     raeburn  3109:                 }
                   3110:             }
                   3111:         }
1.586     raeburn  3112:     }
                   3113:     $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
                   3114:     if ($authtype eq '') {
                   3115:         $authtype = '<input type="radio" name="login" value="fsys" '.
                   3116:                     $fsyscheck.' onchange="'.$jscall.'" onclick="'.
                   3117:                     $jscall.'" />';
                   3118:     }
                   3119:     $autharg = '<input type="text" size="10" name="fsysarg" value=""'.
                   3120:                ' onchange="'.$jscall.'" />';
                   3121:     $result = &mt
1.144     matthew  3122:         ('[_1] Filesystem Authenticated (with initial password [_2])',
1.281     albertel 3123:          '<label><input type="radio" name="login" value="fsys" '.
1.586     raeburn  3124:          $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.605     bisitz   3125:          '</label><input type="password" size="10" name="fsysarg" value="" '.
1.144     matthew  3126:                   'onchange="'.$jscall.'" />');
1.32      matthew  3127:     return $result;
                   3128: }
                   3129: 
1.586     raeburn  3130: sub get_assignable_auth {
                   3131:     my ($dom) = @_;
                   3132:     if ($dom eq '') {
                   3133:         $dom = $env{'request.role.domain'};
                   3134:     }
                   3135:     my %can_assign = (
                   3136:                           krb4 => 1,
                   3137:                           krb5 => 1,
                   3138:                           int  => 1,
                   3139:                           loc  => 1,
                   3140:                      );
                   3141:     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
                   3142:     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   3143:         if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
                   3144:             my $authhash = $domconfig{'usercreation'}{'authtypes'};
                   3145:             my $context;
                   3146:             if ($env{'request.role'} =~ /^au/) {
                   3147:                 $context = 'author';
                   3148:             } elsif ($env{'request.role'} =~ /^dc/) {
                   3149:                 $context = 'domain';
                   3150:             } elsif ($env{'request.course.id'}) {
                   3151:                 $context = 'course';
                   3152:             }
                   3153:             if ($context) {
                   3154:                 if (ref($authhash->{$context}) eq 'HASH') {
                   3155:                    %can_assign = %{$authhash->{$context}}; 
                   3156:                 }
                   3157:             }
                   3158:         }
                   3159:     }
                   3160:     my $authnum = 0;
                   3161:     foreach my $key (keys(%can_assign)) {
                   3162:         if ($can_assign{$key}) {
                   3163:             $authnum ++;
                   3164:         }
                   3165:     }
                   3166:     if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
                   3167:         $authnum --;
                   3168:     }
                   3169:     return ($authnum,%can_assign);
                   3170: }
                   3171: 
1.80      albertel 3172: ###############################################################
                   3173: ##    Get Kerberos Defaults for Domain                 ##
                   3174: ###############################################################
                   3175: ##
                   3176: ## Returns default kerberos version and an associated argument
                   3177: ## as listed in file domain.tab. If not listed, provides
                   3178: ## appropriate default domain and kerberos version.
                   3179: ##
                   3180: #-------------------------------------------
                   3181: 
                   3182: =pod
                   3183: 
1.648     raeburn  3184: =item * &get_kerberos_defaults()
1.80      albertel 3185: 
                   3186: get_kerberos_defaults($target_domain) returns the default kerberos
1.641     raeburn  3187: version and domain. If not found, it defaults to version 4 and the 
                   3188: domain of the server.
1.80      albertel 3189: 
1.648     raeburn  3190: =over 4
                   3191: 
1.80      albertel 3192: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
                   3193: 
1.648     raeburn  3194: =back
                   3195: 
                   3196: =back
                   3197: 
1.80      albertel 3198: =cut
                   3199: 
                   3200: #-------------------------------------------
                   3201: sub get_kerberos_defaults {
                   3202:     my $domain=shift;
1.641     raeburn  3203:     my ($krbdef,$krbdefdom);
                   3204:     my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
                   3205:     if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
                   3206:         $krbdef = $domdefaults{'auth_def'};
                   3207:         $krbdefdom = $domdefaults{'auth_arg_def'};
                   3208:     } else {
1.80      albertel 3209:         $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
                   3210:         my $krbdefdom=$1;
                   3211:         $krbdefdom=~tr/a-z/A-Z/;
                   3212:         $krbdef = "krb4";
                   3213:     }
                   3214:     return ($krbdef,$krbdefdom);
                   3215: }
1.112     bowersj2 3216: 
1.32      matthew  3217: 
1.46      matthew  3218: ###############################################################
                   3219: ##                Thesaurus Functions                        ##
                   3220: ###############################################################
1.20      www      3221: 
1.46      matthew  3222: =pod
1.20      www      3223: 
1.112     bowersj2 3224: =head1 Thesaurus Functions
                   3225: 
                   3226: =over 4
                   3227: 
1.648     raeburn  3228: =item * &initialize_keywords()
1.46      matthew  3229: 
                   3230: Initializes the package variable %Keywords if it is empty.  Uses the
                   3231: package variable $thesaurus_db_file.
                   3232: 
                   3233: =cut
                   3234: 
                   3235: ###################################################
                   3236: 
                   3237: sub initialize_keywords {
                   3238:     return 1 if (scalar keys(%Keywords));
                   3239:     # If we are here, %Keywords is empty, so fill it up
                   3240:     #   Make sure the file we need exists...
                   3241:     if (! -e $thesaurus_db_file) {
                   3242:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
                   3243:                                  " failed because it does not exist");
                   3244:         return 0;
                   3245:     }
                   3246:     #   Set up the hash as a database
                   3247:     my %thesaurus_db;
                   3248:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 3249:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  3250:         &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
                   3251:                                  $thesaurus_db_file);
                   3252:         return 0;
                   3253:     } 
                   3254:     #  Get the average number of appearances of a word.
                   3255:     my $avecount = $thesaurus_db{'average.count'};
                   3256:     #  Put keywords (those that appear > average) into %Keywords
                   3257:     while (my ($word,$data)=each (%thesaurus_db)) {
                   3258:         my ($count,undef) = split /:/,$data;
                   3259:         $Keywords{$word}++ if ($count > $avecount);
                   3260:     }
                   3261:     untie %thesaurus_db;
                   3262:     # Remove special values from %Keywords.
1.356     albertel 3263:     foreach my $value ('total.count','average.count') {
                   3264:         delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586     raeburn  3265:   }
1.46      matthew  3266:     return 1;
                   3267: }
                   3268: 
                   3269: ###################################################
                   3270: 
                   3271: =pod
                   3272: 
1.648     raeburn  3273: =item * &keyword($word)
1.46      matthew  3274: 
                   3275: Returns true if $word is a keyword.  A keyword is a word that appears more 
                   3276: than the average number of times in the thesaurus database.  Calls 
                   3277: &initialize_keywords
                   3278: 
                   3279: =cut
                   3280: 
                   3281: ###################################################
1.20      www      3282: 
                   3283: sub keyword {
1.46      matthew  3284:     return if (!&initialize_keywords());
                   3285:     my $word=lc(shift());
                   3286:     $word=~s/\W//g;
                   3287:     return exists($Keywords{$word});
1.20      www      3288: }
1.46      matthew  3289: 
                   3290: ###############################################################
                   3291: 
                   3292: =pod 
1.20      www      3293: 
1.648     raeburn  3294: =item * &get_related_words()
1.46      matthew  3295: 
1.160     matthew  3296: Look up a word in the thesaurus.  Takes a scalar argument and returns
1.46      matthew  3297: an array of words.  If the keyword is not in the thesaurus, an empty array
                   3298: will be returned.  The order of the words returned is determined by the
                   3299: database which holds them.
                   3300: 
                   3301: Uses global $thesaurus_db_file.
                   3302: 
1.1057    foxr     3303: 
1.46      matthew  3304: =cut
                   3305: 
                   3306: ###############################################################
                   3307: sub get_related_words {
                   3308:     my $keyword = shift;
                   3309:     my %thesaurus_db;
                   3310:     if (! -e $thesaurus_db_file) {
                   3311:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
                   3312:                                  "failed because the file does not exist");
                   3313:         return ();
                   3314:     }
                   3315:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 3316:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  3317:         return ();
                   3318:     } 
                   3319:     my @Words=();
1.429     www      3320:     my $count=0;
1.46      matthew  3321:     if (exists($thesaurus_db{$keyword})) {
1.356     albertel 3322: 	# The first element is the number of times
                   3323: 	# the word appears.  We do not need it now.
1.429     www      3324: 	my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
                   3325: 	my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
                   3326: 	my $threshold=$mostfrequentcount/10;
                   3327:         foreach my $possibleword (@RelatedWords) {
                   3328:             my ($word,$wordcount)=split(/\,/,$possibleword);
                   3329:             if ($wordcount>$threshold) {
                   3330: 		push(@Words,$word);
                   3331:                 $count++;
                   3332:                 if ($count>10) { last; }
                   3333: 	    }
1.20      www      3334:         }
                   3335:     }
1.46      matthew  3336:     untie %thesaurus_db;
                   3337:     return @Words;
1.14      harris41 3338: }
1.1090    foxr     3339: ###############################################################
                   3340: #
                   3341: #  Spell checking
                   3342: #
                   3343: 
                   3344: =pod
                   3345: 
1.1142    raeburn  3346: =back
                   3347: 
1.1090    foxr     3348: =head1 Spell checking
                   3349: 
                   3350: =over 4
                   3351: 
                   3352: =item * &check_spelling($wordlist $language)
                   3353: 
                   3354: Takes a string containing words and feeds it to an external
                   3355: spellcheck program via a pipeline. Returns a string containing
                   3356: them mis-spelled words.
                   3357: 
                   3358: Parameters:
                   3359: 
                   3360: =over 4
                   3361: 
                   3362: =item - $wordlist
                   3363: 
                   3364: String that will be fed into the spellcheck program.
                   3365: 
                   3366: =item - $language
                   3367: 
                   3368: Language string that specifies the language for which the spell
                   3369: check will be performed.
                   3370: 
                   3371: =back
                   3372: 
                   3373: =back
                   3374: 
                   3375: Note: This sub assumes that aspell is installed.
                   3376: 
                   3377: 
                   3378: =cut
                   3379: 
1.46      matthew  3380: 
1.1090    foxr     3381: sub check_spelling {
                   3382:     my ($wordlist, $language) = @_;
1.1091    foxr     3383:     my @misspellings;
                   3384:     
                   3385:     # Generate the speller and set the langauge.
                   3386:     # if explicitly selected:
1.1090    foxr     3387: 
1.1091    foxr     3388:     my $speller = Text::Aspell->new;
1.1090    foxr     3389:     if ($language) {
1.1091    foxr     3390: 	$speller->set_option('lang', $language);
1.1090    foxr     3391:     }
                   3392: 
1.1091    foxr     3393:     # Turn the word list into an array of words by splittingon whitespace
1.1090    foxr     3394: 
1.1091    foxr     3395:     my @words = split(/\s+/, $wordlist);
1.1090    foxr     3396: 
1.1091    foxr     3397:     foreach my $word (@words) {
                   3398: 	if(! $speller->check($word)) {
                   3399: 	    push(@misspellings, $word);
1.1090    foxr     3400: 	}
                   3401:     }
1.1091    foxr     3402:     return join(' ', @misspellings);
                   3403:     
1.1090    foxr     3404: }
                   3405: 
1.61      www      3406: # -------------------------------------------------------------- Plaintext name
1.81      albertel 3407: =pod
                   3408: 
1.112     bowersj2 3409: =head1 User Name Functions
                   3410: 
                   3411: =over 4
                   3412: 
1.648     raeburn  3413: =item * &plainname($uname,$udom,$first)
1.81      albertel 3414: 
1.112     bowersj2 3415: Takes a users logon name and returns it as a string in
1.226     albertel 3416: "first middle last generation" form 
                   3417: if $first is set to 'lastname' then it returns it as
                   3418: 'lastname generation, firstname middlename' if their is a lastname
1.81      albertel 3419: 
                   3420: =cut
1.61      www      3421: 
1.295     www      3422: 
1.81      albertel 3423: ###############################################################
1.61      www      3424: sub plainname {
1.226     albertel 3425:     my ($uname,$udom,$first)=@_;
1.537     albertel 3426:     return if (!defined($uname) || !defined($udom));
1.295     www      3427:     my %names=&getnames($uname,$udom);
1.226     albertel 3428:     my $name=&Apache::lonnet::format_name($names{'firstname'},
                   3429: 					  $names{'middlename'},
                   3430: 					  $names{'lastname'},
                   3431: 					  $names{'generation'},$first);
                   3432:     $name=~s/^\s+//;
1.62      www      3433:     $name=~s/\s+$//;
                   3434:     $name=~s/\s+/ /g;
1.353     albertel 3435:     if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62      www      3436:     return $name;
1.61      www      3437: }
1.66      www      3438: 
                   3439: # -------------------------------------------------------------------- Nickname
1.81      albertel 3440: =pod
                   3441: 
1.648     raeburn  3442: =item * &nickname($uname,$udom)
1.81      albertel 3443: 
                   3444: Gets a users name and returns it as a string as
                   3445: 
                   3446: "&quot;nickname&quot;"
1.66      www      3447: 
1.81      albertel 3448: if the user has a nickname or
                   3449: 
                   3450: "first middle last generation"
                   3451: 
                   3452: if the user does not
                   3453: 
                   3454: =cut
1.66      www      3455: 
                   3456: sub nickname {
                   3457:     my ($uname,$udom)=@_;
1.537     albertel 3458:     return if (!defined($uname) || !defined($udom));
1.295     www      3459:     my %names=&getnames($uname,$udom);
1.68      albertel 3460:     my $name=$names{'nickname'};
1.66      www      3461:     if ($name) {
                   3462:        $name='&quot;'.$name.'&quot;'; 
                   3463:     } else {
                   3464:        $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
                   3465: 	     $names{'lastname'}.' '.$names{'generation'};
                   3466:        $name=~s/\s+$//;
                   3467:        $name=~s/\s+/ /g;
                   3468:     }
                   3469:     return $name;
                   3470: }
                   3471: 
1.295     www      3472: sub getnames {
                   3473:     my ($uname,$udom)=@_;
1.537     albertel 3474:     return if (!defined($uname) || !defined($udom));
1.433     albertel 3475:     if ($udom eq 'public' && $uname eq 'public') {
                   3476: 	return ('lastname' => &mt('Public'));
                   3477:     }
1.295     www      3478:     my $id=$uname.':'.$udom;
                   3479:     my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
                   3480:     if ($cached) {
                   3481: 	return %{$names};
                   3482:     } else {
                   3483: 	my %loadnames=&Apache::lonnet::get('environment',
                   3484:                     ['firstname','middlename','lastname','generation','nickname'],
                   3485: 					 $udom,$uname);
                   3486: 	&Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
                   3487: 	return %loadnames;
                   3488:     }
                   3489: }
1.61      www      3490: 
1.542     raeburn  3491: # -------------------------------------------------------------------- getemails
1.648     raeburn  3492: 
1.542     raeburn  3493: =pod
                   3494: 
1.648     raeburn  3495: =item * &getemails($uname,$udom)
1.542     raeburn  3496: 
                   3497: Gets a user's email information and returns it as a hash with keys:
                   3498: notification, critnotification, permanentemail
                   3499: 
                   3500: For notification and critnotification, values are comma-separated lists 
1.648     raeburn  3501: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542     raeburn  3502:  
1.648     raeburn  3503: 
1.542     raeburn  3504: =cut
                   3505: 
1.648     raeburn  3506: 
1.466     albertel 3507: sub getemails {
                   3508:     my ($uname,$udom)=@_;
                   3509:     if ($udom eq 'public' && $uname eq 'public') {
                   3510: 	return;
                   3511:     }
1.467     www      3512:     if (!$udom) { $udom=$env{'user.domain'}; }
                   3513:     if (!$uname) { $uname=$env{'user.name'}; }
1.466     albertel 3514:     my $id=$uname.':'.$udom;
                   3515:     my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
                   3516:     if ($cached) {
                   3517: 	return %{$names};
                   3518:     } else {
                   3519: 	my %loadnames=&Apache::lonnet::get('environment',
                   3520:                     			   ['notification','critnotification',
                   3521: 					    'permanentemail'],
                   3522: 					   $udom,$uname);
                   3523: 	&Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
                   3524: 	return %loadnames;
                   3525:     }
                   3526: }
                   3527: 
1.551     albertel 3528: sub flush_email_cache {
                   3529:     my ($uname,$udom)=@_;
                   3530:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   3531:     if (!$uname) { $uname=$env{'user.name'};   }
                   3532:     return if ($udom eq 'public' && $uname eq 'public');
                   3533:     my $id=$uname.':'.$udom;
                   3534:     &Apache::lonnet::devalidate_cache_new('emailscache',$id);
                   3535: }
                   3536: 
1.728     raeburn  3537: # -------------------------------------------------------------------- getlangs
                   3538: 
                   3539: =pod
                   3540: 
                   3541: =item * &getlangs($uname,$udom)
                   3542: 
                   3543: Gets a user's language preference and returns it as a hash with key:
                   3544: language.
                   3545: 
                   3546: =cut
                   3547: 
                   3548: 
                   3549: sub getlangs {
                   3550:     my ($uname,$udom) = @_;
                   3551:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   3552:     if (!$uname) { $uname=$env{'user.name'};   }
                   3553:     my $id=$uname.':'.$udom;
                   3554:     my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
                   3555:     if ($cached) {
                   3556:         return %{$langs};
                   3557:     } else {
                   3558:         my %loadlangs=&Apache::lonnet::get('environment',['languages'],
                   3559:                                            $udom,$uname);
                   3560:         &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
                   3561:         return %loadlangs;
                   3562:     }
                   3563: }
                   3564: 
                   3565: sub flush_langs_cache {
                   3566:     my ($uname,$udom)=@_;
                   3567:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   3568:     if (!$uname) { $uname=$env{'user.name'};   }
                   3569:     return if ($udom eq 'public' && $uname eq 'public');
                   3570:     my $id=$uname.':'.$udom;
                   3571:     &Apache::lonnet::devalidate_cache_new('userlangs',$id);
                   3572: }
                   3573: 
1.61      www      3574: # ------------------------------------------------------------------ Screenname
1.81      albertel 3575: 
                   3576: =pod
                   3577: 
1.648     raeburn  3578: =item * &screenname($uname,$udom)
1.81      albertel 3579: 
                   3580: Gets a users screenname and returns it as a string
                   3581: 
                   3582: =cut
1.61      www      3583: 
                   3584: sub screenname {
                   3585:     my ($uname,$udom)=@_;
1.258     albertel 3586:     if ($uname eq $env{'user.name'} &&
                   3587: 	$udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212     albertel 3588:     my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68      albertel 3589:     return $names{'screenname'};
1.62      www      3590: }
                   3591: 
1.212     albertel 3592: 
1.802     bisitz   3593: # ------------------------------------------------------------- Confirm Wrapper
                   3594: =pod
                   3595: 
1.1142    raeburn  3596: =item * &confirmwrapper($message)
1.802     bisitz   3597: 
                   3598: Wrap messages about completion of operation in box
                   3599: 
                   3600: =cut
                   3601: 
                   3602: sub confirmwrapper {
                   3603:     my ($message)=@_;
                   3604:     if ($message) {
                   3605:         return "\n".'<div class="LC_confirm_box">'."\n"
                   3606:                .$message."\n"
                   3607:                .'</div>'."\n";
                   3608:     } else {
                   3609:         return $message;
                   3610:     }
                   3611: }
                   3612: 
1.62      www      3613: # ------------------------------------------------------------- Message Wrapper
                   3614: 
                   3615: sub messagewrapper {
1.369     www      3616:     my ($link,$username,$domain,$subject,$text)=@_;
1.62      www      3617:     return 
1.441     albertel 3618:         '<a href="/adm/email?compose=individual&amp;'.
                   3619:         'recname='.$username.'&amp;recdom='.$domain.
                   3620: 	'&amp;subject='.&escape($subject).'&amp;text='.&escape($text).'" '.
1.200     matthew  3621:         'title="'.&mt('Send message').'">'.$link.'</a>';
1.74      www      3622: }
1.802     bisitz   3623: 
1.74      www      3624: # --------------------------------------------------------------- Notes Wrapper
                   3625: 
                   3626: sub noteswrapper {
                   3627:     my ($link,$un,$do)=@_;
                   3628:     return 
1.896     amueller 3629: "<a href='/adm/email?recordftf=retrieve&amp;recname=$un&amp;recdom=$do'>$link</a>";
1.62      www      3630: }
1.802     bisitz   3631: 
1.62      www      3632: # ------------------------------------------------------------- Aboutme Wrapper
                   3633: 
                   3634: sub aboutmewrapper {
1.1070    raeburn  3635:     my ($link,$username,$domain,$target,$class)=@_;
1.447     raeburn  3636:     if (!defined($username)  && !defined($domain)) {
                   3637:         return;
                   3638:     }
1.1096    raeburn  3639:     return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.1070    raeburn  3640: 	($target?' target="'.$target.'"':'').($class?' class="'.$class.'"':'').' title="'.&mt("View this user's personal information page").'">'.$link.'</a>';
1.62      www      3641: }
                   3642: 
                   3643: # ------------------------------------------------------------ Syllabus Wrapper
                   3644: 
                   3645: sub syllabuswrapper {
1.707     bisitz   3646:     my ($linktext,$coursedir,$domain)=@_;
1.208     matthew  3647:     return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61      www      3648: }
1.14      harris41 3649: 
1.802     bisitz   3650: # -----------------------------------------------------------------------------
                   3651: 
1.208     matthew  3652: sub track_student_link {
1.887     raeburn  3653:     my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
1.268     albertel 3654:     my $link ="/adm/trackstudent?";
1.208     matthew  3655:     my $title = 'View recent activity';
                   3656:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   3657:         defined($sdom)  && $sdom  !~ /^\s*$/) {
1.268     albertel 3658:         $link .= "selected_student=$sname:$sdom";
1.208     matthew  3659:         $title .= ' of this student';
1.268     albertel 3660:     } 
1.208     matthew  3661:     if (defined($target) && $target !~ /^\s*$/) {
                   3662:         $target = qq{target="$target"};
                   3663:     } else {
                   3664:         $target = '';
                   3665:     }
1.268     albertel 3666:     if ($start) { $link.='&amp;start='.$start; }
1.887     raeburn  3667:     if ($only_body) { $link .= '&amp;only_body=1'; }
1.554     albertel 3668:     $title = &mt($title);
                   3669:     $linktext = &mt($linktext);
1.448     albertel 3670:     return qq{<a href="$link" title="$title" $target>$linktext</a>}.
                   3671: 	&help_open_topic('View_recent_activity');
1.208     matthew  3672: }
                   3673: 
1.781     raeburn  3674: sub slot_reservations_link {
                   3675:     my ($linktext,$sname,$sdom,$target) = @_;
                   3676:     my $link ="/adm/slotrequest?command=showresv&amp;origin=aboutme";
                   3677:     my $title = 'View slot reservation history';
                   3678:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   3679:         defined($sdom)  && $sdom  !~ /^\s*$/) {
                   3680:         $link .= "&amp;uname=$sname&amp;udom=$sdom";
                   3681:         $title .= ' of this student';
                   3682:     }
                   3683:     if (defined($target) && $target !~ /^\s*$/) {
                   3684:         $target = qq{target="$target"};
                   3685:     } else {
                   3686:         $target = '';
                   3687:     }
                   3688:     $title = &mt($title);
                   3689:     $linktext = &mt($linktext);
                   3690:     return qq{<a href="$link" title="$title" $target>$linktext</a>};
                   3691: # FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
                   3692: 
                   3693: }
                   3694: 
1.508     www      3695: # ===================================================== Display a student photo
                   3696: 
                   3697: 
1.509     albertel 3698: sub student_image_tag {
1.508     www      3699:     my ($domain,$user)=@_;
                   3700:     my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
                   3701:     if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
                   3702: 	return '<img src="'.$imgsrc.'" align="right" />';
                   3703:     } else {
                   3704: 	return '';
                   3705:     }
                   3706: }
                   3707: 
1.112     bowersj2 3708: =pod
                   3709: 
                   3710: =back
                   3711: 
                   3712: =head1 Access .tab File Data
                   3713: 
                   3714: =over 4
                   3715: 
1.648     raeburn  3716: =item * &languageids() 
1.112     bowersj2 3717: 
                   3718: returns list of all language ids
                   3719: 
                   3720: =cut
                   3721: 
1.14      harris41 3722: sub languageids {
1.16      harris41 3723:     return sort(keys(%language));
1.14      harris41 3724: }
                   3725: 
1.112     bowersj2 3726: =pod
                   3727: 
1.648     raeburn  3728: =item * &languagedescription() 
1.112     bowersj2 3729: 
                   3730: returns description of a specified language id
                   3731: 
                   3732: =cut
                   3733: 
1.14      harris41 3734: sub languagedescription {
1.125     www      3735:     my $code=shift;
                   3736:     return  ($supported_language{$code}?'* ':'').
                   3737:             $language{$code}.
1.126     www      3738: 	    ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145     www      3739: }
                   3740: 
1.1048    foxr     3741: =pod
                   3742: 
                   3743: =item * &plainlanguagedescription
                   3744: 
                   3745: Returns both the plain language description (e.g. 'Creoles and Pidgins, English-based (Other)')
                   3746: and the language character encoding (e.g. ISO) separated by a ' - ' string.
                   3747: 
                   3748: =cut
                   3749: 
1.145     www      3750: sub plainlanguagedescription {
                   3751:     my $code=shift;
                   3752:     return $language{$code};
                   3753: }
                   3754: 
1.1048    foxr     3755: =pod
                   3756: 
                   3757: =item * &supportedlanguagecode
                   3758: 
                   3759: Returns the supported language code (e.g. sptutf maps to pt) given a language
                   3760: code.
                   3761: 
                   3762: =cut
                   3763: 
1.145     www      3764: sub supportedlanguagecode {
                   3765:     my $code=shift;
                   3766:     return $supported_language{$code};
1.97      www      3767: }
                   3768: 
1.112     bowersj2 3769: =pod
                   3770: 
1.1048    foxr     3771: =item * &latexlanguage()
                   3772: 
                   3773: Given a language key code returns the correspondnig language to use
                   3774: to select the correct hyphenation on LaTeX printouts.  This is undef if there
                   3775: is no supported hyphenation for the language code.
                   3776: 
                   3777: =cut
                   3778: 
                   3779: sub latexlanguage {
                   3780:     my $code = shift;
                   3781:     return $latex_language{$code};
                   3782: }
                   3783: 
                   3784: =pod
                   3785: 
                   3786: =item * &latexhyphenation()
                   3787: 
                   3788: Same as above but what's supplied is the language as it might be stored
                   3789: in the metadata.
                   3790: 
                   3791: =cut
                   3792: 
                   3793: sub latexhyphenation {
                   3794:     my $key = shift;
                   3795:     return $latex_language_bykey{$key};
                   3796: }
                   3797: 
                   3798: =pod
                   3799: 
1.648     raeburn  3800: =item * &copyrightids() 
1.112     bowersj2 3801: 
                   3802: returns list of all copyrights
                   3803: 
                   3804: =cut
                   3805: 
                   3806: sub copyrightids {
                   3807:     return sort(keys(%cprtag));
                   3808: }
                   3809: 
                   3810: =pod
                   3811: 
1.648     raeburn  3812: =item * &copyrightdescription() 
1.112     bowersj2 3813: 
                   3814: returns description of a specified copyright id
                   3815: 
                   3816: =cut
                   3817: 
                   3818: sub copyrightdescription {
1.166     www      3819:     return &mt($cprtag{shift(@_)});
1.112     bowersj2 3820: }
1.197     matthew  3821: 
                   3822: =pod
                   3823: 
1.648     raeburn  3824: =item * &source_copyrightids() 
1.192     taceyjo1 3825: 
                   3826: returns list of all source copyrights
                   3827: 
                   3828: =cut
                   3829: 
                   3830: sub source_copyrightids {
                   3831:     return sort(keys(%scprtag));
                   3832: }
                   3833: 
                   3834: =pod
                   3835: 
1.648     raeburn  3836: =item * &source_copyrightdescription() 
1.192     taceyjo1 3837: 
                   3838: returns description of a specified source copyright id
                   3839: 
                   3840: =cut
                   3841: 
                   3842: sub source_copyrightdescription {
                   3843:     return &mt($scprtag{shift(@_)});
                   3844: }
1.112     bowersj2 3845: 
                   3846: =pod
                   3847: 
1.648     raeburn  3848: =item * &filecategories() 
1.112     bowersj2 3849: 
                   3850: returns list of all file categories
                   3851: 
                   3852: =cut
                   3853: 
                   3854: sub filecategories {
                   3855:     return sort(keys(%category_extensions));
                   3856: }
                   3857: 
                   3858: =pod
                   3859: 
1.648     raeburn  3860: =item * &filecategorytypes() 
1.112     bowersj2 3861: 
                   3862: returns list of file types belonging to a given file
                   3863: category
                   3864: 
                   3865: =cut
                   3866: 
                   3867: sub filecategorytypes {
1.356     albertel 3868:     my ($cat) = @_;
                   3869:     return @{$category_extensions{lc($cat)}};
1.112     bowersj2 3870: }
                   3871: 
                   3872: =pod
                   3873: 
1.648     raeburn  3874: =item * &fileembstyle() 
1.112     bowersj2 3875: 
                   3876: returns embedding style for a specified file type
                   3877: 
                   3878: =cut
                   3879: 
                   3880: sub fileembstyle {
                   3881:     return $fe{lc(shift(@_))};
1.169     www      3882: }
                   3883: 
1.351     www      3884: sub filemimetype {
                   3885:     return $fm{lc(shift(@_))};
                   3886: }
                   3887: 
1.169     www      3888: 
                   3889: sub filecategoryselect {
                   3890:     my ($name,$value)=@_;
1.189     matthew  3891:     return &select_form($value,$name,
1.970     raeburn  3892:                         {'' => &mt('Any category'), map { $_,$_ } sort(keys(%category_extensions))});
1.112     bowersj2 3893: }
                   3894: 
                   3895: =pod
                   3896: 
1.648     raeburn  3897: =item * &filedescription() 
1.112     bowersj2 3898: 
                   3899: returns description for a specified file type
                   3900: 
                   3901: =cut
                   3902: 
                   3903: sub filedescription {
1.188     matthew  3904:     my $file_description = $fd{lc(shift())};
                   3905:     $file_description =~ s:([\[\]]):~$1:g;
                   3906:     return &mt($file_description);
1.112     bowersj2 3907: }
                   3908: 
                   3909: =pod
                   3910: 
1.648     raeburn  3911: =item * &filedescriptionex() 
1.112     bowersj2 3912: 
                   3913: returns description for a specified file type with
                   3914: extra formatting
                   3915: 
                   3916: =cut
                   3917: 
                   3918: sub filedescriptionex {
                   3919:     my $ex=shift;
1.188     matthew  3920:     my $file_description = $fd{lc($ex)};
                   3921:     $file_description =~ s:([\[\]]):~$1:g;
                   3922:     return '.'.$ex.' '.&mt($file_description);
1.112     bowersj2 3923: }
                   3924: 
                   3925: # End of .tab access
                   3926: =pod
                   3927: 
                   3928: =back
                   3929: 
                   3930: =cut
                   3931: 
                   3932: # ------------------------------------------------------------------ File Types
                   3933: sub fileextensions {
                   3934:     return sort(keys(%fe));
                   3935: }
                   3936: 
1.97      www      3937: # ----------------------------------------------------------- Display Languages
                   3938: # returns a hash with all desired display languages
                   3939: #
                   3940: 
                   3941: sub display_languages {
                   3942:     my %languages=();
1.695     raeburn  3943:     foreach my $lang (&Apache::lonlocal::preferred_languages()) {
1.356     albertel 3944: 	$languages{$lang}=1;
1.97      www      3945:     }
                   3946:     &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258     albertel 3947:     if ($env{'form.displaylanguage'}) {
1.356     albertel 3948: 	foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
                   3949: 	    $languages{$lang}=1;
1.97      www      3950:         }
                   3951:     }
                   3952:     return %languages;
1.14      harris41 3953: }
                   3954: 
1.582     albertel 3955: sub languages {
                   3956:     my ($possible_langs) = @_;
1.695     raeburn  3957:     my @preferred_langs = &Apache::lonlocal::preferred_languages();
1.582     albertel 3958:     if (!ref($possible_langs)) {
                   3959: 	if( wantarray ) {
                   3960: 	    return @preferred_langs;
                   3961: 	} else {
                   3962: 	    return $preferred_langs[0];
                   3963: 	}
                   3964:     }
                   3965:     my %possibilities = map { $_ => 1 } (@$possible_langs);
                   3966:     my @preferred_possibilities;
                   3967:     foreach my $preferred_lang (@preferred_langs) {
                   3968: 	if (exists($possibilities{$preferred_lang})) {
                   3969: 	    push(@preferred_possibilities, $preferred_lang);
                   3970: 	}
                   3971:     }
                   3972:     if( wantarray ) {
                   3973: 	return @preferred_possibilities;
                   3974:     }
                   3975:     return $preferred_possibilities[0];
                   3976: }
                   3977: 
1.742     raeburn  3978: sub user_lang {
                   3979:     my ($touname,$toudom,$fromcid) = @_;
                   3980:     my @userlangs;
                   3981:     if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
                   3982:         @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
                   3983:                     $env{'course.'.$fromcid.'.languages'}));
                   3984:     } else {
                   3985:         my %langhash = &getlangs($touname,$toudom);
                   3986:         if ($langhash{'languages'} ne '') {
                   3987:             @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
                   3988:         } else {
                   3989:             my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
                   3990:             if ($domdefs{'lang_def'} ne '') {
                   3991:                 @userlangs = ($domdefs{'lang_def'});
                   3992:             }
                   3993:         }
                   3994:     }
                   3995:     my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
                   3996:     my $user_lh = Apache::localize->get_handle(@languages);
                   3997:     return $user_lh;
                   3998: }
                   3999: 
                   4000: 
1.112     bowersj2 4001: ###############################################################
                   4002: ##               Student Answer Attempts                     ##
                   4003: ###############################################################
                   4004: 
                   4005: =pod
                   4006: 
                   4007: =head1 Alternate Problem Views
                   4008: 
                   4009: =over 4
                   4010: 
1.648     raeburn  4011: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.1199    raeburn  4012:     $getattempt, $regexp, $gradesub, $usec, $identifier)
1.112     bowersj2 4013: 
                   4014: Return string with previous attempt on problem. Arguments:
                   4015: 
                   4016: =over 4
                   4017: 
                   4018: =item * $symb: Problem, including path
                   4019: 
                   4020: =item * $username: username of the desired student
                   4021: 
                   4022: =item * $domain: domain of the desired student
1.14      harris41 4023: 
1.112     bowersj2 4024: =item * $course: Course ID
1.14      harris41 4025: 
1.112     bowersj2 4026: =item * $getattempt: Leave blank for all attempts, otherwise put
                   4027:     something
1.14      harris41 4028: 
1.112     bowersj2 4029: =item * $regexp: if string matches this regexp, the string will be
                   4030:     sent to $gradesub
1.14      harris41 4031: 
1.112     bowersj2 4032: =item * $gradesub: routine that processes the string if it matches $regexp
1.14      harris41 4033: 
1.1199    raeburn  4034: =item * $usec: section of the desired student
                   4035: 
                   4036: =item * $identifier: counter for student (multiple students one problem) or 
                   4037:     problem (one student; whole sequence).
                   4038: 
1.112     bowersj2 4039: =back
1.14      harris41 4040: 
1.112     bowersj2 4041: The output string is a table containing all desired attempts, if any.
1.16      harris41 4042: 
1.112     bowersj2 4043: =cut
1.1       albertel 4044: 
                   4045: sub get_previous_attempt {
1.1199    raeburn  4046:   my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub,$usec,$identifier)=@_;
1.1       albertel 4047:   my $prevattempts='';
1.43      ng       4048:   no strict 'refs';
1.1       albertel 4049:   if ($symb) {
1.3       albertel 4050:     my (%returnhash)=
                   4051:       &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1       albertel 4052:     if ($returnhash{'version'}) {
                   4053:       my %lasthash=();
                   4054:       my $version;
                   4055:       for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1212    raeburn  4056:         foreach my $key (reverse(sort(split(/\:/,$returnhash{$version.':keys'})))) {
                   4057:             if ($key =~ /\.rawrndseed$/) {
                   4058:                 my ($id) = ($key =~ /^(.+)\.rawrndseed$/);
                   4059:                 $lasthash{$id.'.rndseed'} = $returnhash{$version.':'.$key};
                   4060:             } else {
                   4061:                 $lasthash{$key}=$returnhash{$version.':'.$key};
                   4062:             }
1.19      harris41 4063:         }
1.1       albertel 4064:       }
1.596     albertel 4065:       $prevattempts=&start_data_table().&start_data_table_header_row();
                   4066:       $prevattempts.='<th>'.&mt('History').'</th>';
1.1199    raeburn  4067:       my (%typeparts,%lasthidden,%regraded,%hidestatus);
1.945     raeburn  4068:       my $showsurv=&Apache::lonnet::allowed('vas',$env{'request.course.id'});
1.356     albertel 4069:       foreach my $key (sort(keys(%lasthash))) {
                   4070: 	my ($ign,@parts) = split(/\./,$key);
1.41      ng       4071: 	if ($#parts > 0) {
1.31      albertel 4072: 	  my $data=$parts[-1];
1.989     raeburn  4073:           next if ($data eq 'foilorder');
1.31      albertel 4074: 	  pop(@parts);
1.1010    www      4075:           $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.'&nbsp;</th>';
1.945     raeburn  4076:           if ($data eq 'type') {
                   4077:               unless ($showsurv) {
                   4078:                   my $id = join(',',@parts);
                   4079:                   $typeparts{$ign.'.'.$id} = $lasthash{$key};
1.978     raeburn  4080:                   if (($lasthash{$key} eq 'anonsurvey') || ($lasthash{$key} eq 'anonsurveycred')) {
                   4081:                       $lasthidden{$ign.'.'.$id} = 1;
                   4082:                   }
1.945     raeburn  4083:               }
1.1199    raeburn  4084:               if ($identifier ne '') {
                   4085:                   my $id = join(',',@parts);
                   4086:                   if (&Apache::lonnet::EXT("resource.$id.problemstatus",$symb,
                   4087:                                                $domain,$username,$usec,undef,$course) =~ /^no/) {
                   4088:                       $hidestatus{$ign.'.'.$id} = 1;
                   4089:                   }
                   4090:               }
                   4091:           } elsif ($data eq 'regrader') {
                   4092:               if (($identifier ne '') && (@parts)) {
1.1200    raeburn  4093:                   my $id = join(',',@parts);
                   4094:                   $regraded{$ign.'.'.$id} = 1;
1.1199    raeburn  4095:               }
1.1010    www      4096:           } 
1.31      albertel 4097: 	} else {
1.41      ng       4098: 	  if ($#parts == 0) {
                   4099: 	    $prevattempts.='<th>'.$parts[0].'</th>';
                   4100: 	  } else {
                   4101: 	    $prevattempts.='<th>'.$ign.'</th>';
                   4102: 	  }
1.31      albertel 4103: 	}
1.16      harris41 4104:       }
1.596     albertel 4105:       $prevattempts.=&end_data_table_header_row();
1.40      ng       4106:       if ($getattempt eq '') {
1.1199    raeburn  4107:         my (%solved,%resets,%probstatus);
1.1200    raeburn  4108:         if (($identifier ne '') && (keys(%regraded) > 0)) {
                   4109:             for ($version=1;$version<=$returnhash{'version'};$version++) {
                   4110:                 foreach my $id (keys(%regraded)) {
                   4111:                     if (($returnhash{$version.':'.$id.'.regrader'}) &&
                   4112:                         ($returnhash{$version.':'.$id.'.tries'} eq '') &&
                   4113:                         ($returnhash{$version.':'.$id.'.award'} eq '')) {
                   4114:                         push(@{$resets{$id}},$version);
1.1199    raeburn  4115:                     }
                   4116:                 }
                   4117:             }
1.1200    raeburn  4118:         }
                   4119: 	for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1199    raeburn  4120:             my (@hidden,@unsolved);
1.945     raeburn  4121:             if (%typeparts) {
                   4122:                 foreach my $id (keys(%typeparts)) {
1.1199    raeburn  4123:                     if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') || 
                   4124:                         ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {
1.945     raeburn  4125:                         push(@hidden,$id);
1.1199    raeburn  4126:                     } elsif ($identifier ne '') {
                   4127:                         unless (($returnhash{$version.':'.$id.'.type'} eq 'survey') ||
                   4128:                                 ($returnhash{$version.':'.$id.'.type'} eq 'surveycred') ||
                   4129:                                 ($hidestatus{$id})) {
1.1200    raeburn  4130:                             next if ((ref($resets{$id}) eq 'ARRAY') && grep(/^\Q$version\E$/,@{$resets{$id}}));
1.1199    raeburn  4131:                             if ($returnhash{$version.':'.$id.'.solved'} eq 'correct_by_student') {
                   4132:                                 push(@{$solved{$id}},$version);
                   4133:                             } elsif (($returnhash{$version.':'.$id.'.solved'} ne '') &&
                   4134:                                      (ref($solved{$id}) eq 'ARRAY')) {
                   4135:                                 my $skip;
                   4136:                                 if (ref($resets{$id}) eq 'ARRAY') {
                   4137:                                     foreach my $reset (@{$resets{$id}}) {
                   4138:                                         if ($reset > $solved{$id}[-1]) {
                   4139:                                             $skip=1;
                   4140:                                             last;
                   4141:                                         }
                   4142:                                     }
                   4143:                                 }
                   4144:                                 unless ($skip) {
                   4145:                                     my ($ign,$partslist) = split(/\./,$id,2);
                   4146:                                     push(@unsolved,$partslist);
                   4147:                                 }
                   4148:                             }
                   4149:                         }
1.945     raeburn  4150:                     }
                   4151:                 }
                   4152:             }
                   4153:             $prevattempts.=&start_data_table_row().
1.1199    raeburn  4154:                            '<td>'.&mt('Transaction [_1]',$version);
                   4155:             if (@unsolved) {
                   4156:                 $prevattempts .= '<span class="LC_nobreak"><label>'.
                   4157:                                  '<input type="checkbox" name="HIDE'.$identifier.'" value="'.$version.':'.join('_',@unsolved).'" />'.
                   4158:                                  &mt('Hide').'</label></span>';
                   4159:             }
                   4160:             $prevattempts .= '</td>';
1.945     raeburn  4161:             if (@hidden) {
                   4162:                 foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4163:                     next if ($key =~ /\.foilorder$/);
1.945     raeburn  4164:                     my $hide;
                   4165:                     foreach my $id (@hidden) {
                   4166:                         if ($key =~ /^\Q$id\E/) {
                   4167:                             $hide = 1;
                   4168:                             last;
                   4169:                         }
                   4170:                     }
                   4171:                     if ($hide) {
                   4172:                         my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
                   4173:                         if (($data eq 'award') || ($data eq 'awarddetail')) {
                   4174:                             my $value = &format_previous_attempt_value($key,
                   4175:                                              $returnhash{$version.':'.$key});
1.1173    kruse    4176:                             $prevattempts.='<td>'.$value.'&nbsp;</td>';
1.945     raeburn  4177:                         } else {
                   4178:                             $prevattempts.='<td>&nbsp;</td>';
                   4179:                         }
                   4180:                     } else {
                   4181:                         if ($key =~ /\./) {
1.1212    raeburn  4182:                             my $value = $returnhash{$version.':'.$key};
                   4183:                             if ($key =~ /\.rndseed$/) {
                   4184:                                 my ($id) = ($key =~ /^(.+)\.[^.]+$/);
                   4185:                                 if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
                   4186:                                     $value = $returnhash{$version.':'.$id.'.rawrndseed'};
                   4187:                                 }
                   4188:                             }
                   4189:                             $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
                   4190:                                            '&nbsp;</td>';
1.945     raeburn  4191:                         } else {
                   4192:                             $prevattempts.='<td>&nbsp;</td>';
                   4193:                         }
                   4194:                     }
                   4195:                 }
                   4196:             } else {
                   4197: 	        foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4198:                     next if ($key =~ /\.foilorder$/);
1.1212    raeburn  4199:                     my $value = $returnhash{$version.':'.$key};
                   4200:                     if ($key =~ /\.rndseed$/) {
                   4201:                         my ($id) = ($key =~ /^(.+)\.[^.]+$/);
                   4202:                         if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
                   4203:                             $value = $returnhash{$version.':'.$id.'.rawrndseed'};
                   4204:                         }
                   4205:                     }
                   4206:                     $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
                   4207:                                    '&nbsp;</td>';
1.945     raeburn  4208: 	        }
                   4209:             }
                   4210: 	    $prevattempts.=&end_data_table_row();
1.40      ng       4211: 	 }
1.1       albertel 4212:       }
1.945     raeburn  4213:       my @currhidden = keys(%lasthidden);
1.596     albertel 4214:       $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356     albertel 4215:       foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4216:           next if ($key =~ /\.foilorder$/);
1.945     raeburn  4217:           if (%typeparts) {
                   4218:               my $hidden;
                   4219:               foreach my $id (@currhidden) {
                   4220:                   if ($key =~ /^\Q$id\E/) {
                   4221:                       $hidden = 1;
                   4222:                       last;
                   4223:                   }
                   4224:               }
                   4225:               if ($hidden) {
                   4226:                   my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
                   4227:                   if (($data eq 'award') || ($data eq 'awarddetail')) {
                   4228:                       my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4229:                       if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4230:                           $value = &$gradesub($value);
                   4231:                       }
1.1173    kruse    4232:                       $prevattempts.='<td>'. $value.'&nbsp;</td>';
1.945     raeburn  4233:                   } else {
                   4234:                       $prevattempts.='<td>&nbsp;</td>';
                   4235:                   }
                   4236:               } else {
                   4237:                   my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4238:                   if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4239:                       $value = &$gradesub($value);
                   4240:                   }
1.1173    kruse    4241:                   $prevattempts.='<td>'.$value.'&nbsp;</td>';
1.945     raeburn  4242:               }
                   4243:           } else {
                   4244: 	      my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4245: 	      if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4246:                   $value = &$gradesub($value);
                   4247:               }
1.1173    kruse    4248: 	     $prevattempts.='<td>'.$value.'&nbsp;</td>';
1.945     raeburn  4249:           }
1.16      harris41 4250:       }
1.596     albertel 4251:       $prevattempts.= &end_data_table_row().&end_data_table();
1.1       albertel 4252:     } else {
1.596     albertel 4253:       $prevattempts=
                   4254: 	  &start_data_table().&start_data_table_row().
                   4255: 	  '<td>'.&mt('Nothing submitted - no attempts.').'</td>'.
                   4256: 	  &end_data_table_row().&end_data_table();
1.1       albertel 4257:     }
                   4258:   } else {
1.596     albertel 4259:     $prevattempts=
                   4260: 	  &start_data_table().&start_data_table_row().
                   4261: 	  '<td>'.&mt('No data.').'</td>'.
                   4262: 	  &end_data_table_row().&end_data_table();
1.1       albertel 4263:   }
1.10      albertel 4264: }
                   4265: 
1.581     albertel 4266: sub format_previous_attempt_value {
                   4267:     my ($key,$value) = @_;
1.1011    www      4268:     if (($key =~ /timestamp/) || ($key=~/duedate/)) {
1.1173    kruse    4269:         $value = &Apache::lonlocal::locallocaltime($value);
1.581     albertel 4270:     } elsif (ref($value) eq 'ARRAY') {
1.1173    kruse    4271:         $value = &HTML::Entities::encode('('.join(', ', @{ $value }).')','"<>&');
1.988     raeburn  4272:     } elsif ($key =~ /answerstring$/) {
                   4273:         my %answers = &Apache::lonnet::str2hash($value);
1.1173    kruse    4274:         my @answer = %answers;
                   4275:         %answers = map {&HTML::Entities::encode($_, '"<>&')} @answer;
1.988     raeburn  4276:         my @anskeys = sort(keys(%answers));
                   4277:         if (@anskeys == 1) {
                   4278:             my $answer = $answers{$anskeys[0]};
1.1001    raeburn  4279:             if ($answer =~ m{\0}) {
                   4280:                 $answer =~ s{\0}{,}g;
1.988     raeburn  4281:             }
                   4282:             my $tag_internal_answer_name = 'INTERNAL';
                   4283:             if ($anskeys[0] eq $tag_internal_answer_name) {
                   4284:                 $value = $answer; 
                   4285:             } else {
                   4286:                 $value = $anskeys[0].'='.$answer;
                   4287:             }
                   4288:         } else {
                   4289:             foreach my $ans (@anskeys) {
                   4290:                 my $answer = $answers{$ans};
1.1001    raeburn  4291:                 if ($answer =~ m{\0}) {
                   4292:                     $answer =~ s{\0}{,}g;
1.988     raeburn  4293:                 }
                   4294:                 $value .=  $ans.'='.$answer.'<br />';;
                   4295:             } 
                   4296:         }
1.581     albertel 4297:     } else {
1.1173    kruse    4298:         $value = &HTML::Entities::encode(&unescape($value), '"<>&');
1.581     albertel 4299:     }
                   4300:     return $value;
                   4301: }
                   4302: 
                   4303: 
1.107     albertel 4304: sub relative_to_absolute {
                   4305:     my ($url,$output)=@_;
                   4306:     my $parser=HTML::TokeParser->new(\$output);
                   4307:     my $token;
                   4308:     my $thisdir=$url;
                   4309:     my @rlinks=();
                   4310:     while ($token=$parser->get_token) {
                   4311: 	if ($token->[0] eq 'S') {
                   4312: 	    if ($token->[1] eq 'a') {
                   4313: 		if ($token->[2]->{'href'}) {
                   4314: 		    $rlinks[$#rlinks+1]=$token->[2]->{'href'};
                   4315: 		}
                   4316: 	    } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
                   4317: 		$rlinks[$#rlinks+1]=$token->[2]->{'src'};
                   4318: 	    } elsif ($token->[1] eq 'base') {
                   4319: 		$thisdir=$token->[2]->{'href'};
                   4320: 	    }
                   4321: 	}
                   4322:     }
                   4323:     $thisdir=~s-/[^/]*$--;
1.356     albertel 4324:     foreach my $link (@rlinks) {
1.726     raeburn  4325: 	unless (($link=~/^https?\:\/\//i) ||
1.356     albertel 4326: 		($link=~/^\//) ||
                   4327: 		($link=~/^javascript:/i) ||
                   4328: 		($link=~/^mailto:/i) ||
                   4329: 		($link=~/^\#/)) {
                   4330: 	    my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
                   4331: 	    $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107     albertel 4332: 	}
                   4333:     }
                   4334: # -------------------------------------------------- Deal with Applet codebases
                   4335:     $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
                   4336:     return $output;
                   4337: }
                   4338: 
1.112     bowersj2 4339: =pod
                   4340: 
1.648     raeburn  4341: =item * &get_student_view()
1.112     bowersj2 4342: 
                   4343: show a snapshot of what student was looking at
                   4344: 
                   4345: =cut
                   4346: 
1.10      albertel 4347: sub get_student_view {
1.186     albertel 4348:   my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114     www      4349:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 4350:   my (%form);
1.10      albertel 4351:   my @elements=('symb','courseid','domain','username');
                   4352:   foreach my $element (@elements) {
1.186     albertel 4353:       $form{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 4354:   }
1.186     albertel 4355:   if (defined($moreenv)) {
                   4356:       %form=(%form,%{$moreenv});
                   4357:   }
1.236     albertel 4358:   if (defined($target)) { $form{'grade_target'} = $target; }
1.107     albertel 4359:   $feedurl=&Apache::lonnet::clutter($feedurl);
1.650     www      4360:   my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11      albertel 4361:   $userview=~s/\<body[^\>]*\>//gi;
                   4362:   $userview=~s/\<\/body\>//gi;
                   4363:   $userview=~s/\<html\>//gi;
                   4364:   $userview=~s/\<\/html\>//gi;
                   4365:   $userview=~s/\<head\>//gi;
                   4366:   $userview=~s/\<\/head\>//gi;
                   4367:   $userview=~s/action\s*\=/would_be_action\=/gi;
1.107     albertel 4368:   $userview=&relative_to_absolute($feedurl,$userview);
1.650     www      4369:   if (wantarray) {
                   4370:      return ($userview,$response);
                   4371:   } else {
                   4372:      return $userview;
                   4373:   }
                   4374: }
                   4375: 
                   4376: sub get_student_view_with_retries {
                   4377:   my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
                   4378: 
                   4379:     my $ok = 0;                 # True if we got a good response.
                   4380:     my $content;
                   4381:     my $response;
                   4382: 
                   4383:     # Try to get the student_view done. within the retries count:
                   4384:     
                   4385:     do {
                   4386:          ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
                   4387:          $ok      = $response->is_success;
                   4388:          if (!$ok) {
                   4389:             &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
                   4390:          }
                   4391:          $retries--;
                   4392:     } while (!$ok && ($retries > 0));
                   4393:     
                   4394:     if (!$ok) {
                   4395:        $content = '';          # On error return an empty content.
                   4396:     }
1.651     www      4397:     if (wantarray) {
                   4398:        return ($content, $response);
                   4399:     } else {
                   4400:        return $content;
                   4401:     }
1.11      albertel 4402: }
                   4403: 
1.112     bowersj2 4404: =pod
                   4405: 
1.648     raeburn  4406: =item * &get_student_answers() 
1.112     bowersj2 4407: 
                   4408: show a snapshot of how student was answering problem
                   4409: 
                   4410: =cut
                   4411: 
1.11      albertel 4412: sub get_student_answers {
1.100     sakharuk 4413:   my ($symb,$username,$domain,$courseid,%form) = @_;
1.114     www      4414:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 4415:   my (%moreenv);
1.11      albertel 4416:   my @elements=('symb','courseid','domain','username');
                   4417:   foreach my $element (@elements) {
1.186     albertel 4418:     $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 4419:   }
1.186     albertel 4420:   $moreenv{'grade_target'}='answer';
                   4421:   %moreenv=(%form,%moreenv);
1.497     raeburn  4422:   $feedurl = &Apache::lonnet::clutter($feedurl);
                   4423:   my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10      albertel 4424:   return $userview;
1.1       albertel 4425: }
1.116     albertel 4426: 
                   4427: =pod
                   4428: 
                   4429: =item * &submlink()
                   4430: 
1.242     albertel 4431: Inputs: $text $uname $udom $symb $target
1.116     albertel 4432: 
                   4433: Returns: A link to grades.pm such as to see the SUBM view of a student
                   4434: 
                   4435: =cut
                   4436: 
                   4437: ###############################################
                   4438: sub submlink {
1.242     albertel 4439:     my ($text,$uname,$udom,$symb,$target)=@_;
1.116     albertel 4440:     if (!($uname && $udom)) {
                   4441: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 4442: 	    &Apache::lonnet::whichuser($symb);
1.116     albertel 4443: 	if (!$symb) { $symb=$cursymb; }
                   4444:     }
1.254     matthew  4445:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      4446:     $symb=&escape($symb);
1.960     bisitz   4447:     if ($target) { $target=" target=\"$target\""; }
                   4448:     return
                   4449:         '<a href="/adm/grades?command=submission'.
                   4450:         '&amp;symb='.$symb.
                   4451:         '&amp;student='.$uname.
                   4452:         '&amp;userdom='.$udom.'"'.
                   4453:         $target.'>'.$text.'</a>';
1.242     albertel 4454: }
                   4455: ##############################################
                   4456: 
                   4457: =pod
                   4458: 
                   4459: =item * &pgrdlink()
                   4460: 
                   4461: Inputs: $text $uname $udom $symb $target
                   4462: 
                   4463: Returns: A link to grades.pm such as to see the PGRD view of a student
                   4464: 
                   4465: =cut
                   4466: 
                   4467: ###############################################
                   4468: sub pgrdlink {
                   4469:     my $link=&submlink(@_);
                   4470:     $link=~s/(&command=submission)/$1&showgrading=yes/;
                   4471:     return $link;
                   4472: }
                   4473: ##############################################
                   4474: 
                   4475: =pod
                   4476: 
                   4477: =item * &pprmlink()
                   4478: 
                   4479: Inputs: $text $uname $udom $symb $target
                   4480: 
                   4481: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283     albertel 4482: student and a specific resource
1.242     albertel 4483: 
                   4484: =cut
                   4485: 
                   4486: ###############################################
                   4487: sub pprmlink {
                   4488:     my ($text,$uname,$udom,$symb,$target)=@_;
                   4489:     if (!($uname && $udom)) {
                   4490: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 4491: 	    &Apache::lonnet::whichuser($symb);
1.242     albertel 4492: 	if (!$symb) { $symb=$cursymb; }
                   4493:     }
1.254     matthew  4494:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      4495:     $symb=&escape($symb);
1.242     albertel 4496:     if ($target) { $target="target=\"$target\""; }
1.595     albertel 4497:     return '<a href="/adm/parmset?command=set&amp;'.
                   4498: 	'symb='.$symb.'&amp;uname='.$uname.
                   4499: 	'&amp;udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116     albertel 4500: }
                   4501: ##############################################
1.37      matthew  4502: 
1.112     bowersj2 4503: =pod
                   4504: 
                   4505: =back
                   4506: 
                   4507: =cut
                   4508: 
1.37      matthew  4509: ###############################################
1.51      www      4510: 
                   4511: 
                   4512: sub timehash {
1.687     raeburn  4513:     my ($thistime) = @_;
                   4514:     my $timezone = &Apache::lonlocal::gettimezone();
                   4515:     my $dt = DateTime->from_epoch(epoch => $thistime)
                   4516:                      ->set_time_zone($timezone);
                   4517:     my $wday = $dt->day_of_week();
                   4518:     if ($wday == 7) { $wday = 0; }
                   4519:     return ( 'second' => $dt->second(),
                   4520:              'minute' => $dt->minute(),
                   4521:              'hour'   => $dt->hour(),
                   4522:              'day'     => $dt->day_of_month(),
                   4523:              'month'   => $dt->month(),
                   4524:              'year'    => $dt->year(),
                   4525:              'weekday' => $wday,
                   4526:              'dayyear' => $dt->day_of_year(),
                   4527:              'dlsav'   => $dt->is_dst() );
1.51      www      4528: }
                   4529: 
1.370     www      4530: sub utc_string {
                   4531:     my ($date)=@_;
1.371     www      4532:     return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370     www      4533: }
                   4534: 
1.51      www      4535: sub maketime {
                   4536:     my %th=@_;
1.687     raeburn  4537:     my ($epoch_time,$timezone,$dt);
                   4538:     $timezone = &Apache::lonlocal::gettimezone();
                   4539:     eval {
                   4540:         $dt = DateTime->new( year   => $th{'year'},
                   4541:                              month  => $th{'month'},
                   4542:                              day    => $th{'day'},
                   4543:                              hour   => $th{'hour'},
                   4544:                              minute => $th{'minute'},
                   4545:                              second => $th{'second'},
                   4546:                              time_zone => $timezone,
                   4547:                          );
                   4548:     };
                   4549:     if (!$@) {
                   4550:         $epoch_time = $dt->epoch;
                   4551:         if ($epoch_time) {
                   4552:             return $epoch_time;
                   4553:         }
                   4554:     }
1.51      www      4555:     return POSIX::mktime(
                   4556:         ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210     www      4557:          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70      www      4558: }
                   4559: 
                   4560: #########################################
1.51      www      4561: 
                   4562: sub findallcourses {
1.482     raeburn  4563:     my ($roles,$uname,$udom) = @_;
1.355     albertel 4564:     my %roles;
                   4565:     if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348     albertel 4566:     my %courses;
1.51      www      4567:     my $now=time;
1.482     raeburn  4568:     if (!defined($uname)) {
                   4569:         $uname = $env{'user.name'};
                   4570:     }
                   4571:     if (!defined($udom)) {
                   4572:         $udom = $env{'user.domain'};
                   4573:     }
                   4574:     if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
1.1073    raeburn  4575:         my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
1.482     raeburn  4576:         if (!%roles) {
                   4577:             %roles = (
                   4578:                        cc => 1,
1.907     raeburn  4579:                        co => 1,
1.482     raeburn  4580:                        in => 1,
                   4581:                        ep => 1,
                   4582:                        ta => 1,
                   4583:                        cr => 1,
                   4584:                        st => 1,
                   4585:              );
                   4586:         }
                   4587:         foreach my $entry (keys(%roleshash)) {
                   4588:             my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
                   4589:             if ($trole =~ /^cr/) { 
                   4590:                 next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
                   4591:             } else {
                   4592:                 next if (!exists($roles{$trole}));
                   4593:             }
                   4594:             if ($tend) {
                   4595:                 next if ($tend < $now);
                   4596:             }
                   4597:             if ($tstart) {
                   4598:                 next if ($tstart > $now);
                   4599:             }
1.1058    raeburn  4600:             my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role);
1.482     raeburn  4601:             (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
1.1058    raeburn  4602:             my $value = $trole.'/'.$cdom.'/';
1.482     raeburn  4603:             if ($secpart eq '') {
                   4604:                 ($cnum,$role) = split(/_/,$cnumpart); 
                   4605:                 $sec = 'none';
1.1058    raeburn  4606:                 $value .= $cnum.'/';
1.482     raeburn  4607:             } else {
                   4608:                 $cnum = $cnumpart;
                   4609:                 ($sec,$role) = split(/_/,$secpart);
1.1058    raeburn  4610:                 $value .= $cnum.'/'.$sec;
                   4611:             }
                   4612:             if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
                   4613:                 unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
                   4614:                     push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
                   4615:                 }
                   4616:             } else {
                   4617:                 @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.490     raeburn  4618:             }
1.482     raeburn  4619:         }
                   4620:     } else {
                   4621:         foreach my $key (keys(%env)) {
1.483     albertel 4622: 	    if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
                   4623:                  $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482     raeburn  4624: 	        my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
                   4625: 	        next if ($role eq 'ca' || $role eq 'aa');
                   4626: 	        next if (%roles && !exists($roles{$role}));
                   4627: 	        my ($starttime,$endtime)=split(/\./,$env{$key});
                   4628:                 my $active=1;
                   4629:                 if ($starttime) {
                   4630: 		    if ($now<$starttime) { $active=0; }
                   4631:                 }
                   4632:                 if ($endtime) {
                   4633:                     if ($now>$endtime) { $active=0; }
                   4634:                 }
                   4635:                 if ($active) {
1.1058    raeburn  4636:                     my $value = $role.'/'.$cdom.'/'.$cnum.'/';
1.482     raeburn  4637:                     if ($sec eq '') {
                   4638:                         $sec = 'none';
1.1058    raeburn  4639:                     } else {
                   4640:                         $value .= $sec;
                   4641:                     }
                   4642:                     if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
                   4643:                         unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
                   4644:                             push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
                   4645:                         }
                   4646:                     } else {
                   4647:                         @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.482     raeburn  4648:                     }
1.474     raeburn  4649:                 }
                   4650:             }
1.51      www      4651:         }
                   4652:     }
1.474     raeburn  4653:     return %courses;
1.51      www      4654: }
1.37      matthew  4655: 
1.54      www      4656: ###############################################
1.474     raeburn  4657: 
                   4658: sub blockcheck {
1.1189    raeburn  4659:     my ($setters,$activity,$uname,$udom,$url,$is_course) = @_;
1.490     raeburn  4660: 
1.1189    raeburn  4661:     if (defined($udom) && defined($uname)) {
                   4662:         # If uname and udom are for a course, check for blocks in the course.
                   4663:         if (($is_course) || (&Apache::lonnet::is_course($udom,$uname))) {
                   4664:             my ($startblock,$endblock,$triggerblock) =
                   4665:                 &get_blocks($setters,$activity,$udom,$uname,$url);
                   4666:             return ($startblock,$endblock,$triggerblock);
                   4667:         }
                   4668:     } else {
1.490     raeburn  4669:         $udom = $env{'user.domain'};
                   4670:         $uname = $env{'user.name'};
                   4671:     }
                   4672: 
1.502     raeburn  4673:     my $startblock = 0;
                   4674:     my $endblock = 0;
1.1062    raeburn  4675:     my $triggerblock = '';
1.482     raeburn  4676:     my %live_courses = &findallcourses(undef,$uname,$udom);
1.474     raeburn  4677: 
1.490     raeburn  4678:     # If uname is for a user, and activity is course-specific, i.e.,
                   4679:     # boards, chat or groups, check for blocking in current course only.
1.474     raeburn  4680: 
1.490     raeburn  4681:     if (($activity eq 'boards' || $activity eq 'chat' ||
1.1189    raeburn  4682:          $activity eq 'groups' || $activity eq 'printout') &&
                   4683:         ($env{'request.course.id'})) {
1.490     raeburn  4684:         foreach my $key (keys(%live_courses)) {
                   4685:             if ($key ne $env{'request.course.id'}) {
                   4686:                 delete($live_courses{$key});
                   4687:             }
                   4688:         }
                   4689:     }
                   4690: 
                   4691:     my $otheruser = 0;
                   4692:     my %own_courses;
                   4693:     if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
                   4694:         # Resource belongs to user other than current user.
                   4695:         $otheruser = 1;
                   4696:         # Gather courses for current user
                   4697:         %own_courses = 
                   4698:             &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
                   4699:     }
                   4700: 
                   4701:     # Gather active course roles - course coordinator, instructor, 
                   4702:     # exam proctor, ta, student, or custom role.
1.474     raeburn  4703: 
                   4704:     foreach my $course (keys(%live_courses)) {
1.482     raeburn  4705:         my ($cdom,$cnum);
                   4706:         if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
                   4707:             $cdom = $env{'course.'.$course.'.domain'};
                   4708:             $cnum = $env{'course.'.$course.'.num'};
                   4709:         } else {
1.490     raeburn  4710:             ($cdom,$cnum) = split(/_/,$course); 
1.482     raeburn  4711:         }
                   4712:         my $no_ownblock = 0;
                   4713:         my $no_userblock = 0;
1.533     raeburn  4714:         if ($otheruser && $activity ne 'com') {
1.490     raeburn  4715:             # Check if current user has 'evb' priv for this
                   4716:             if (defined($own_courses{$course})) {
                   4717:                 foreach my $sec (keys(%{$own_courses{$course}})) {
                   4718:                     my $checkrole = 'cm./'.$cdom.'/'.$cnum;
                   4719:                     if ($sec ne 'none') {
                   4720:                         $checkrole .= '/'.$sec;
                   4721:                     }
                   4722:                     if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   4723:                         $no_ownblock = 1;
                   4724:                         last;
                   4725:                     }
                   4726:                 }
                   4727:             }
                   4728:             # if they have 'evb' priv and are currently not playing student
                   4729:             next if (($no_ownblock) &&
                   4730:                  ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
                   4731:         }
1.474     raeburn  4732:         foreach my $sec (keys(%{$live_courses{$course}})) {
1.482     raeburn  4733:             my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474     raeburn  4734:             if ($sec ne 'none') {
1.482     raeburn  4735:                 $checkrole .= '/'.$sec;
1.474     raeburn  4736:             }
1.490     raeburn  4737:             if ($otheruser) {
                   4738:                 # Resource belongs to user other than current user.
                   4739:                 # Assemble privs for that user, and check for 'evb' priv.
1.1058    raeburn  4740:                 my (%allroles,%userroles);
                   4741:                 if (ref($live_courses{$course}{$sec}) eq 'ARRAY') {
                   4742:                     foreach my $entry (@{$live_courses{$course}{$sec}}) { 
                   4743:                         my ($trole,$tdom,$tnum,$tsec);
                   4744:                         if ($entry =~ /^cr/) {
                   4745:                             ($trole,$tdom,$tnum,$tsec) = 
                   4746:                                 ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
                   4747:                         } else {
                   4748:                            ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
                   4749:                         }
                   4750:                         my ($spec,$area,$trest);
                   4751:                         $area = '/'.$tdom.'/'.$tnum;
                   4752:                         $trest = $tnum;
                   4753:                         if ($tsec ne '') {
                   4754:                             $area .= '/'.$tsec;
                   4755:                             $trest .= '/'.$tsec;
                   4756:                         }
                   4757:                         $spec = $trole.'.'.$area;
                   4758:                         if ($trole =~ /^cr/) {
                   4759:                             &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
                   4760:                                                               $tdom,$spec,$trest,$area);
                   4761:                         } else {
                   4762:                             &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
                   4763:                                                                 $tdom,$spec,$trest,$area);
                   4764:                         }
                   4765:                     }
                   4766:                     my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
                   4767:                     if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
                   4768:                         if ($1) {
                   4769:                             $no_userblock = 1;
                   4770:                             last;
                   4771:                         }
1.486     raeburn  4772:                     }
                   4773:                 }
1.490     raeburn  4774:             } else {
                   4775:                 # Resource belongs to current user
                   4776:                 # Check for 'evb' priv via lonnet::allowed().
1.482     raeburn  4777:                 if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   4778:                     $no_ownblock = 1;
                   4779:                     last;
                   4780:                 }
1.474     raeburn  4781:             }
                   4782:         }
                   4783:         # if they have the evb priv and are currently not playing student
1.482     raeburn  4784:         next if (($no_ownblock) &&
1.491     albertel 4785:                  ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482     raeburn  4786:         next if ($no_userblock);
1.474     raeburn  4787: 
1.866     kalberla 4788:         # Retrieve blocking times and identity of locker for course
1.490     raeburn  4789:         # of specified user, unless user has 'evb' privilege.
1.502     raeburn  4790:         
1.1062    raeburn  4791:         my ($start,$end,$trigger) = 
                   4792:             &get_blocks($setters,$activity,$cdom,$cnum,$url);
1.502     raeburn  4793:         if (($start != 0) && 
                   4794:             (($startblock == 0) || ($startblock > $start))) {
                   4795:             $startblock = $start;
1.1062    raeburn  4796:             if ($trigger ne '') {
                   4797:                 $triggerblock = $trigger;
                   4798:             }
1.502     raeburn  4799:         }
                   4800:         if (($end != 0)  &&
                   4801:             (($endblock == 0) || ($endblock < $end))) {
                   4802:             $endblock = $end;
1.1062    raeburn  4803:             if ($trigger ne '') {
                   4804:                 $triggerblock = $trigger;
                   4805:             }
1.502     raeburn  4806:         }
1.490     raeburn  4807:     }
1.1062    raeburn  4808:     return ($startblock,$endblock,$triggerblock);
1.490     raeburn  4809: }
                   4810: 
                   4811: sub get_blocks {
1.1062    raeburn  4812:     my ($setters,$activity,$cdom,$cnum,$url) = @_;
1.490     raeburn  4813:     my $startblock = 0;
                   4814:     my $endblock = 0;
1.1062    raeburn  4815:     my $triggerblock = '';
1.490     raeburn  4816:     my $course = $cdom.'_'.$cnum;
                   4817:     $setters->{$course} = {};
                   4818:     $setters->{$course}{'staff'} = [];
                   4819:     $setters->{$course}{'times'} = [];
1.1062    raeburn  4820:     $setters->{$course}{'triggers'} = [];
                   4821:     my (@blockers,%triggered);
                   4822:     my $now = time;
                   4823:     my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum);
                   4824:     if ($activity eq 'docs') {
                   4825:         @blockers = &Apache::lonnet::has_comm_blocking('bre',undef,$url,\%commblocks);
                   4826:         foreach my $block (@blockers) {
                   4827:             if ($block =~ /^firstaccess____(.+)$/) {
                   4828:                 my $item = $1;
                   4829:                 my $type = 'map';
                   4830:                 my $timersymb = $item;
                   4831:                 if ($item eq 'course') {
                   4832:                     $type = 'course';
                   4833:                 } elsif ($item =~ /___\d+___/) {
                   4834:                     $type = 'resource';
                   4835:                 } else {
                   4836:                     $timersymb = &Apache::lonnet::symbread($item);
                   4837:                 }
                   4838:                 my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
                   4839:                 my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
                   4840:                 $triggered{$block} = {
                   4841:                                        start => $start,
                   4842:                                        end   => $end,
                   4843:                                        type  => $type,
                   4844:                                      };
                   4845:             }
                   4846:         }
                   4847:     } else {
                   4848:         foreach my $block (keys(%commblocks)) {
                   4849:             if ($block =~ m/^(\d+)____(\d+)$/) { 
                   4850:                 my ($start,$end) = ($1,$2);
                   4851:                 if ($start <= time && $end >= time) {
                   4852:                     if (ref($commblocks{$block}) eq 'HASH') {
                   4853:                         if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
                   4854:                             if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
                   4855:                                 unless(grep(/^\Q$block\E$/,@blockers)) {
                   4856:                                     push(@blockers,$block);
                   4857:                                 }
                   4858:                             }
                   4859:                         }
                   4860:                     }
                   4861:                 }
                   4862:             } elsif ($block =~ /^firstaccess____(.+)$/) {
                   4863:                 my $item = $1;
                   4864:                 my $timersymb = $item; 
                   4865:                 my $type = 'map';
                   4866:                 if ($item eq 'course') {
                   4867:                     $type = 'course';
                   4868:                 } elsif ($item =~ /___\d+___/) {
                   4869:                     $type = 'resource';
                   4870:                 } else {
                   4871:                     $timersymb = &Apache::lonnet::symbread($item);
                   4872:                 }
                   4873:                 my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
                   4874:                 my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb}; 
                   4875:                 if ($start && $end) {
                   4876:                     if (($start <= time) && ($end >= time)) {
                   4877:                         unless (grep(/^\Q$block\E$/,@blockers)) {
                   4878:                             push(@blockers,$block);
                   4879:                             $triggered{$block} = {
                   4880:                                                    start => $start,
                   4881:                                                    end   => $end,
                   4882:                                                    type  => $type,
                   4883:                                                  };
                   4884:                         }
                   4885:                     }
1.490     raeburn  4886:                 }
1.1062    raeburn  4887:             }
                   4888:         }
                   4889:     }
                   4890:     foreach my $blocker (@blockers) {
                   4891:         my ($staff_name,$staff_dom,$title,$blocks) =
                   4892:             &parse_block_record($commblocks{$blocker});
                   4893:         push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
                   4894:         my ($start,$end,$triggertype);
                   4895:         if ($blocker =~ m/^(\d+)____(\d+)$/) {
                   4896:             ($start,$end) = ($1,$2);
                   4897:         } elsif (ref($triggered{$blocker}) eq 'HASH') {
                   4898:             $start = $triggered{$blocker}{'start'};
                   4899:             $end = $triggered{$blocker}{'end'};
                   4900:             $triggertype = $triggered{$blocker}{'type'};
                   4901:         }
                   4902:         if ($start) {
                   4903:             push(@{$$setters{$course}{'times'}}, [$start,$end]);
                   4904:             if ($triggertype) {
                   4905:                 push(@{$$setters{$course}{'triggers'}},$triggertype);
                   4906:             } else {
                   4907:                 push(@{$$setters{$course}{'triggers'}},0);
                   4908:             }
                   4909:             if ( ($startblock == 0) || ($startblock > $start) ) {
                   4910:                 $startblock = $start;
                   4911:                 if ($triggertype) {
                   4912:                     $triggerblock = $blocker;
1.474     raeburn  4913:                 }
                   4914:             }
1.1062    raeburn  4915:             if ( ($endblock == 0) || ($endblock < $end) ) {
                   4916:                $endblock = $end;
                   4917:                if ($triggertype) {
                   4918:                    $triggerblock = $blocker;
                   4919:                }
                   4920:             }
1.474     raeburn  4921:         }
                   4922:     }
1.1062    raeburn  4923:     return ($startblock,$endblock,$triggerblock);
1.474     raeburn  4924: }
                   4925: 
                   4926: sub parse_block_record {
                   4927:     my ($record) = @_;
                   4928:     my ($setuname,$setudom,$title,$blocks);
                   4929:     if (ref($record) eq 'HASH') {
                   4930:         ($setuname,$setudom) = split(/:/,$record->{'setter'});
                   4931:         $title = &unescape($record->{'event'});
                   4932:         $blocks = $record->{'blocks'};
                   4933:     } else {
                   4934:         my @data = split(/:/,$record,3);
                   4935:         if (scalar(@data) eq 2) {
                   4936:             $title = $data[1];
                   4937:             ($setuname,$setudom) = split(/@/,$data[0]);
                   4938:         } else {
                   4939:             ($setuname,$setudom,$title) = @data;
                   4940:         }
                   4941:         $blocks = { 'com' => 'on' };
                   4942:     }
                   4943:     return ($setuname,$setudom,$title,$blocks);
                   4944: }
                   4945: 
1.854     kalberla 4946: sub blocking_status {
1.1189    raeburn  4947:     my ($activity,$uname,$udom,$url,$is_course) = @_;
1.1061    raeburn  4948:     my %setters;
1.890     droeschl 4949: 
1.1061    raeburn  4950: # check for active blocking
1.1062    raeburn  4951:     my ($startblock,$endblock,$triggerblock) = 
1.1189    raeburn  4952:         &blockcheck(\%setters,$activity,$uname,$udom,$url,$is_course);
1.1062    raeburn  4953:     my $blocked = 0;
                   4954:     if ($startblock && $endblock) {
                   4955:         $blocked = 1;
                   4956:     }
1.890     droeschl 4957: 
1.1061    raeburn  4958: # caller just wants to know whether a block is active
                   4959:     if (!wantarray) { return $blocked; }
                   4960: 
                   4961: # build a link to a popup window containing the details
                   4962:     my $querystring  = "?activity=$activity";
                   4963: # $uname and $udom decide whose portfolio the user is trying to look at
1.1232    raeburn  4964:     if (($activity eq 'port') || ($activity eq 'passwd')) {
                   4965:         $querystring .= "&amp;udom=$udom"      if ($udom =~ /^$match_domain$/); 
                   4966:         $querystring .= "&amp;uname=$uname"    if ($uname =~ /^$match_username$/);
1.1062    raeburn  4967:     } elsif ($activity eq 'docs') {
                   4968:         $querystring .= '&amp;url='.&HTML::Entities::encode($url,'&"');
                   4969:     }
1.1061    raeburn  4970: 
                   4971:     my $output .= <<'END_MYBLOCK';
                   4972: function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
                   4973:     var options = "width=" + w + ",height=" + h + ",";
                   4974:     options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
                   4975:     options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
                   4976:     var newWin = window.open(url, wdwName, options);
                   4977:     newWin.focus();
                   4978: }
1.890     droeschl 4979: END_MYBLOCK
1.854     kalberla 4980: 
1.1061    raeburn  4981:     $output = Apache::lonhtmlcommon::scripttag($output);
1.890     droeschl 4982:   
1.1061    raeburn  4983:     my $popupUrl = "/adm/blockingstatus/$querystring";
1.1062    raeburn  4984:     my $text = &mt('Communication Blocked');
1.1217    raeburn  4985:     my $class = 'LC_comblock';
1.1062    raeburn  4986:     if ($activity eq 'docs') {
                   4987:         $text = &mt('Content Access Blocked');
1.1217    raeburn  4988:         $class = '';
1.1063    raeburn  4989:     } elsif ($activity eq 'printout') {
                   4990:         $text = &mt('Printing Blocked');
1.1232    raeburn  4991:     } elsif ($activity eq 'passwd') {
                   4992:         $text = &mt('Password Changing Blocked');
1.1062    raeburn  4993:     }
1.1061    raeburn  4994:     $output .= <<"END_BLOCK";
1.1217    raeburn  4995: <div class='$class'>
1.869     kalberla 4996:   <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890     droeschl 4997:   title='$text'>
                   4998:   <img class='LC_noBorder LC_middle' title='$text' src='/res/adm/pages/comblock.png' alt='$text'/></a>
1.869     kalberla 4999:   <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring' 
1.890     droeschl 5000:   title='$text'>$text</a>
1.867     kalberla 5001: </div>
                   5002: 
                   5003: END_BLOCK
1.474     raeburn  5004: 
1.1061    raeburn  5005:     return ($blocked, $output);
1.854     kalberla 5006: }
1.490     raeburn  5007: 
1.60      matthew  5008: ###############################################
                   5009: 
1.682     raeburn  5010: sub check_ip_acc {
1.1201    raeburn  5011:     my ($acc,$clientip)=@_;
1.682     raeburn  5012:     &Apache::lonxml::debug("acc is $acc");
                   5013:     if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
                   5014:         return 1;
                   5015:     }
1.1219    raeburn  5016:     my $allowed;
1.1201    raeburn  5017:     my $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'} || $clientip;
1.682     raeburn  5018: 
                   5019:     my $name;
1.1219    raeburn  5020:     my %access = (
                   5021:                      allowfrom => 1,
                   5022:                      denyfrom  => 0,
                   5023:                  );
                   5024:     my @allows;
                   5025:     my @denies;
                   5026:     foreach my $item (split(',',$acc)) {
                   5027:         $item =~ s/^\s*//;
                   5028:         $item =~ s/\s*$//;
                   5029:         my $pattern;
                   5030:         if ($item =~ /^\!(.+)$/) {
                   5031:             push(@denies,$1);
                   5032:         } else {
                   5033:             push(@allows,$item);
                   5034:         }
                   5035:    }
                   5036:    my $numdenies = scalar(@denies);
                   5037:    my $numallows = scalar(@allows);
                   5038:    my $count = 0;
                   5039:    foreach my $pattern (@denies,@allows) {
                   5040:         $count ++; 
                   5041:         my $acctype = 'allowfrom';
                   5042:         if ($count <= $numdenies) {
                   5043:             $acctype = 'denyfrom';
                   5044:         }
1.682     raeburn  5045:         if ($pattern =~ /\*$/) {
                   5046:             #35.8.*
                   5047:             $pattern=~s/\*//;
1.1219    raeburn  5048:             if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682     raeburn  5049:         } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
                   5050:             #35.8.3.[34-56]
                   5051:             my $low=$2;
                   5052:             my $high=$3;
                   5053:             $pattern=$1;
                   5054:             if ($ip =~ /^\Q$pattern\E/) {
                   5055:                 my $last=(split(/\./,$ip))[3];
1.1219    raeburn  5056:                 if ($last <=$high && $last >=$low) { $allowed=$access{$acctype}; }
1.682     raeburn  5057:             }
                   5058:         } elsif ($pattern =~ /^\*/) {
                   5059:             #*.msu.edu
                   5060:             $pattern=~s/\*//;
                   5061:             if (!defined($name)) {
                   5062:                 use Socket;
                   5063:                 my $netaddr=inet_aton($ip);
                   5064:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   5065:             }
1.1219    raeburn  5066:             if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
1.682     raeburn  5067:         } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
                   5068:             #127.0.0.1
1.1219    raeburn  5069:             if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682     raeburn  5070:         } else {
                   5071:             #some.name.com
                   5072:             if (!defined($name)) {
                   5073:                 use Socket;
                   5074:                 my $netaddr=inet_aton($ip);
                   5075:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   5076:             }
1.1219    raeburn  5077:             if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
                   5078:         }
                   5079:         if ($allowed =~ /^(0|1)$/) { last; }
                   5080:     }
                   5081:     if ($allowed eq '') {
                   5082:         if ($numdenies && !$numallows) {
                   5083:             $allowed = 1;
                   5084:         } else {
                   5085:             $allowed = 0;
1.682     raeburn  5086:         }
                   5087:     }
                   5088:     return $allowed;
                   5089: }
                   5090: 
                   5091: ###############################################
                   5092: 
1.60      matthew  5093: =pod
                   5094: 
1.112     bowersj2 5095: =head1 Domain Template Functions
                   5096: 
                   5097: =over 4
                   5098: 
                   5099: =item * &determinedomain()
1.60      matthew  5100: 
                   5101: Inputs: $domain (usually will be undef)
                   5102: 
1.63      www      5103: Returns: Determines which domain should be used for designs
1.60      matthew  5104: 
                   5105: =cut
1.54      www      5106: 
1.60      matthew  5107: ###############################################
1.63      www      5108: sub determinedomain {
                   5109:     my $domain=shift;
1.531     albertel 5110:     if (! $domain) {
1.60      matthew  5111:         # Determine domain if we have not been given one
1.893     raeburn  5112:         $domain = &Apache::lonnet::default_login_domain();
1.258     albertel 5113:         if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
                   5114:         if ($env{'request.role.domain'}) { 
                   5115:             $domain=$env{'request.role.domain'}; 
1.60      matthew  5116:         }
                   5117:     }
1.63      www      5118:     return $domain;
                   5119: }
                   5120: ###############################################
1.517     raeburn  5121: 
1.518     albertel 5122: sub devalidate_domconfig_cache {
                   5123:     my ($udom)=@_;
                   5124:     &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
                   5125: }
                   5126: 
                   5127: # ---------------------- Get domain configuration for a domain
                   5128: sub get_domainconf {
                   5129:     my ($udom) = @_;
                   5130:     my $cachetime=1800;
                   5131:     my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
                   5132:     if (defined($cached)) { return %{$result}; }
                   5133: 
                   5134:     my %domconfig = &Apache::lonnet::get_dom('configuration',
1.948     raeburn  5135: 					     ['login','rolecolors','autoenroll'],$udom);
1.632     raeburn  5136:     my (%designhash,%legacy);
1.518     albertel 5137:     if (keys(%domconfig) > 0) {
                   5138:         if (ref($domconfig{'login'}) eq 'HASH') {
1.632     raeburn  5139:             if (keys(%{$domconfig{'login'}})) {
                   5140:                 foreach my $key (keys(%{$domconfig{'login'}})) {
1.699     raeburn  5141:                     if (ref($domconfig{'login'}{$key}) eq 'HASH') {
1.1208    raeburn  5142:                         if (($key eq 'loginvia') || ($key eq 'headtag')) {
                   5143:                             if (ref($domconfig{'login'}{$key}) eq 'HASH') {
                   5144:                                 foreach my $hostname (keys(%{$domconfig{'login'}{$key}})) {
                   5145:                                     if (ref($domconfig{'login'}{$key}{$hostname}) eq 'HASH') {
                   5146:                                         if ($key eq 'loginvia') {
                   5147:                                             if ($domconfig{'login'}{'loginvia'}{$hostname}{'server'}) {
                   5148:                                                 my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
                   5149:                                                 $designhash{$udom.'.login.loginvia'} = $server;
                   5150:                                                 if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {
                   5151: 
                   5152:                                                     $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
                   5153:                                                 } else {
                   5154:                                                     $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
                   5155:                                                 }
1.948     raeburn  5156:                                             }
1.1208    raeburn  5157:                                         } elsif ($key eq 'headtag') {
                   5158:                                             if ($domconfig{'login'}{'headtag'}{$hostname}{'url'}) {
                   5159:                                                 $designhash{$udom.'.login.headtag_'.$hostname} = $domconfig{'login'}{'headtag'}{$hostname}{'url'};
1.948     raeburn  5160:                                             }
1.946     raeburn  5161:                                         }
1.1208    raeburn  5162:                                         if ($domconfig{'login'}{$key}{$hostname}{'exempt'}) {
                   5163:                                             $designhash{$udom.'.login.'.$key.'_exempt_'.$hostname} = $domconfig{'login'}{$key}{$hostname}{'exempt'};
                   5164:                                         }
1.946     raeburn  5165:                                     }
                   5166:                                 }
                   5167:                             }
                   5168:                         } else {
                   5169:                             foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
                   5170:                                 $designhash{$udom.'.login.'.$key.'_'.$img} = 
                   5171:                                     $domconfig{'login'}{$key}{$img};
                   5172:                             }
1.699     raeburn  5173:                         }
                   5174:                     } else {
                   5175:                         $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
                   5176:                     }
1.632     raeburn  5177:                 }
                   5178:             } else {
                   5179:                 $legacy{'login'} = 1;
1.518     albertel 5180:             }
1.632     raeburn  5181:         } else {
                   5182:             $legacy{'login'} = 1;
1.518     albertel 5183:         }
                   5184:         if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632     raeburn  5185:             if (keys(%{$domconfig{'rolecolors'}})) {
                   5186:                 foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
                   5187:                     if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
                   5188:                         foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
                   5189:                             $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
                   5190:                         }
1.518     albertel 5191:                     }
                   5192:                 }
1.632     raeburn  5193:             } else {
                   5194:                 $legacy{'rolecolors'} = 1;
1.518     albertel 5195:             }
1.632     raeburn  5196:         } else {
                   5197:             $legacy{'rolecolors'} = 1;
1.518     albertel 5198:         }
1.948     raeburn  5199:         if (ref($domconfig{'autoenroll'}) eq 'HASH') {
                   5200:             if ($domconfig{'autoenroll'}{'co-owners'}) {
                   5201:                 $designhash{$udom.'.autoassign.co-owners'}=$domconfig{'autoenroll'}{'co-owners'};
                   5202:             }
                   5203:         }
1.632     raeburn  5204:         if (keys(%legacy) > 0) {
                   5205:             my %legacyhash = &get_legacy_domconf($udom);
                   5206:             foreach my $item (keys(%legacyhash)) {
                   5207:                 if ($item =~ /^\Q$udom\E\.login/) {
                   5208:                     if ($legacy{'login'}) { 
                   5209:                         $designhash{$item} = $legacyhash{$item};
                   5210:                     }
                   5211:                 } else {
                   5212:                     if ($legacy{'rolecolors'}) {
                   5213:                         $designhash{$item} = $legacyhash{$item};
                   5214:                     }
1.518     albertel 5215:                 }
                   5216:             }
                   5217:         }
1.632     raeburn  5218:     } else {
                   5219:         %designhash = &get_legacy_domconf($udom); 
1.518     albertel 5220:     }
                   5221:     &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
                   5222: 				  $cachetime);
                   5223:     return %designhash;
                   5224: }
                   5225: 
1.632     raeburn  5226: sub get_legacy_domconf {
                   5227:     my ($udom) = @_;
                   5228:     my %legacyhash;
                   5229:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
                   5230:     my $designfile =  $designdir.'/'.$udom.'.tab';
                   5231:     if (-e $designfile) {
                   5232:         if ( open (my $fh,"<$designfile") ) {
                   5233:             while (my $line = <$fh>) {
                   5234:                 next if ($line =~ /^\#/);
                   5235:                 chomp($line);
                   5236:                 my ($key,$val)=(split(/\=/,$line));
                   5237:                 if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
                   5238:             }
                   5239:             close($fh);
                   5240:         }
                   5241:     }
1.1026    raeburn  5242:     if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/adm/lonDomLogos/'.$udom.'.gif') {
1.632     raeburn  5243:         $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
                   5244:     }
                   5245:     return %legacyhash;
                   5246: }
                   5247: 
1.63      www      5248: =pod
                   5249: 
1.112     bowersj2 5250: =item * &domainlogo()
1.63      www      5251: 
                   5252: Inputs: $domain (usually will be undef)
                   5253: 
                   5254: Returns: A link to a domain logo, if the domain logo exists.
                   5255: If the domain logo does not exist, a description of the domain.
                   5256: 
                   5257: =cut
1.112     bowersj2 5258: 
1.63      www      5259: ###############################################
                   5260: sub domainlogo {
1.517     raeburn  5261:     my $domain = &determinedomain(shift);
1.518     albertel 5262:     my %designhash = &get_domainconf($domain);    
1.517     raeburn  5263:     # See if there is a logo
                   5264:     if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519     raeburn  5265:         my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538     albertel 5266:         if ($imgsrc =~ m{^/(adm|res)/}) {
                   5267: 	    if ($imgsrc =~ m{^/res/}) {
                   5268: 		my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
                   5269: 		&Apache::lonnet::repcopy($local_name);
                   5270: 	    }
                   5271: 	   $imgsrc = &lonhttpdurl($imgsrc);
1.519     raeburn  5272:         } 
                   5273:         return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514     albertel 5274:     } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
                   5275:         return &Apache::lonnet::domain($domain,'description');
1.59      www      5276:     } else {
1.60      matthew  5277:         return '';
1.59      www      5278:     }
                   5279: }
1.63      www      5280: ##############################################
                   5281: 
                   5282: =pod
                   5283: 
1.112     bowersj2 5284: =item * &designparm()
1.63      www      5285: 
                   5286: Inputs: $which parameter; $domain (usually will be undef)
                   5287: 
                   5288: Returns: value of designparamter $which
                   5289: 
                   5290: =cut
1.112     bowersj2 5291: 
1.397     albertel 5292: 
1.400     albertel 5293: ##############################################
1.397     albertel 5294: sub designparm {
                   5295:     my ($which,$domain)=@_;
                   5296:     if (exists($env{'environment.color.'.$which})) {
1.817     bisitz   5297:         return $env{'environment.color.'.$which};
1.96      www      5298:     }
1.63      www      5299:     $domain=&determinedomain($domain);
1.1016    raeburn  5300:     my %domdesign;
                   5301:     unless ($domain eq 'public') {
                   5302:         %domdesign = &get_domainconf($domain);
                   5303:     }
1.520     raeburn  5304:     my $output;
1.517     raeburn  5305:     if ($domdesign{$domain.'.'.$which} ne '') {
1.817     bisitz   5306:         $output = $domdesign{$domain.'.'.$which};
1.63      www      5307:     } else {
1.520     raeburn  5308:         $output = $defaultdesign{$which};
                   5309:     }
                   5310:     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635     raeburn  5311:         ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538     albertel 5312:         if ($output =~ m{^/(adm|res)/}) {
1.817     bisitz   5313:             if ($output =~ m{^/res/}) {
                   5314:                 my $local_name = &Apache::lonnet::filelocation('',$output);
                   5315:                 &Apache::lonnet::repcopy($local_name);
                   5316:             }
1.520     raeburn  5317:             $output = &lonhttpdurl($output);
                   5318:         }
1.63      www      5319:     }
1.520     raeburn  5320:     return $output;
1.63      www      5321: }
1.59      www      5322: 
1.822     bisitz   5323: ##############################################
                   5324: =pod
                   5325: 
1.832     bisitz   5326: =item * &authorspace()
                   5327: 
1.1028    raeburn  5328: Inputs: $url (usually will be undef).
1.832     bisitz   5329: 
1.1132    raeburn  5330: Returns: Path to Authoring Space containing the resource or 
1.1028    raeburn  5331:          directory being viewed (or for which action is being taken). 
                   5332:          If $url is provided, and begins /priv/<domain>/<uname>
                   5333:          the path will be that portion of the $context argument.
                   5334:          Otherwise the path will be for the author space of the current
                   5335:          user when the current role is author, or for that of the 
                   5336:          co-author/assistant co-author space when the current role 
                   5337:          is co-author or assistant co-author.
1.832     bisitz   5338: 
                   5339: =cut
                   5340: 
                   5341: sub authorspace {
1.1028    raeburn  5342:     my ($url) = @_;
                   5343:     if ($url ne '') {
                   5344:         if ($url =~ m{^(/priv/$match_domain/$match_username/)}) {
                   5345:            return $1;
                   5346:         }
                   5347:     }
1.832     bisitz   5348:     my $caname = '';
1.1024    www      5349:     my $cadom = '';
1.1028    raeburn  5350:     if ($env{'request.role'} =~ /^(?:ca|aa)/) {
1.1024    www      5351:         ($cadom,$caname) =
1.832     bisitz   5352:             ($env{'request.role'}=~/($match_domain)\/($match_username)$/);
1.1028    raeburn  5353:     } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
1.832     bisitz   5354:         $caname = $env{'user.name'};
1.1024    www      5355:         $cadom = $env{'user.domain'};
1.832     bisitz   5356:     }
1.1028    raeburn  5357:     if (($caname ne '') && ($cadom ne '')) {
                   5358:         return "/priv/$cadom/$caname/";
                   5359:     }
                   5360:     return;
1.832     bisitz   5361: }
                   5362: 
                   5363: ##############################################
                   5364: =pod
                   5365: 
1.822     bisitz   5366: =item * &head_subbox()
                   5367: 
                   5368: Inputs: $content (contains HTML code with page functions, etc.)
                   5369: 
                   5370: Returns: HTML div with $content
                   5371:          To be included in page header
                   5372: 
                   5373: =cut
                   5374: 
                   5375: sub head_subbox {
                   5376:     my ($content)=@_;
                   5377:     my $output =
1.993     raeburn  5378:         '<div class="LC_head_subbox">'
1.822     bisitz   5379:        .$content
                   5380:        .'</div>'
                   5381: }
                   5382: 
                   5383: ##############################################
                   5384: =pod
                   5385: 
                   5386: =item * &CSTR_pageheader()
                   5387: 
1.1026    raeburn  5388: Input: (optional) filename from which breadcrumb trail is built.
                   5389:        In most cases no input as needed, as $env{'request.filename'}
                   5390:        is appropriate for use in building the breadcrumb trail.
1.822     bisitz   5391: 
                   5392: Returns: HTML div with CSTR path and recent box
1.1132    raeburn  5393:          To be included on Authoring Space pages
1.822     bisitz   5394: 
                   5395: =cut
                   5396: 
                   5397: sub CSTR_pageheader {
1.1026    raeburn  5398:     my ($trailfile) = @_;
                   5399:     if ($trailfile eq '') {
                   5400:         $trailfile = $env{'request.filename'};
                   5401:     }
                   5402: 
                   5403: # this is for resources; directories have customtitle, and crumbs
                   5404: # and select recent are created in lonpubdir.pm
                   5405: 
                   5406:     my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
1.1022    www      5407:     my ($udom,$uname,$thisdisfn)=
1.1113    raeburn  5408:         ($trailfile =~ m{^\Q$londocroot\E/priv/([^/]+)/([^/]+)(?:|/(.*))$});
1.1026    raeburn  5409:     my $formaction = "/priv/$udom/$uname/$thisdisfn";
                   5410:     $formaction =~ s{/+}{/}g;
1.822     bisitz   5411: 
                   5412:     my $parentpath = '';
                   5413:     my $lastitem = '';
                   5414:     if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
                   5415:         $parentpath = $1;
                   5416:         $lastitem = $2;
                   5417:     } else {
                   5418:         $lastitem = $thisdisfn;
                   5419:     }
1.921     bisitz   5420: 
                   5421:     my $output =
1.822     bisitz   5422:          '<div>'
                   5423:         .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
1.1132    raeburn  5424:         .'<b>'.&mt('Authoring Space:').'</b> '
1.822     bisitz   5425:         .'<form name="dirs" method="post" action="'.$formaction
1.921     bisitz   5426:         .'" target="_top">' #FIXME lonpubdir: target="_parent"
1.1024    www      5427:         .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv/'.$udom,undef,undef);
1.921     bisitz   5428: 
                   5429:     if ($lastitem) {
                   5430:         $output .=
                   5431:              '<span class="LC_filename">'
                   5432:             .$lastitem
                   5433:             .'</span>';
                   5434:     }
                   5435:     $output .=
                   5436:          '<br />'
1.822     bisitz   5437:         #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/','_top','/priv','','+1',1)."</b></tt><br />"
                   5438:         .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
                   5439:         .'</form>'
                   5440:         .&Apache::lonmenu::constspaceform()
                   5441:         .'</div>';
1.921     bisitz   5442: 
                   5443:     return $output;
1.822     bisitz   5444: }
                   5445: 
1.60      matthew  5446: ###############################################
                   5447: ###############################################
                   5448: 
                   5449: =pod
                   5450: 
1.112     bowersj2 5451: =back
                   5452: 
1.549     albertel 5453: =head1 HTML Helpers
1.112     bowersj2 5454: 
                   5455: =over 4
                   5456: 
                   5457: =item * &bodytag()
1.60      matthew  5458: 
                   5459: Returns a uniform header for LON-CAPA web pages.
                   5460: 
                   5461: Inputs: 
                   5462: 
1.112     bowersj2 5463: =over 4
                   5464: 
                   5465: =item * $title, A title to be displayed on the page.
                   5466: 
                   5467: =item * $function, the current role (can be undef).
                   5468: 
                   5469: =item * $addentries, extra parameters for the <body> tag.
                   5470: 
                   5471: =item * $bodyonly, if defined, only return the <body> tag.
                   5472: 
                   5473: =item * $domain, if defined, force a given domain.
                   5474: 
                   5475: =item * $forcereg, if page should register as content page (relevant for 
1.86      www      5476:             text interface only)
1.60      matthew  5477: 
1.814     bisitz   5478: =item * $no_nav_bar, if true, keep the 'what is this' info but remove the
                   5479:                      navigational links
1.317     albertel 5480: 
1.338     albertel 5481: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
                   5482: 
1.460     albertel 5483: =item * $args, optional argument valid values are
                   5484:             no_auto_mt_title -> prevents &mt()ing the title arg
                   5485: 
1.1096    raeburn  5486: =item * $advtoolsref, optional argument, ref to an array containing
                   5487:             inlineremote items to be added in "Functions" menu below
                   5488:             breadcrumbs.
                   5489: 
1.112     bowersj2 5490: =back
                   5491: 
1.60      matthew  5492: Returns: A uniform header for LON-CAPA web pages.  
                   5493: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
                   5494: If $bodyonly is undef or zero, an html string containing a <body> tag and 
                   5495: other decorations will be returned.
                   5496: 
                   5497: =cut
                   5498: 
1.54      www      5499: sub bodytag {
1.831     bisitz   5500:     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
1.1096    raeburn  5501:         $no_nav_bar,$bgcolor,$args,$advtoolsref)=@_;
1.339     albertel 5502: 
1.954     raeburn  5503:     my $public;
                   5504:     if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
                   5505:         || ($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
                   5506:         $public = 1;
                   5507:     }
1.460     albertel 5508:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.1154    raeburn  5509:     my $httphost = $args->{'use_absolute'};
1.339     albertel 5510: 
1.183     matthew  5511:     $function = &get_users_function() if (!$function);
1.339     albertel 5512:     my $img =    &designparm($function.'.img',$domain);
                   5513:     my $font =   &designparm($function.'.font',$domain);
                   5514:     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
                   5515: 
1.803     bisitz   5516:     my %design = ( 'style'   => 'margin-top: 0',
1.535     albertel 5517: 		   'bgcolor' => $pgbg,
1.339     albertel 5518: 		   'text'    => $font,
                   5519:                    'alink'   => &designparm($function.'.alink',$domain),
                   5520: 		   'vlink'   => &designparm($function.'.vlink',$domain),
                   5521: 		   'link'    => &designparm($function.'.link',$domain),);
1.438     albertel 5522:     @design{keys(%$addentries)} = @$addentries{keys(%$addentries)}; 
1.339     albertel 5523: 
1.63      www      5524:  # role and realm
1.1178    raeburn  5525:     my ($role,$realm) = split(m{\./},$env{'request.role'},2);
                   5526:     if ($realm) {
                   5527:         $realm = '/'.$realm;
                   5528:     }
1.378     raeburn  5529:     if ($role  eq 'ca') {
1.479     albertel 5530:         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500     albertel 5531:         $realm = &plainname($rname,$rdom);
1.378     raeburn  5532:     } 
1.55      www      5533: # realm
1.258     albertel 5534:     if ($env{'request.course.id'}) {
1.378     raeburn  5535:         if ($env{'request.role'} !~ /^cr/) {
                   5536:             $role = &Apache::lonnet::plaintext($role,&course_type());
                   5537:         }
1.898     raeburn  5538:         if ($env{'request.course.sec'}) {
                   5539:             $role .= ('&nbsp;'x2).'-&nbsp;'.&mt('section:').'&nbsp;'.$env{'request.course.sec'};
                   5540:         }   
1.359     albertel 5541: 	$realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378     raeburn  5542:     } else {
                   5543:         $role = &Apache::lonnet::plaintext($role);
1.54      www      5544:     }
1.433     albertel 5545: 
1.359     albertel 5546:     if (!$realm) { $realm='&nbsp;'; }
1.330     albertel 5547: 
1.438     albertel 5548:     my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329     albertel 5549: 
1.101     www      5550: # construct main body tag
1.359     albertel 5551:     my $bodytag = "<body $extra_body_attr>".
1.1235    raeburn  5552: 	&Apache::lontexconvert::init_math_support();
1.252     albertel 5553: 
1.1131    raeburn  5554:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   5555: 
1.1130    raeburn  5556:     if (($bodyonly) || ($no_nav_bar) || ($env{'form.inhibitmenu'} eq 'yes')) {
1.60      matthew  5557:         return $bodytag;
1.1130    raeburn  5558:     }
1.359     albertel 5559: 
1.954     raeburn  5560:     if ($public) {
1.433     albertel 5561: 	undef($role);
                   5562:     }
1.359     albertel 5563:     
1.762     bisitz   5564:     my $titleinfo = '<h1>'.$title.'</h1>';
1.359     albertel 5565:     #
                   5566:     # Extra info if you are the DC
                   5567:     my $dc_info = '';
                   5568:     if ($env{'user.adv'} && exists($env{'user.role.dc./'.
                   5569:                         $env{'course.'.$env{'request.course.id'}.
                   5570:                                  '.domain'}.'/'})) {
                   5571:         my $cid = $env{'request.course.id'};
1.917     raeburn  5572:         $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380     www      5573:         $dc_info =~ s/\s+$//;
1.359     albertel 5574:     }
                   5575: 
1.1237  ! raeburn  5576:     my $crstype;
        !          5577:     if ($env{'request.course.id'}) {
        !          5578:         $crstype = $env{'course.'.$env{'request.course.id'}.'.type'};
        !          5579:     } elsif ($args->{'crstype'}) {
        !          5580:         $crstype = $args->{'crstype'};
        !          5581:     }
        !          5582:     if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) {
        !          5583:         undef($role);
        !          5584:     } else {
        !          5585:         $role = '<span class="LC_nobreak">('.$role.')</span>' if $role;
        !          5586:     }
1.853     droeschl 5587: 
1.903     droeschl 5588:         if ($env{'request.state'} eq 'construct') { $forcereg=1; }
                   5589: 
                   5590:         #    if ($env{'request.state'} eq 'construct') {
                   5591:         #        $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
                   5592:         #    }
                   5593: 
1.1130    raeburn  5594:         $bodytag .= Apache::lonhtmlcommon::scripttag(
1.1154    raeburn  5595:             Apache::lonmenu::utilityfunctions($httphost), 'start');
1.359     albertel 5596: 
1.1237  ! raeburn  5597:         my ($left,$right) = Apache::lonmenu::primary_menu($crstype);
1.359     albertel 5598: 
1.916     droeschl 5599:         if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
1.917     raeburn  5600:              if ($dc_info) {
                   5601:                  $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
                   5602:              }
1.1130    raeburn  5603:              $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
1.916     droeschl 5604:                 <em>$realm</em> $dc_info</div>|;
1.903     droeschl 5605:             return $bodytag;
                   5606:         }
1.894     droeschl 5607: 
1.927     raeburn  5608:         unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
1.1130    raeburn  5609:             $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
1.927     raeburn  5610:         }
1.916     droeschl 5611: 
1.1130    raeburn  5612:         $bodytag .= $right;
1.852     droeschl 5613: 
1.917     raeburn  5614:         if ($dc_info) {
                   5615:             $dc_info = &dc_courseid_toggle($dc_info);
                   5616:         }
                   5617:         $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
1.916     droeschl 5618: 
1.1169    raeburn  5619:         #if directed to not display the secondary menu, don't.  
1.1168    raeburn  5620:         if ($args->{'no_secondary_menu'}) {
                   5621:             return $bodytag;
                   5622:         }
1.1169    raeburn  5623:         #don't show menus for public users
1.954     raeburn  5624:         if (!$public){
1.1154    raeburn  5625:             $bodytag .= Apache::lonmenu::secondary_menu($httphost);
1.903     droeschl 5626:             $bodytag .= Apache::lonmenu::serverform();
1.920     raeburn  5627:             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
                   5628:             if ($env{'request.state'} eq 'construct') {
1.962     droeschl 5629:                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,
1.920     raeburn  5630:                                 $args->{'bread_crumbs'});
1.1096    raeburn  5631:             } elsif ($forcereg) {
                   5632:                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
                   5633:                                                             $args->{'group'});
                   5634:             } else {
                   5635:                 $bodytag .= 
                   5636:                     &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
                   5637:                                                         $forcereg,$args->{'group'},
                   5638:                                                         $args->{'bread_crumbs'},
                   5639:                                                         $advtoolsref);
1.920     raeburn  5640:             }
1.903     droeschl 5641:         }else{
                   5642:             # this is to seperate menu from content when there's no secondary
                   5643:             # menu. Especially needed for public accessible ressources.
                   5644:             $bodytag .= '<hr style="clear:both" />';
                   5645:             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); 
1.235     raeburn  5646:         }
1.903     droeschl 5647: 
1.235     raeburn  5648:         return $bodytag;
1.182     matthew  5649: }
                   5650: 
1.917     raeburn  5651: sub dc_courseid_toggle {
                   5652:     my ($dc_info) = @_;
1.980     raeburn  5653:     return ' <span id="dccidtext" class="LC_cusr_subheading LC_nobreak">'.
1.1069    raeburn  5654:            '<a href="javascript:showCourseID();" class="LC_menubuttons_link">'.
1.917     raeburn  5655:            &mt('(More ...)').'</a></span>'.
                   5656:            '<div id="dccid" class="LC_dccid">'.$dc_info.'</div>';
                   5657: }
                   5658: 
1.330     albertel 5659: sub make_attr_string {
                   5660:     my ($register,$attr_ref) = @_;
                   5661: 
                   5662:     if ($attr_ref && !ref($attr_ref)) {
                   5663: 	die("addentries Must be a hash ref ".
                   5664: 	    join(':',caller(1))." ".
                   5665: 	    join(':',caller(0))." ");
                   5666:     }
                   5667: 
                   5668:     if ($register) {
1.339     albertel 5669: 	my ($on_load,$on_unload);
                   5670: 	foreach my $key (keys(%{$attr_ref})) {
                   5671: 	    if      (lc($key) eq 'onload') {
                   5672: 		$on_load.=$attr_ref->{$key}.';';
                   5673: 		delete($attr_ref->{$key});
                   5674: 
                   5675: 	    } elsif (lc($key) eq 'onunload') {
                   5676: 		$on_unload.=$attr_ref->{$key}.';';
                   5677: 		delete($attr_ref->{$key});
                   5678: 	    }
                   5679: 	}
1.953     droeschl 5680: 	$attr_ref->{'onload'}  = $on_load;
                   5681: 	$attr_ref->{'onunload'}= $on_unload;
1.330     albertel 5682:     }
1.339     albertel 5683: 
1.330     albertel 5684:     my $attr_string;
1.1159    raeburn  5685:     foreach my $attr (sort(keys(%$attr_ref))) {
1.330     albertel 5686: 	$attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
                   5687:     }
                   5688:     return $attr_string;
                   5689: }
                   5690: 
                   5691: 
1.182     matthew  5692: ###############################################
1.251     albertel 5693: ###############################################
                   5694: 
                   5695: =pod
                   5696: 
                   5697: =item * &endbodytag()
                   5698: 
                   5699: Returns a uniform footer for LON-CAPA web pages.
                   5700: 
1.635     raeburn  5701: Inputs: 1 - optional reference to an args hash
                   5702: If in the hash, key for noredirectlink has a value which evaluates to true,
                   5703: a 'Continue' link is not displayed if the page contains an
                   5704: internal redirect in the <head></head> section,
                   5705: i.e., $env{'internal.head.redirect'} exists   
1.251     albertel 5706: 
                   5707: =cut
                   5708: 
                   5709: sub endbodytag {
1.635     raeburn  5710:     my ($args) = @_;
1.1080    raeburn  5711:     my $endbodytag;
                   5712:     unless ((ref($args) eq 'HASH') && ($args->{'notbody'})) {
                   5713:         $endbodytag='</body>';
                   5714:     }
1.315     albertel 5715:     if ( exists( $env{'internal.head.redirect'} ) ) {
1.635     raeburn  5716:         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
                   5717: 	    $endbodytag=
                   5718: 	        "<br /><a href=\"$env{'internal.head.redirect'}\">".
                   5719: 	        &mt('Continue').'</a>'.
                   5720: 	        $endbodytag;
                   5721:         }
1.315     albertel 5722:     }
1.251     albertel 5723:     return $endbodytag;
                   5724: }
                   5725: 
1.352     albertel 5726: =pod
                   5727: 
                   5728: =item * &standard_css()
                   5729: 
                   5730: Returns a style sheet
                   5731: 
                   5732: Inputs: (all optional)
                   5733:             domain         -> force to color decorate a page for a specific
                   5734:                                domain
                   5735:             function       -> force usage of a specific rolish color scheme
                   5736:             bgcolor        -> override the default page bgcolor
                   5737: 
                   5738: =cut
                   5739: 
1.343     albertel 5740: sub standard_css {
1.345     albertel 5741:     my ($function,$domain,$bgcolor) = @_;
1.352     albertel 5742:     $function  = &get_users_function() if (!$function);
                   5743:     my $img    = &designparm($function.'.img',   $domain);
                   5744:     my $tabbg  = &designparm($function.'.tabbg', $domain);
                   5745:     my $font   = &designparm($function.'.font',  $domain);
1.801     tempelho 5746:     my $fontmenu = &designparm($function.'.fontmenu', $domain);
1.791     tempelho 5747: #second colour for later usage
1.345     albertel 5748:     my $sidebg = &designparm($function.'.sidebg',$domain);
1.382     albertel 5749:     my $pgbg_or_bgcolor =
                   5750: 	         $bgcolor ||
1.352     albertel 5751: 	         &designparm($function.'.pgbg',  $domain);
1.382     albertel 5752:     my $pgbg   = &designparm($function.'.pgbg',  $domain);
1.352     albertel 5753:     my $alink  = &designparm($function.'.alink', $domain);
                   5754:     my $vlink  = &designparm($function.'.vlink', $domain);
                   5755:     my $link   = &designparm($function.'.link',  $domain);
                   5756: 
1.602     albertel 5757:     my $sans                 = 'Verdana,Arial,Helvetica,sans-serif';
1.395     albertel 5758:     my $mono                 = 'monospace';
1.850     bisitz   5759:     my $data_table_head      = $sidebg;
                   5760:     my $data_table_light     = '#FAFAFA';
1.1060    bisitz   5761:     my $data_table_dark      = '#E0E0E0';
1.470     banghart 5762:     my $data_table_darker    = '#CCCCCC';
1.349     albertel 5763:     my $data_table_highlight = '#FFFF00';
1.352     albertel 5764:     my $mail_new             = '#FFBB77';
                   5765:     my $mail_new_hover       = '#DD9955';
                   5766:     my $mail_read            = '#BBBB77';
                   5767:     my $mail_read_hover      = '#999944';
                   5768:     my $mail_replied         = '#AAAA88';
                   5769:     my $mail_replied_hover   = '#888855';
                   5770:     my $mail_other           = '#99BBBB';
                   5771:     my $mail_other_hover     = '#669999';
1.391     albertel 5772:     my $table_header         = '#DDDDDD';
1.489     raeburn  5773:     my $feedback_link_bg     = '#BBBBBB';
1.911     bisitz   5774:     my $lg_border_color      = '#C8C8C8';
1.952     onken    5775:     my $button_hover         = '#BF2317';
1.392     albertel 5776: 
1.608     albertel 5777:     my $border = ($env{'browser.type'} eq 'explorer' ||
1.911     bisitz   5778:       $env{'browser.type'} eq 'safari'     ) ? '0 2px 0 2px'
                   5779:                                              : '0 3px 0 4px';
1.448     albertel 5780: 
1.523     albertel 5781: 
1.343     albertel 5782:     return <<END;
1.947     droeschl 5783: 
                   5784: /* needed for iframe to allow 100% height in FF */
                   5785: body, html { 
                   5786:     margin: 0;
                   5787:     padding: 0 0.5%;
                   5788:     height: 99%; /* to avoid scrollbars */
                   5789: }
                   5790: 
1.795     www      5791: body {
1.911     bisitz   5792:   font-family: $sans;
                   5793:   line-height:130%;
                   5794:   font-size:0.83em;
                   5795:   color:$font;
1.795     www      5796: }
                   5797: 
1.959     onken    5798: a:focus,
                   5799: a:focus img {
1.795     www      5800:   color: red;
                   5801: }
1.698     harmsja  5802: 
1.911     bisitz   5803: form, .inline {
                   5804:   display: inline;
1.795     www      5805: }
1.721     harmsja  5806: 
1.795     www      5807: .LC_right {
1.911     bisitz   5808:   text-align:right;
1.795     www      5809: }
                   5810: 
                   5811: .LC_middle {
1.911     bisitz   5812:   vertical-align:middle;
1.795     www      5813: }
1.721     harmsja  5814: 
1.1130    raeburn  5815: .LC_floatleft {
                   5816:   float: left;
                   5817: }
                   5818: 
                   5819: .LC_floatright {
                   5820:   float: right;
                   5821: }
                   5822: 
1.911     bisitz   5823: .LC_400Box {
                   5824:   width:400px;
                   5825: }
1.721     harmsja  5826: 
1.947     droeschl 5827: .LC_iframecontainer {
                   5828:     width: 98%;
                   5829:     margin: 0;
                   5830:     position: fixed;
                   5831:     top: 8.5em;
                   5832:     bottom: 0;
                   5833: }
                   5834: 
                   5835: .LC_iframecontainer iframe{
                   5836:     border: none;
                   5837:     width: 100%;
                   5838:     height: 100%;
                   5839: }
                   5840: 
1.778     bisitz   5841: .LC_filename {
                   5842:   font-family: $mono;
                   5843:   white-space:pre;
1.921     bisitz   5844:   font-size: 120%;
1.778     bisitz   5845: }
                   5846: 
                   5847: .LC_fileicon {
                   5848:   border: none;
                   5849:   height: 1.3em;
                   5850:   vertical-align: text-bottom;
                   5851:   margin-right: 0.3em;
                   5852:   text-decoration:none;
                   5853: }
                   5854: 
1.1008    www      5855: .LC_setting {
                   5856:   text-decoration:underline;
                   5857: }
                   5858: 
1.350     albertel 5859: .LC_error {
                   5860:   color: red;
                   5861: }
1.795     www      5862: 
1.1097    bisitz   5863: .LC_warning {
                   5864:   color: darkorange;
                   5865: }
                   5866: 
1.457     albertel 5867: .LC_diff_removed {
1.733     bisitz   5868:   color: red;
1.394     albertel 5869: }
1.532     albertel 5870: 
                   5871: .LC_info,
1.457     albertel 5872: .LC_success,
                   5873: .LC_diff_added {
1.350     albertel 5874:   color: green;
                   5875: }
1.795     www      5876: 
1.802     bisitz   5877: div.LC_confirm_box {
                   5878:   background-color: #FAFAFA;
                   5879:   border: 1px solid $lg_border_color;
                   5880:   margin-right: 0;
                   5881:   padding: 5px;
                   5882: }
                   5883: 
                   5884: div.LC_confirm_box .LC_error img,
                   5885: div.LC_confirm_box .LC_success img {
                   5886:   vertical-align: middle;
                   5887: }
                   5888: 
1.440     albertel 5889: .LC_icon {
1.771     droeschl 5890:   border: none;
1.790     droeschl 5891:   vertical-align: middle;
1.771     droeschl 5892: }
                   5893: 
1.543     albertel 5894: .LC_docs_spacer {
                   5895:   width: 25px;
                   5896:   height: 1px;
1.771     droeschl 5897:   border: none;
1.543     albertel 5898: }
1.346     albertel 5899: 
1.532     albertel 5900: .LC_internal_info {
1.735     bisitz   5901:   color: #999999;
1.532     albertel 5902: }
                   5903: 
1.794     www      5904: .LC_discussion {
1.1050    www      5905:   background: $data_table_dark;
1.911     bisitz   5906:   border: 1px solid black;
                   5907:   margin: 2px;
1.794     www      5908: }
                   5909: 
                   5910: .LC_disc_action_left {
1.1050    www      5911:   background: $sidebg;
1.911     bisitz   5912:   text-align: left;
1.1050    www      5913:   padding: 4px;
                   5914:   margin: 2px;
1.794     www      5915: }
                   5916: 
                   5917: .LC_disc_action_right {
1.1050    www      5918:   background: $sidebg;
1.911     bisitz   5919:   text-align: right;
1.1050    www      5920:   padding: 4px;
                   5921:   margin: 2px;
1.794     www      5922: }
                   5923: 
                   5924: .LC_disc_new_item {
1.911     bisitz   5925:   background: white;
                   5926:   border: 2px solid red;
1.1050    www      5927:   margin: 4px;
                   5928:   padding: 4px;
1.794     www      5929: }
                   5930: 
                   5931: .LC_disc_old_item {
1.911     bisitz   5932:   background: white;
1.1050    www      5933:   margin: 4px;
                   5934:   padding: 4px;
1.794     www      5935: }
                   5936: 
1.458     albertel 5937: table.LC_pastsubmission {
                   5938:   border: 1px solid black;
                   5939:   margin: 2px;
                   5940: }
                   5941: 
1.924     bisitz   5942: table#LC_menubuttons {
1.345     albertel 5943:   width: 100%;
                   5944:   background: $pgbg;
1.392     albertel 5945:   border: 2px;
1.402     albertel 5946:   border-collapse: separate;
1.803     bisitz   5947:   padding: 0;
1.345     albertel 5948: }
1.392     albertel 5949: 
1.801     tempelho 5950: table#LC_title_bar a {
                   5951:   color: $fontmenu;
                   5952: }
1.836     bisitz   5953: 
1.807     droeschl 5954: table#LC_title_bar {
1.819     tempelho 5955:   clear: both;
1.836     bisitz   5956:   display: none;
1.807     droeschl 5957: }
                   5958: 
1.795     www      5959: table#LC_title_bar,
1.933     droeschl 5960: table.LC_breadcrumbs, /* obsolete? */
1.393     albertel 5961: table#LC_title_bar.LC_with_remote {
1.359     albertel 5962:   width: 100%;
1.392     albertel 5963:   border-color: $pgbg;
                   5964:   border-style: solid;
                   5965:   border-width: $border;
1.379     albertel 5966:   background: $pgbg;
1.801     tempelho 5967:   color: $fontmenu;
1.392     albertel 5968:   border-collapse: collapse;
1.803     bisitz   5969:   padding: 0;
1.819     tempelho 5970:   margin: 0;
1.359     albertel 5971: }
1.795     www      5972: 
1.933     droeschl 5973: ul.LC_breadcrumb_tools_outerlist {
1.913     droeschl 5974:     margin: 0;
                   5975:     padding: 0;
1.933     droeschl 5976:     position: relative;
                   5977:     list-style: none;
1.913     droeschl 5978: }
1.933     droeschl 5979: ul.LC_breadcrumb_tools_outerlist li {
1.913     droeschl 5980:     display: inline;
                   5981: }
1.933     droeschl 5982: 
                   5983: .LC_breadcrumb_tools_navigation {
1.913     droeschl 5984:     padding: 0;
1.933     droeschl 5985:     margin: 0;
                   5986:     float: left;
1.913     droeschl 5987: }
1.933     droeschl 5988: .LC_breadcrumb_tools_tools {
                   5989:     padding: 0;
                   5990:     margin: 0;
1.913     droeschl 5991:     float: right;
                   5992: }
                   5993: 
1.359     albertel 5994: table#LC_title_bar td {
                   5995:   background: $tabbg;
                   5996: }
1.795     www      5997: 
1.911     bisitz   5998: table#LC_menubuttons img {
1.803     bisitz   5999:   border: none;
1.346     albertel 6000: }
1.795     www      6001: 
1.842     droeschl 6002: .LC_breadcrumbs_component {
1.911     bisitz   6003:   float: right;
                   6004:   margin: 0 1em;
1.357     albertel 6005: }
1.842     droeschl 6006: .LC_breadcrumbs_component img {
1.911     bisitz   6007:   vertical-align: middle;
1.777     tempelho 6008: }
1.795     www      6009: 
1.383     albertel 6010: td.LC_table_cell_checkbox {
                   6011:   text-align: center;
                   6012: }
1.795     www      6013: 
                   6014: .LC_fontsize_small {
1.911     bisitz   6015:   font-size: 70%;
1.705     tempelho 6016: }
                   6017: 
1.844     bisitz   6018: #LC_breadcrumbs {
1.911     bisitz   6019:   clear:both;
                   6020:   background: $sidebg;
                   6021:   border-bottom: 1px solid $lg_border_color;
                   6022:   line-height: 2.5em;
1.933     droeschl 6023:   overflow: hidden;
1.911     bisitz   6024:   margin: 0;
                   6025:   padding: 0;
1.995     raeburn  6026:   text-align: left;
1.819     tempelho 6027: }
1.862     bisitz   6028: 
1.1098    bisitz   6029: .LC_head_subbox, .LC_actionbox {
1.911     bisitz   6030:   clear:both;
                   6031:   background: #F8F8F8; /* $sidebg; */
1.915     droeschl 6032:   border: 1px solid $sidebg;
1.1098    bisitz   6033:   margin: 0 0 10px 0;
1.966     bisitz   6034:   padding: 3px;
1.995     raeburn  6035:   text-align: left;
1.822     bisitz   6036: }
                   6037: 
1.795     www      6038: .LC_fontsize_medium {
1.911     bisitz   6039:   font-size: 85%;
1.705     tempelho 6040: }
                   6041: 
1.795     www      6042: .LC_fontsize_large {
1.911     bisitz   6043:   font-size: 120%;
1.705     tempelho 6044: }
                   6045: 
1.346     albertel 6046: .LC_menubuttons_inline_text {
                   6047:   color: $font;
1.698     harmsja  6048:   font-size: 90%;
1.701     harmsja  6049:   padding-left:3px;
1.346     albertel 6050: }
                   6051: 
1.934     droeschl 6052: .LC_menubuttons_inline_text img{
                   6053:   vertical-align: middle;
                   6054: }
                   6055: 
1.1051    www      6056: li.LC_menubuttons_inline_text img {
1.951     onken    6057:   cursor:pointer;
1.1002    droeschl 6058:   text-decoration: none;
1.951     onken    6059: }
                   6060: 
1.526     www      6061: .LC_menubuttons_link {
                   6062:   text-decoration: none;
                   6063: }
1.795     www      6064: 
1.522     albertel 6065: .LC_menubuttons_category {
1.521     www      6066:   color: $font;
1.526     www      6067:   background: $pgbg;
1.521     www      6068:   font-size: larger;
                   6069:   font-weight: bold;
                   6070: }
                   6071: 
1.346     albertel 6072: td.LC_menubuttons_text {
1.911     bisitz   6073:   color: $font;
1.346     albertel 6074: }
1.706     harmsja  6075: 
1.346     albertel 6076: .LC_current_location {
                   6077:   background: $tabbg;
                   6078: }
1.795     www      6079: 
1.938     bisitz   6080: table.LC_data_table {
1.347     albertel 6081:   border: 1px solid #000000;
1.402     albertel 6082:   border-collapse: separate;
1.426     albertel 6083:   border-spacing: 1px;
1.610     albertel 6084:   background: $pgbg;
1.347     albertel 6085: }
1.795     www      6086: 
1.422     albertel 6087: .LC_data_table_dense {
                   6088:   font-size: small;
                   6089: }
1.795     www      6090: 
1.507     raeburn  6091: table.LC_nested_outer {
                   6092:   border: 1px solid #000000;
1.589     raeburn  6093:   border-collapse: collapse;
1.803     bisitz   6094:   border-spacing: 0;
1.507     raeburn  6095:   width: 100%;
                   6096: }
1.795     www      6097: 
1.879     raeburn  6098: table.LC_innerpickbox,
1.507     raeburn  6099: table.LC_nested {
1.803     bisitz   6100:   border: none;
1.589     raeburn  6101:   border-collapse: collapse;
1.803     bisitz   6102:   border-spacing: 0;
1.507     raeburn  6103:   width: 100%;
                   6104: }
1.795     www      6105: 
1.911     bisitz   6106: table.LC_data_table tr th,
                   6107: table.LC_calendar tr th,
1.879     raeburn  6108: table.LC_prior_tries tr th,
                   6109: table.LC_innerpickbox tr th {
1.349     albertel 6110:   font-weight: bold;
                   6111:   background-color: $data_table_head;
1.801     tempelho 6112:   color:$fontmenu;
1.701     harmsja  6113:   font-size:90%;
1.347     albertel 6114: }
1.795     www      6115: 
1.879     raeburn  6116: table.LC_innerpickbox tr th,
                   6117: table.LC_innerpickbox tr td {
                   6118:   vertical-align: top;
                   6119: }
                   6120: 
1.711     raeburn  6121: table.LC_data_table tr.LC_info_row > td {
1.735     bisitz   6122:   background-color: #CCCCCC;
1.711     raeburn  6123:   font-weight: bold;
                   6124:   text-align: left;
                   6125: }
1.795     www      6126: 
1.912     bisitz   6127: table.LC_data_table tr.LC_odd_row > td {
                   6128:   background-color: $data_table_light;
                   6129:   padding: 2px;
                   6130:   vertical-align: top;
                   6131: }
                   6132: 
1.809     bisitz   6133: table.LC_pick_box tr > td.LC_odd_row {
1.349     albertel 6134:   background-color: $data_table_light;
1.912     bisitz   6135:   vertical-align: top;
                   6136: }
                   6137: 
                   6138: table.LC_data_table tr.LC_even_row > td {
                   6139:   background-color: $data_table_dark;
1.425     albertel 6140:   padding: 2px;
1.900     bisitz   6141:   vertical-align: top;
1.347     albertel 6142: }
1.795     www      6143: 
1.809     bisitz   6144: table.LC_pick_box tr > td.LC_even_row {
1.349     albertel 6145:   background-color: $data_table_dark;
1.900     bisitz   6146:   vertical-align: top;
1.347     albertel 6147: }
1.795     www      6148: 
1.425     albertel 6149: table.LC_data_table tr.LC_data_table_highlight td {
                   6150:   background-color: $data_table_darker;
                   6151: }
1.795     www      6152: 
1.639     raeburn  6153: table.LC_data_table tr td.LC_leftcol_header {
                   6154:   background-color: $data_table_head;
                   6155:   font-weight: bold;
                   6156: }
1.795     www      6157: 
1.451     albertel 6158: table.LC_data_table tr.LC_empty_row td,
1.507     raeburn  6159: table.LC_nested tr.LC_empty_row td {
1.421     albertel 6160:   font-weight: bold;
                   6161:   font-style: italic;
                   6162:   text-align: center;
                   6163:   padding: 8px;
1.347     albertel 6164: }
1.795     www      6165: 
1.1114    raeburn  6166: table.LC_data_table tr.LC_empty_row td,
                   6167: table.LC_data_table tr.LC_footer_row td {
1.940     bisitz   6168:   background-color: $sidebg;
                   6169: }
                   6170: 
                   6171: table.LC_nested tr.LC_empty_row td {
                   6172:   background-color: #FFFFFF;
                   6173: }
                   6174: 
1.890     droeschl 6175: table.LC_caption {
                   6176: }
                   6177: 
1.507     raeburn  6178: table.LC_nested tr.LC_empty_row td {
1.465     albertel 6179:   padding: 4ex
                   6180: }
1.795     www      6181: 
1.507     raeburn  6182: table.LC_nested_outer tr th {
                   6183:   font-weight: bold;
1.801     tempelho 6184:   color:$fontmenu;
1.507     raeburn  6185:   background-color: $data_table_head;
1.701     harmsja  6186:   font-size: small;
1.507     raeburn  6187:   border-bottom: 1px solid #000000;
                   6188: }
1.795     www      6189: 
1.507     raeburn  6190: table.LC_nested_outer tr td.LC_subheader {
                   6191:   background-color: $data_table_head;
                   6192:   font-weight: bold;
                   6193:   font-size: small;
                   6194:   border-bottom: 1px solid #000000;
                   6195:   text-align: right;
1.451     albertel 6196: }
1.795     www      6197: 
1.507     raeburn  6198: table.LC_nested tr.LC_info_row td {
1.735     bisitz   6199:   background-color: #CCCCCC;
1.451     albertel 6200:   font-weight: bold;
                   6201:   font-size: small;
1.507     raeburn  6202:   text-align: center;
                   6203: }
1.795     www      6204: 
1.589     raeburn  6205: table.LC_nested tr.LC_info_row td.LC_left_item,
                   6206: table.LC_nested_outer tr th.LC_left_item {
1.507     raeburn  6207:   text-align: left;
1.451     albertel 6208: }
1.795     www      6209: 
1.507     raeburn  6210: table.LC_nested td {
1.735     bisitz   6211:   background-color: #FFFFFF;
1.451     albertel 6212:   font-size: small;
1.507     raeburn  6213: }
1.795     www      6214: 
1.507     raeburn  6215: table.LC_nested_outer tr th.LC_right_item,
                   6216: table.LC_nested tr.LC_info_row td.LC_right_item,
                   6217: table.LC_nested tr.LC_odd_row td.LC_right_item,
                   6218: table.LC_nested tr td.LC_right_item {
1.451     albertel 6219:   text-align: right;
                   6220: }
                   6221: 
1.507     raeburn  6222: table.LC_nested tr.LC_odd_row td {
1.735     bisitz   6223:   background-color: #EEEEEE;
1.451     albertel 6224: }
                   6225: 
1.473     raeburn  6226: table.LC_createuser {
                   6227: }
                   6228: 
                   6229: table.LC_createuser tr.LC_section_row td {
1.701     harmsja  6230:   font-size: small;
1.473     raeburn  6231: }
                   6232: 
                   6233: table.LC_createuser tr.LC_info_row td  {
1.735     bisitz   6234:   background-color: #CCCCCC;
1.473     raeburn  6235:   font-weight: bold;
                   6236:   text-align: center;
                   6237: }
                   6238: 
1.349     albertel 6239: table.LC_calendar {
                   6240:   border: 1px solid #000000;
                   6241:   border-collapse: collapse;
1.917     raeburn  6242:   width: 98%;
1.349     albertel 6243: }
1.795     www      6244: 
1.349     albertel 6245: table.LC_calendar_pickdate {
                   6246:   font-size: xx-small;
                   6247: }
1.795     www      6248: 
1.349     albertel 6249: table.LC_calendar tr td {
                   6250:   border: 1px solid #000000;
                   6251:   vertical-align: top;
1.917     raeburn  6252:   width: 14%;
1.349     albertel 6253: }
1.795     www      6254: 
1.349     albertel 6255: table.LC_calendar tr td.LC_calendar_day_empty {
                   6256:   background-color: $data_table_dark;
                   6257: }
1.795     www      6258: 
1.779     bisitz   6259: table.LC_calendar tr td.LC_calendar_day_current {
                   6260:   background-color: $data_table_highlight;
1.777     tempelho 6261: }
1.795     www      6262: 
1.938     bisitz   6263: table.LC_data_table tr td.LC_mail_new {
1.349     albertel 6264:   background-color: $mail_new;
                   6265: }
1.795     www      6266: 
1.938     bisitz   6267: table.LC_data_table tr.LC_mail_new:hover {
1.349     albertel 6268:   background-color: $mail_new_hover;
                   6269: }
1.795     www      6270: 
1.938     bisitz   6271: table.LC_data_table tr td.LC_mail_read {
1.349     albertel 6272:   background-color: $mail_read;
                   6273: }
1.795     www      6274: 
1.938     bisitz   6275: /*
                   6276: table.LC_data_table tr.LC_mail_read:hover {
1.349     albertel 6277:   background-color: $mail_read_hover;
                   6278: }
1.938     bisitz   6279: */
1.795     www      6280: 
1.938     bisitz   6281: table.LC_data_table tr td.LC_mail_replied {
1.349     albertel 6282:   background-color: $mail_replied;
                   6283: }
1.795     www      6284: 
1.938     bisitz   6285: /*
                   6286: table.LC_data_table tr.LC_mail_replied:hover {
1.349     albertel 6287:   background-color: $mail_replied_hover;
                   6288: }
1.938     bisitz   6289: */
1.795     www      6290: 
1.938     bisitz   6291: table.LC_data_table tr td.LC_mail_other {
1.349     albertel 6292:   background-color: $mail_other;
                   6293: }
1.795     www      6294: 
1.938     bisitz   6295: /*
                   6296: table.LC_data_table tr.LC_mail_other:hover {
1.349     albertel 6297:   background-color: $mail_other_hover;
                   6298: }
1.938     bisitz   6299: */
1.494     raeburn  6300: 
1.777     tempelho 6301: table.LC_data_table tr > td.LC_browser_file,
                   6302: table.LC_data_table tr > td.LC_browser_file_published {
1.899     bisitz   6303:   background: #AAEE77;
1.389     albertel 6304: }
1.795     www      6305: 
1.777     tempelho 6306: table.LC_data_table tr > td.LC_browser_file_locked,
                   6307: table.LC_data_table tr > td.LC_browser_file_unpublished {
1.389     albertel 6308:   background: #FFAA99;
1.387     albertel 6309: }
1.795     www      6310: 
1.777     tempelho 6311: table.LC_data_table tr > td.LC_browser_file_obsolete {
1.899     bisitz   6312:   background: #888888;
1.779     bisitz   6313: }
1.795     www      6314: 
1.777     tempelho 6315: table.LC_data_table tr > td.LC_browser_file_modified,
1.779     bisitz   6316: table.LC_data_table tr > td.LC_browser_file_metamodified {
1.899     bisitz   6317:   background: #F8F866;
1.777     tempelho 6318: }
1.795     www      6319: 
1.696     bisitz   6320: table.LC_data_table tr.LC_browser_folder > td {
1.899     bisitz   6321:   background: #E0E8FF;
1.387     albertel 6322: }
1.696     bisitz   6323: 
1.707     bisitz   6324: table.LC_data_table tr > td.LC_roles_is {
1.911     bisitz   6325:   /* background: #77FF77; */
1.707     bisitz   6326: }
1.795     www      6327: 
1.707     bisitz   6328: table.LC_data_table tr > td.LC_roles_future {
1.939     bisitz   6329:   border-right: 8px solid #FFFF77;
1.707     bisitz   6330: }
1.795     www      6331: 
1.707     bisitz   6332: table.LC_data_table tr > td.LC_roles_will {
1.939     bisitz   6333:   border-right: 8px solid #FFAA77;
1.707     bisitz   6334: }
1.795     www      6335: 
1.707     bisitz   6336: table.LC_data_table tr > td.LC_roles_expired {
1.939     bisitz   6337:   border-right: 8px solid #FF7777;
1.707     bisitz   6338: }
1.795     www      6339: 
1.707     bisitz   6340: table.LC_data_table tr > td.LC_roles_will_not {
1.939     bisitz   6341:   border-right: 8px solid #AAFF77;
1.707     bisitz   6342: }
1.795     www      6343: 
1.707     bisitz   6344: table.LC_data_table tr > td.LC_roles_selected {
1.939     bisitz   6345:   border-right: 8px solid #11CC55;
1.707     bisitz   6346: }
                   6347: 
1.388     albertel 6348: span.LC_current_location {
1.701     harmsja  6349:   font-size:larger;
1.388     albertel 6350:   background: $pgbg;
                   6351: }
1.387     albertel 6352: 
1.1029    www      6353: span.LC_current_nav_location {
                   6354:   font-weight:bold;
                   6355:   background: $sidebg;
                   6356: }
                   6357: 
1.395     albertel 6358: span.LC_parm_menu_item {
                   6359:   font-size: larger;
                   6360: }
1.795     www      6361: 
1.395     albertel 6362: span.LC_parm_scope_all {
                   6363:   color: red;
                   6364: }
1.795     www      6365: 
1.395     albertel 6366: span.LC_parm_scope_folder {
                   6367:   color: green;
                   6368: }
1.795     www      6369: 
1.395     albertel 6370: span.LC_parm_scope_resource {
                   6371:   color: orange;
                   6372: }
1.795     www      6373: 
1.395     albertel 6374: span.LC_parm_part {
                   6375:   color: blue;
                   6376: }
1.795     www      6377: 
1.911     bisitz   6378: span.LC_parm_folder,
                   6379: span.LC_parm_symb {
1.395     albertel 6380:   font-size: x-small;
                   6381:   font-family: $mono;
                   6382:   color: #AAAAAA;
                   6383: }
                   6384: 
1.977     bisitz   6385: ul.LC_parm_parmlist li {
                   6386:   display: inline-block;
                   6387:   padding: 0.3em 0.8em;
                   6388:   vertical-align: top;
                   6389:   width: 150px;
                   6390:   border-top:1px solid $lg_border_color;
                   6391: }
                   6392: 
1.795     www      6393: td.LC_parm_overview_level_menu,
                   6394: td.LC_parm_overview_map_menu,
                   6395: td.LC_parm_overview_parm_selectors,
                   6396: td.LC_parm_overview_restrictions  {
1.396     albertel 6397:   border: 1px solid black;
                   6398:   border-collapse: collapse;
                   6399: }
1.795     www      6400: 
1.396     albertel 6401: table.LC_parm_overview_restrictions td {
                   6402:   border-width: 1px 4px 1px 4px;
                   6403:   border-style: solid;
                   6404:   border-color: $pgbg;
                   6405:   text-align: center;
                   6406: }
1.795     www      6407: 
1.396     albertel 6408: table.LC_parm_overview_restrictions th {
                   6409:   background: $tabbg;
                   6410:   border-width: 1px 4px 1px 4px;
                   6411:   border-style: solid;
                   6412:   border-color: $pgbg;
                   6413: }
1.795     www      6414: 
1.398     albertel 6415: table#LC_helpmenu {
1.803     bisitz   6416:   border: none;
1.398     albertel 6417:   height: 55px;
1.803     bisitz   6418:   border-spacing: 0;
1.398     albertel 6419: }
                   6420: 
                   6421: table#LC_helpmenu fieldset legend {
                   6422:   font-size: larger;
                   6423: }
1.795     www      6424: 
1.397     albertel 6425: table#LC_helpmenu_links {
                   6426:   width: 100%;
                   6427:   border: 1px solid black;
                   6428:   background: $pgbg;
1.803     bisitz   6429:   padding: 0;
1.397     albertel 6430:   border-spacing: 1px;
                   6431: }
1.795     www      6432: 
1.397     albertel 6433: table#LC_helpmenu_links tr td {
                   6434:   padding: 1px;
                   6435:   background: $tabbg;
1.399     albertel 6436:   text-align: center;
                   6437:   font-weight: bold;
1.397     albertel 6438: }
1.396     albertel 6439: 
1.795     www      6440: table#LC_helpmenu_links a:link,
                   6441: table#LC_helpmenu_links a:visited,
1.397     albertel 6442: table#LC_helpmenu_links a:active {
                   6443:   text-decoration: none;
                   6444:   color: $font;
                   6445: }
1.795     www      6446: 
1.397     albertel 6447: table#LC_helpmenu_links a:hover {
                   6448:   text-decoration: underline;
                   6449:   color: $vlink;
                   6450: }
1.396     albertel 6451: 
1.417     albertel 6452: .LC_chrt_popup_exists {
                   6453:   border: 1px solid #339933;
                   6454:   margin: -1px;
                   6455: }
1.795     www      6456: 
1.417     albertel 6457: .LC_chrt_popup_up {
                   6458:   border: 1px solid yellow;
                   6459:   margin: -1px;
                   6460: }
1.795     www      6461: 
1.417     albertel 6462: .LC_chrt_popup {
                   6463:   border: 1px solid #8888FF;
                   6464:   background: #CCCCFF;
                   6465: }
1.795     www      6466: 
1.421     albertel 6467: table.LC_pick_box {
                   6468:   border-collapse: separate;
                   6469:   background: white;
                   6470:   border: 1px solid black;
                   6471:   border-spacing: 1px;
                   6472: }
1.795     www      6473: 
1.421     albertel 6474: table.LC_pick_box td.LC_pick_box_title {
1.850     bisitz   6475:   background: $sidebg;
1.421     albertel 6476:   font-weight: bold;
1.900     bisitz   6477:   text-align: left;
1.740     bisitz   6478:   vertical-align: top;
1.421     albertel 6479:   width: 184px;
                   6480:   padding: 8px;
                   6481: }
1.795     www      6482: 
1.579     raeburn  6483: table.LC_pick_box td.LC_pick_box_value {
                   6484:   text-align: left;
                   6485:   padding: 8px;
                   6486: }
1.795     www      6487: 
1.579     raeburn  6488: table.LC_pick_box td.LC_pick_box_select {
                   6489:   text-align: left;
                   6490:   padding: 8px;
                   6491: }
1.795     www      6492: 
1.424     albertel 6493: table.LC_pick_box td.LC_pick_box_separator {
1.803     bisitz   6494:   padding: 0;
1.421     albertel 6495:   height: 1px;
                   6496:   background: black;
                   6497: }
1.795     www      6498: 
1.421     albertel 6499: table.LC_pick_box td.LC_pick_box_submit {
                   6500:   text-align: right;
                   6501: }
1.795     www      6502: 
1.579     raeburn  6503: table.LC_pick_box td.LC_evenrow_value {
                   6504:   text-align: left;
                   6505:   padding: 8px;
                   6506:   background-color: $data_table_light;
                   6507: }
1.795     www      6508: 
1.579     raeburn  6509: table.LC_pick_box td.LC_oddrow_value {
                   6510:   text-align: left;
                   6511:   padding: 8px;
                   6512:   background-color: $data_table_light;
                   6513: }
1.795     www      6514: 
1.579     raeburn  6515: span.LC_helpform_receipt_cat {
                   6516:   font-weight: bold;
                   6517: }
1.795     www      6518: 
1.424     albertel 6519: table.LC_group_priv_box {
                   6520:   background: white;
                   6521:   border: 1px solid black;
                   6522:   border-spacing: 1px;
                   6523: }
1.795     www      6524: 
1.424     albertel 6525: table.LC_group_priv_box td.LC_pick_box_title {
                   6526:   background: $tabbg;
                   6527:   font-weight: bold;
                   6528:   text-align: right;
                   6529:   width: 184px;
                   6530: }
1.795     www      6531: 
1.424     albertel 6532: table.LC_group_priv_box td.LC_groups_fixed {
                   6533:   background: $data_table_light;
                   6534:   text-align: center;
                   6535: }
1.795     www      6536: 
1.424     albertel 6537: table.LC_group_priv_box td.LC_groups_optional {
                   6538:   background: $data_table_dark;
                   6539:   text-align: center;
                   6540: }
1.795     www      6541: 
1.424     albertel 6542: table.LC_group_priv_box td.LC_groups_functionality {
                   6543:   background: $data_table_darker;
                   6544:   text-align: center;
                   6545:   font-weight: bold;
                   6546: }
1.795     www      6547: 
1.424     albertel 6548: table.LC_group_priv td {
                   6549:   text-align: left;
1.803     bisitz   6550:   padding: 0;
1.424     albertel 6551: }
                   6552: 
                   6553: .LC_navbuttons {
                   6554:   margin: 2ex 0ex 2ex 0ex;
                   6555: }
1.795     www      6556: 
1.423     albertel 6557: .LC_topic_bar {
                   6558:   font-weight: bold;
                   6559:   background: $tabbg;
1.918     wenzelju 6560:   margin: 1em 0em 1em 2em;
1.805     bisitz   6561:   padding: 3px;
1.918     wenzelju 6562:   font-size: 1.2em;
1.423     albertel 6563: }
1.795     www      6564: 
1.423     albertel 6565: .LC_topic_bar span {
1.918     wenzelju 6566:   left: 0.5em;
                   6567:   position: absolute;
1.423     albertel 6568:   vertical-align: middle;
1.918     wenzelju 6569:   font-size: 1.2em;
1.423     albertel 6570: }
1.795     www      6571: 
1.423     albertel 6572: table.LC_course_group_status {
                   6573:   margin: 20px;
                   6574: }
1.795     www      6575: 
1.423     albertel 6576: table.LC_status_selector td {
                   6577:   vertical-align: top;
                   6578:   text-align: center;
1.424     albertel 6579:   padding: 4px;
                   6580: }
1.795     www      6581: 
1.599     albertel 6582: div.LC_feedback_link {
1.616     albertel 6583:   clear: both;
1.829     kalberla 6584:   background: $sidebg;
1.779     bisitz   6585:   width: 100%;
1.829     kalberla 6586:   padding-bottom: 10px;
                   6587:   border: 1px $tabbg solid;
1.833     kalberla 6588:   height: 22px;
                   6589:   line-height: 22px;
                   6590:   padding-top: 5px;
                   6591: }
                   6592: 
                   6593: div.LC_feedback_link img {
                   6594:   height: 22px;
1.867     kalberla 6595:   vertical-align:middle;
1.829     kalberla 6596: }
                   6597: 
1.911     bisitz   6598: div.LC_feedback_link a {
1.829     kalberla 6599:   text-decoration: none;
1.489     raeburn  6600: }
1.795     www      6601: 
1.867     kalberla 6602: div.LC_comblock {
1.911     bisitz   6603:   display:inline;
1.867     kalberla 6604:   color:$font;
                   6605:   font-size:90%;
                   6606: }
                   6607: 
                   6608: div.LC_feedback_link div.LC_comblock {
                   6609:   padding-left:5px;
                   6610: }
                   6611: 
                   6612: div.LC_feedback_link div.LC_comblock a {
                   6613:   color:$font;
                   6614: }
                   6615: 
1.489     raeburn  6616: span.LC_feedback_link {
1.858     bisitz   6617:   /* background: $feedback_link_bg; */
1.599     albertel 6618:   font-size: larger;
                   6619: }
1.795     www      6620: 
1.599     albertel 6621: span.LC_message_link {
1.858     bisitz   6622:   /* background: $feedback_link_bg; */
1.599     albertel 6623:   font-size: larger;
                   6624:   position: absolute;
                   6625:   right: 1em;
1.489     raeburn  6626: }
1.421     albertel 6627: 
1.515     albertel 6628: table.LC_prior_tries {
1.524     albertel 6629:   border: 1px solid #000000;
                   6630:   border-collapse: separate;
                   6631:   border-spacing: 1px;
1.515     albertel 6632: }
1.523     albertel 6633: 
1.515     albertel 6634: table.LC_prior_tries td {
1.524     albertel 6635:   padding: 2px;
1.515     albertel 6636: }
1.523     albertel 6637: 
                   6638: .LC_answer_correct {
1.795     www      6639:   background: lightgreen;
                   6640:   color: darkgreen;
                   6641:   padding: 6px;
1.523     albertel 6642: }
1.795     www      6643: 
1.523     albertel 6644: .LC_answer_charged_try {
1.797     www      6645:   background: #FFAAAA;
1.795     www      6646:   color: darkred;
                   6647:   padding: 6px;
1.523     albertel 6648: }
1.795     www      6649: 
1.779     bisitz   6650: .LC_answer_not_charged_try,
1.523     albertel 6651: .LC_answer_no_grade,
                   6652: .LC_answer_late {
1.795     www      6653:   background: lightyellow;
1.523     albertel 6654:   color: black;
1.795     www      6655:   padding: 6px;
1.523     albertel 6656: }
1.795     www      6657: 
1.523     albertel 6658: .LC_answer_previous {
1.795     www      6659:   background: lightblue;
                   6660:   color: darkblue;
                   6661:   padding: 6px;
1.523     albertel 6662: }
1.795     www      6663: 
1.779     bisitz   6664: .LC_answer_no_message {
1.777     tempelho 6665:   background: #FFFFFF;
                   6666:   color: black;
1.795     www      6667:   padding: 6px;
1.779     bisitz   6668: }
1.795     www      6669: 
1.779     bisitz   6670: .LC_answer_unknown {
                   6671:   background: orange;
                   6672:   color: black;
1.795     www      6673:   padding: 6px;
1.777     tempelho 6674: }
1.795     www      6675: 
1.529     albertel 6676: span.LC_prior_numerical,
                   6677: span.LC_prior_string,
                   6678: span.LC_prior_custom,
                   6679: span.LC_prior_reaction,
                   6680: span.LC_prior_math {
1.925     bisitz   6681:   font-family: $mono;
1.523     albertel 6682:   white-space: pre;
                   6683: }
                   6684: 
1.525     albertel 6685: span.LC_prior_string {
1.925     bisitz   6686:   font-family: $mono;
1.525     albertel 6687:   white-space: pre;
                   6688: }
                   6689: 
1.523     albertel 6690: table.LC_prior_option {
                   6691:   width: 100%;
                   6692:   border-collapse: collapse;
                   6693: }
1.795     www      6694: 
1.911     bisitz   6695: table.LC_prior_rank,
1.795     www      6696: table.LC_prior_match {
1.528     albertel 6697:   border-collapse: collapse;
                   6698: }
1.795     www      6699: 
1.528     albertel 6700: table.LC_prior_option tr td,
                   6701: table.LC_prior_rank tr td,
                   6702: table.LC_prior_match tr td {
1.524     albertel 6703:   border: 1px solid #000000;
1.515     albertel 6704: }
                   6705: 
1.855     bisitz   6706: .LC_nobreak {
1.544     albertel 6707:   white-space: nowrap;
1.519     raeburn  6708: }
                   6709: 
1.576     raeburn  6710: span.LC_cusr_emph {
                   6711:   font-style: italic;
                   6712: }
                   6713: 
1.633     raeburn  6714: span.LC_cusr_subheading {
                   6715:   font-weight: normal;
                   6716:   font-size: 85%;
                   6717: }
                   6718: 
1.861     bisitz   6719: div.LC_docs_entry_move {
1.859     bisitz   6720:   border: 1px solid #BBBBBB;
1.545     albertel 6721:   background: #DDDDDD;
1.861     bisitz   6722:   width: 22px;
1.859     bisitz   6723:   padding: 1px;
                   6724:   margin: 0;
1.545     albertel 6725: }
                   6726: 
1.861     bisitz   6727: table.LC_data_table tr > td.LC_docs_entry_commands,
                   6728: table.LC_data_table tr > td.LC_docs_entry_parameter {
1.545     albertel 6729:   font-size: x-small;
                   6730: }
1.795     www      6731: 
1.861     bisitz   6732: .LC_docs_entry_parameter {
                   6733:   white-space: nowrap;
                   6734: }
                   6735: 
1.544     albertel 6736: .LC_docs_copy {
1.545     albertel 6737:   color: #000099;
1.544     albertel 6738: }
1.795     www      6739: 
1.544     albertel 6740: .LC_docs_cut {
1.545     albertel 6741:   color: #550044;
1.544     albertel 6742: }
1.795     www      6743: 
1.544     albertel 6744: .LC_docs_rename {
1.545     albertel 6745:   color: #009900;
1.544     albertel 6746: }
1.795     www      6747: 
1.544     albertel 6748: .LC_docs_remove {
1.545     albertel 6749:   color: #990000;
                   6750: }
                   6751: 
1.547     albertel 6752: .LC_docs_reinit_warn,
                   6753: .LC_docs_ext_edit {
                   6754:   font-size: x-small;
                   6755: }
                   6756: 
1.545     albertel 6757: table.LC_docs_adddocs td,
                   6758: table.LC_docs_adddocs th {
                   6759:   border: 1px solid #BBBBBB;
                   6760:   padding: 4px;
                   6761:   background: #DDDDDD;
1.543     albertel 6762: }
                   6763: 
1.584     albertel 6764: table.LC_sty_begin {
                   6765:   background: #BBFFBB;
                   6766: }
1.795     www      6767: 
1.584     albertel 6768: table.LC_sty_end {
                   6769:   background: #FFBBBB;
                   6770: }
                   6771: 
1.589     raeburn  6772: table.LC_double_column {
1.803     bisitz   6773:   border-width: 0;
1.589     raeburn  6774:   border-collapse: collapse;
                   6775:   width: 100%;
                   6776:   padding: 2px;
                   6777: }
                   6778: 
                   6779: table.LC_double_column tr td.LC_left_col {
1.590     raeburn  6780:   top: 2px;
1.589     raeburn  6781:   left: 2px;
                   6782:   width: 47%;
                   6783:   vertical-align: top;
                   6784: }
                   6785: 
                   6786: table.LC_double_column tr td.LC_right_col {
                   6787:   top: 2px;
1.779     bisitz   6788:   right: 2px;
1.589     raeburn  6789:   width: 47%;
                   6790:   vertical-align: top;
                   6791: }
                   6792: 
1.591     raeburn  6793: div.LC_left_float {
                   6794:   float: left;
                   6795:   padding-right: 5%;
1.597     albertel 6796:   padding-bottom: 4px;
1.591     raeburn  6797: }
                   6798: 
                   6799: div.LC_clear_float_header {
1.597     albertel 6800:   padding-bottom: 2px;
1.591     raeburn  6801: }
                   6802: 
                   6803: div.LC_clear_float_footer {
1.597     albertel 6804:   padding-top: 10px;
1.591     raeburn  6805:   clear: both;
                   6806: }
                   6807: 
1.597     albertel 6808: div.LC_grade_show_user {
1.941     bisitz   6809: /*  border-left: 5px solid $sidebg; */
                   6810:   border-top: 5px solid #000000;
                   6811:   margin: 50px 0 0 0;
1.936     bisitz   6812:   padding: 15px 0 5px 10px;
1.597     albertel 6813: }
1.795     www      6814: 
1.936     bisitz   6815: div.LC_grade_show_user_odd_row {
1.941     bisitz   6816: /*  border-left: 5px solid #000000; */
                   6817: }
                   6818: 
                   6819: div.LC_grade_show_user div.LC_Box {
                   6820:   margin-right: 50px;
1.597     albertel 6821: }
                   6822: 
                   6823: div.LC_grade_submissions,
                   6824: div.LC_grade_message_center,
1.936     bisitz   6825: div.LC_grade_info_links {
1.597     albertel 6826:   margin: 5px;
                   6827:   width: 99%;
                   6828:   background: #FFFFFF;
                   6829: }
1.795     www      6830: 
1.597     albertel 6831: div.LC_grade_submissions_header,
1.936     bisitz   6832: div.LC_grade_message_center_header {
1.705     tempelho 6833:   font-weight: bold;
                   6834:   font-size: large;
1.597     albertel 6835: }
1.795     www      6836: 
1.597     albertel 6837: div.LC_grade_submissions_body,
1.936     bisitz   6838: div.LC_grade_message_center_body {
1.597     albertel 6839:   border: 1px solid black;
                   6840:   width: 99%;
                   6841:   background: #FFFFFF;
                   6842: }
1.795     www      6843: 
1.613     albertel 6844: table.LC_scantron_action {
                   6845:   width: 100%;
                   6846: }
1.795     www      6847: 
1.613     albertel 6848: table.LC_scantron_action tr th {
1.698     harmsja  6849:   font-weight:bold;
                   6850:   font-style:normal;
1.613     albertel 6851: }
1.795     www      6852: 
1.779     bisitz   6853: .LC_edit_problem_header,
1.614     albertel 6854: div.LC_edit_problem_footer {
1.705     tempelho 6855:   font-weight: normal;
                   6856:   font-size:  medium;
1.602     albertel 6857:   margin: 2px;
1.1060    bisitz   6858:   background-color: $sidebg;
1.600     albertel 6859: }
1.795     www      6860: 
1.600     albertel 6861: div.LC_edit_problem_header,
1.602     albertel 6862: div.LC_edit_problem_header div,
1.614     albertel 6863: div.LC_edit_problem_footer,
                   6864: div.LC_edit_problem_footer div,
1.602     albertel 6865: div.LC_edit_problem_editxml_header,
                   6866: div.LC_edit_problem_editxml_header div {
1.1205    golterma 6867:   z-index: 100;
1.600     albertel 6868: }
1.795     www      6869: 
1.600     albertel 6870: div.LC_edit_problem_header_title {
1.705     tempelho 6871:   font-weight: bold;
                   6872:   font-size: larger;
1.602     albertel 6873:   background: $tabbg;
                   6874:   padding: 3px;
1.1060    bisitz   6875:   margin: 0 0 5px 0;
1.602     albertel 6876: }
1.795     www      6877: 
1.602     albertel 6878: table.LC_edit_problem_header_title {
                   6879:   width: 100%;
1.600     albertel 6880:   background: $tabbg;
1.602     albertel 6881: }
                   6882: 
1.1205    golterma 6883: div.LC_edit_actionbar {
                   6884:     background-color: $sidebg;
1.1218    droeschl 6885:     margin: 0;
                   6886:     padding: 0;
                   6887:     line-height: 200%;
1.602     albertel 6888: }
1.795     www      6889: 
1.1218    droeschl 6890: div.LC_edit_actionbar div{
                   6891:     padding: 0;
                   6892:     margin: 0;
                   6893:     display: inline-block;
1.600     albertel 6894: }
1.795     www      6895: 
1.1124    bisitz   6896: .LC_edit_opt {
                   6897:   padding-left: 1em;
                   6898:   white-space: nowrap;
                   6899: }
                   6900: 
1.1152    golterma 6901: .LC_edit_problem_latexhelper{
                   6902:     text-align: right;
                   6903: }
                   6904: 
                   6905: #LC_edit_problem_colorful div{
                   6906:     margin-left: 40px;
                   6907: }
                   6908: 
1.1205    golterma 6909: #LC_edit_problem_codemirror div{
                   6910:     margin-left: 0px;
                   6911: }
                   6912: 
1.911     bisitz   6913: img.stift {
1.803     bisitz   6914:   border-width: 0;
                   6915:   vertical-align: middle;
1.677     riegler  6916: }
1.680     riegler  6917: 
1.923     bisitz   6918: table td.LC_mainmenu_col_fieldset {
1.680     riegler  6919:   vertical-align: top;
1.777     tempelho 6920: }
1.795     www      6921: 
1.716     raeburn  6922: div.LC_createcourse {
1.911     bisitz   6923:   margin: 10px 10px 10px 10px;
1.716     raeburn  6924: }
                   6925: 
1.917     raeburn  6926: .LC_dccid {
1.1130    raeburn  6927:   float: right;
1.917     raeburn  6928:   margin: 0.2em 0 0 0;
                   6929:   padding: 0;
                   6930:   font-size: 90%;
                   6931:   display:none;
                   6932: }
                   6933: 
1.897     wenzelju 6934: ol.LC_primary_menu a:hover,
1.721     harmsja  6935: ol#LC_MenuBreadcrumbs a:hover,
                   6936: ol#LC_PathBreadcrumbs a:hover,
1.897     wenzelju 6937: ul#LC_secondary_menu a:hover,
1.721     harmsja  6938: .LC_FormSectionClearButton input:hover
1.795     www      6939: ul.LC_TabContent   li:hover a {
1.952     onken    6940:   color:$button_hover;
1.911     bisitz   6941:   text-decoration:none;
1.693     droeschl 6942: }
                   6943: 
1.779     bisitz   6944: h1 {
1.911     bisitz   6945:   padding: 0;
                   6946:   line-height:130%;
1.693     droeschl 6947: }
1.698     harmsja  6948: 
1.911     bisitz   6949: h2,
                   6950: h3,
                   6951: h4,
                   6952: h5,
                   6953: h6 {
                   6954:   margin: 5px 0 5px 0;
                   6955:   padding: 0;
                   6956:   line-height:130%;
1.693     droeschl 6957: }
1.795     www      6958: 
                   6959: .LC_hcell {
1.911     bisitz   6960:   padding:3px 15px 3px 15px;
                   6961:   margin: 0;
                   6962:   background-color:$tabbg;
                   6963:   color:$fontmenu;
                   6964:   border-bottom:solid 1px $lg_border_color;
1.693     droeschl 6965: }
1.795     www      6966: 
1.840     bisitz   6967: .LC_Box > .LC_hcell {
1.911     bisitz   6968:   margin: 0 -10px 10px -10px;
1.835     bisitz   6969: }
                   6970: 
1.721     harmsja  6971: .LC_noBorder {
1.911     bisitz   6972:   border: 0;
1.698     harmsja  6973: }
1.693     droeschl 6974: 
1.721     harmsja  6975: .LC_FormSectionClearButton input {
1.911     bisitz   6976:   background-color:transparent;
                   6977:   border: none;
                   6978:   cursor:pointer;
                   6979:   text-decoration:underline;
1.693     droeschl 6980: }
1.763     bisitz   6981: 
                   6982: .LC_help_open_topic {
1.911     bisitz   6983:   color: #FFFFFF;
                   6984:   background-color: #EEEEFF;
                   6985:   margin: 1px;
                   6986:   padding: 4px;
                   6987:   border: 1px solid #000033;
                   6988:   white-space: nowrap;
                   6989:   /* vertical-align: middle; */
1.759     neumanie 6990: }
1.693     droeschl 6991: 
1.911     bisitz   6992: dl,
                   6993: ul,
                   6994: div,
                   6995: fieldset {
                   6996:   margin: 10px 10px 10px 0;
                   6997:   /* overflow: hidden; */
1.693     droeschl 6998: }
1.795     www      6999: 
1.1211    raeburn  7000: article.geogebraweb div {
                   7001:     margin: 0;
                   7002: }
                   7003: 
1.838     bisitz   7004: fieldset > legend {
1.911     bisitz   7005:   font-weight: bold;
                   7006:   padding: 0 5px 0 5px;
1.838     bisitz   7007: }
                   7008: 
1.813     bisitz   7009: #LC_nav_bar {
1.911     bisitz   7010:   float: left;
1.995     raeburn  7011:   background-color: $pgbg_or_bgcolor;
1.966     bisitz   7012:   margin: 0 0 2px 0;
1.807     droeschl 7013: }
                   7014: 
1.916     droeschl 7015: #LC_realm {
                   7016:   margin: 0.2em 0 0 0;
                   7017:   padding: 0;
                   7018:   font-weight: bold;
                   7019:   text-align: center;
1.995     raeburn  7020:   background-color: $pgbg_or_bgcolor;
1.916     droeschl 7021: }
                   7022: 
1.911     bisitz   7023: #LC_nav_bar em {
                   7024:   font-weight: bold;
                   7025:   font-style: normal;
1.807     droeschl 7026: }
                   7027: 
1.897     wenzelju 7028: ol.LC_primary_menu {
1.934     droeschl 7029:   margin: 0;
1.1076    raeburn  7030:   padding: 0;
1.807     droeschl 7031: }
                   7032: 
1.852     droeschl 7033: ol#LC_PathBreadcrumbs {
1.911     bisitz   7034:   margin: 0;
1.693     droeschl 7035: }
                   7036: 
1.897     wenzelju 7037: ol.LC_primary_menu li {
1.1076    raeburn  7038:   color: RGB(80, 80, 80);
                   7039:   vertical-align: middle;
                   7040:   text-align: left;
                   7041:   list-style: none;
1.1205    golterma 7042:   position: relative;
1.1076    raeburn  7043:   float: left;
1.1205    golterma 7044:   z-index: 100; /* will be displayed above codemirror and underneath the help-layer */
                   7045:   line-height: 1.5em;
1.1076    raeburn  7046: }
                   7047: 
1.1205    golterma 7048: ol.LC_primary_menu li a,
                   7049: ol.LC_primary_menu li p {
1.1076    raeburn  7050:   display: block;
                   7051:   margin: 0;
                   7052:   padding: 0 5px 0 10px;
                   7053:   text-decoration: none;
                   7054: }
                   7055: 
1.1205    golterma 7056: ol.LC_primary_menu li p span.LC_primary_menu_innertitle {
                   7057:   display: inline-block;
                   7058:   width: 95%;
                   7059:   text-align: left;
                   7060: }
                   7061: 
                   7062: ol.LC_primary_menu li p span.LC_primary_menu_innerarrow {
                   7063:   display: inline-block;	
                   7064:   width: 5%;
                   7065:   float: right;
                   7066:   text-align: right;
                   7067:   font-size: 70%;
                   7068: }
                   7069: 
                   7070: ol.LC_primary_menu ul {
1.1076    raeburn  7071:   display: none;
1.1205    golterma 7072:   width: 15em;
1.1076    raeburn  7073:   background-color: $data_table_light;
1.1205    golterma 7074:   position: absolute;
                   7075:   top: 100%;
1.1076    raeburn  7076: }
                   7077: 
1.1205    golterma 7078: ol.LC_primary_menu ul ul {
                   7079:   left: 100%;
                   7080:   top: 0;
                   7081: }
                   7082: 
                   7083: ol.LC_primary_menu li:hover > ul, ol.LC_primary_menu li.hover > ul {
1.1076    raeburn  7084:   display: block;
                   7085:   position: absolute;
                   7086:   margin: 0;
                   7087:   padding: 0;
1.1078    raeburn  7088:   z-index: 2;
1.1076    raeburn  7089: }
                   7090: 
                   7091: ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
1.1205    golterma 7092: /* First Submenu -> size should be smaller than the menu title of the whole menu */
1.1076    raeburn  7093:   font-size: 90%;
1.911     bisitz   7094:   vertical-align: top;
1.1076    raeburn  7095:   float: none;
1.1079    raeburn  7096:   border-left: 1px solid black;
                   7097:   border-right: 1px solid black;
1.1205    golterma 7098: /* A dark bottom border to visualize different menu options; 
                   7099: overwritten in the create_submenu routine for the last border-bottom of the menu */
                   7100:   border-bottom: 1px solid $data_table_dark; 
1.1076    raeburn  7101: }
                   7102: 
1.1205    golterma 7103: ol.LC_primary_menu li li p:hover {
                   7104:   color:$button_hover;
                   7105:   text-decoration:none;
                   7106:   background-color:$data_table_dark;
1.1076    raeburn  7107: }
                   7108: 
                   7109: ol.LC_primary_menu li li a:hover {
                   7110:    color:$button_hover;
                   7111:    background-color:$data_table_dark;
1.693     droeschl 7112: }
                   7113: 
1.1205    golterma 7114: /* Font-size equal to the size of the predecessors*/
                   7115: ol.LC_primary_menu li:hover li li {
                   7116:   font-size: 100%;
                   7117: }
                   7118: 
1.897     wenzelju 7119: ol.LC_primary_menu li img {
1.911     bisitz   7120:   vertical-align: bottom;
1.934     droeschl 7121:   height: 1.1em;
1.1077    raeburn  7122:   margin: 0.2em 0 0 0;
1.693     droeschl 7123: }
                   7124: 
1.897     wenzelju 7125: ol.LC_primary_menu a {
1.911     bisitz   7126:   color: RGB(80, 80, 80);
                   7127:   text-decoration: none;
1.693     droeschl 7128: }
1.795     www      7129: 
1.949     droeschl 7130: ol.LC_primary_menu a.LC_new_message {
                   7131:   font-weight:bold;
                   7132:   color: darkred;
                   7133: }
                   7134: 
1.975     raeburn  7135: ol.LC_docs_parameters {
                   7136:   margin-left: 0;
                   7137:   padding: 0;
                   7138:   list-style: none;
                   7139: }
                   7140: 
                   7141: ol.LC_docs_parameters li {
                   7142:   margin: 0;
                   7143:   padding-right: 20px;
                   7144:   display: inline;
                   7145: }
                   7146: 
1.976     raeburn  7147: ol.LC_docs_parameters li:before {
                   7148:   content: "\\002022 \\0020";
                   7149: }
                   7150: 
                   7151: li.LC_docs_parameters_title {
                   7152:   font-weight: bold;
                   7153: }
                   7154: 
                   7155: ol.LC_docs_parameters li.LC_docs_parameters_title:before {
                   7156:   content: "";
                   7157: }
                   7158: 
1.897     wenzelju 7159: ul#LC_secondary_menu {
1.1107    raeburn  7160:   clear: right;
1.911     bisitz   7161:   color: $fontmenu;
                   7162:   background: $tabbg;
                   7163:   list-style: none;
                   7164:   padding: 0;
                   7165:   margin: 0;
                   7166:   width: 100%;
1.995     raeburn  7167:   text-align: left;
1.1107    raeburn  7168:   float: left;
1.808     droeschl 7169: }
                   7170: 
1.897     wenzelju 7171: ul#LC_secondary_menu li {
1.911     bisitz   7172:   font-weight: bold;
                   7173:   line-height: 1.8em;
1.1107    raeburn  7174:   border-right: 1px solid black;
                   7175:   float: left;
                   7176: }
                   7177: 
                   7178: ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
                   7179:   background-color: $data_table_light;
                   7180: }
                   7181: 
                   7182: ul#LC_secondary_menu li a {
1.911     bisitz   7183:   padding: 0 0.8em;
1.1107    raeburn  7184: }
                   7185: 
                   7186: ul#LC_secondary_menu li ul {
                   7187:   display: none;
                   7188: }
                   7189: 
                   7190: ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
                   7191:   display: block;
                   7192:   position: absolute;
                   7193:   margin: 0;
                   7194:   padding: 0;
                   7195:   list-style:none;
                   7196:   float: none;
                   7197:   background-color: $data_table_light;
                   7198:   z-index: 2;
                   7199:   margin-left: -1px;
                   7200: }
                   7201: 
                   7202: ul#LC_secondary_menu li ul li {
                   7203:   font-size: 90%;
                   7204:   vertical-align: top;
                   7205:   border-left: 1px solid black;
1.911     bisitz   7206:   border-right: 1px solid black;
1.1119    raeburn  7207:   background-color: $data_table_light;
1.1107    raeburn  7208:   list-style:none;
                   7209:   float: none;
                   7210: }
                   7211: 
                   7212: ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
                   7213:   background-color: $data_table_dark;
1.807     droeschl 7214: }
                   7215: 
1.847     tempelho 7216: ul.LC_TabContent {
1.911     bisitz   7217:   display:block;
                   7218:   background: $sidebg;
                   7219:   border-bottom: solid 1px $lg_border_color;
                   7220:   list-style:none;
1.1020    raeburn  7221:   margin: -1px -10px 0 -10px;
1.911     bisitz   7222:   padding: 0;
1.693     droeschl 7223: }
                   7224: 
1.795     www      7225: ul.LC_TabContent li,
                   7226: ul.LC_TabContentBigger li {
1.911     bisitz   7227:   float:left;
1.741     harmsja  7228: }
1.795     www      7229: 
1.897     wenzelju 7230: ul#LC_secondary_menu li a {
1.911     bisitz   7231:   color: $fontmenu;
                   7232:   text-decoration: none;
1.693     droeschl 7233: }
1.795     www      7234: 
1.721     harmsja  7235: ul.LC_TabContent {
1.952     onken    7236:   min-height:20px;
1.721     harmsja  7237: }
1.795     www      7238: 
                   7239: ul.LC_TabContent li {
1.911     bisitz   7240:   vertical-align:middle;
1.959     onken    7241:   padding: 0 16px 0 10px;
1.911     bisitz   7242:   background-color:$tabbg;
                   7243:   border-bottom:solid 1px $lg_border_color;
1.1020    raeburn  7244:   border-left: solid 1px $font;
1.721     harmsja  7245: }
1.795     www      7246: 
1.847     tempelho 7247: ul.LC_TabContent .right {
1.911     bisitz   7248:   float:right;
1.847     tempelho 7249: }
                   7250: 
1.911     bisitz   7251: ul.LC_TabContent li a,
                   7252: ul.LC_TabContent li {
                   7253:   color:rgb(47,47,47);
                   7254:   text-decoration:none;
                   7255:   font-size:95%;
                   7256:   font-weight:bold;
1.952     onken    7257:   min-height:20px;
                   7258: }
                   7259: 
1.959     onken    7260: ul.LC_TabContent li a:hover,
                   7261: ul.LC_TabContent li a:focus {
1.952     onken    7262:   color: $button_hover;
1.959     onken    7263:   background:none;
                   7264:   outline:none;
1.952     onken    7265: }
                   7266: 
                   7267: ul.LC_TabContent li:hover {
                   7268:   color: $button_hover;
                   7269:   cursor:pointer;
1.721     harmsja  7270: }
1.795     www      7271: 
1.911     bisitz   7272: ul.LC_TabContent li.active {
1.952     onken    7273:   color: $font;
1.911     bisitz   7274:   background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
1.952     onken    7275:   border-bottom:solid 1px #FFFFFF;
                   7276:   cursor: default;
1.744     ehlerst  7277: }
1.795     www      7278: 
1.959     onken    7279: ul.LC_TabContent li.active a {
                   7280:   color:$font;
                   7281:   background:#FFFFFF;
                   7282:   outline: none;
                   7283: }
1.1047    raeburn  7284: 
                   7285: ul.LC_TabContent li.goback {
                   7286:   float: left;
                   7287:   border-left: none;
                   7288: }
                   7289: 
1.870     tempelho 7290: #maincoursedoc {
1.911     bisitz   7291:   clear:both;
1.870     tempelho 7292: }
                   7293: 
                   7294: ul.LC_TabContentBigger {
1.911     bisitz   7295:   display:block;
                   7296:   list-style:none;
                   7297:   padding: 0;
1.870     tempelho 7298: }
                   7299: 
1.795     www      7300: ul.LC_TabContentBigger li {
1.911     bisitz   7301:   vertical-align:bottom;
                   7302:   height: 30px;
                   7303:   font-size:110%;
                   7304:   font-weight:bold;
                   7305:   color: #737373;
1.841     tempelho 7306: }
                   7307: 
1.957     onken    7308: ul.LC_TabContentBigger li.active {
                   7309:   position: relative;
                   7310:   top: 1px;
                   7311: }
                   7312: 
1.870     tempelho 7313: ul.LC_TabContentBigger li a {
1.911     bisitz   7314:   background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
                   7315:   height: 30px;
                   7316:   line-height: 30px;
                   7317:   text-align: center;
                   7318:   display: block;
                   7319:   text-decoration: none;
1.958     onken    7320:   outline: none;  
1.741     harmsja  7321: }
1.795     www      7322: 
1.870     tempelho 7323: ul.LC_TabContentBigger li.active a {
1.911     bisitz   7324:   background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
                   7325:   color:$font;
1.744     ehlerst  7326: }
1.795     www      7327: 
1.870     tempelho 7328: ul.LC_TabContentBigger li b {
1.911     bisitz   7329:   background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
                   7330:   display: block;
                   7331:   float: left;
                   7332:   padding: 0 30px;
1.957     onken    7333:   border-bottom: 1px solid $lg_border_color;
1.870     tempelho 7334: }
                   7335: 
1.956     onken    7336: ul.LC_TabContentBigger li:hover b {
                   7337:   color:$button_hover;
                   7338: }
                   7339: 
1.870     tempelho 7340: ul.LC_TabContentBigger li.active b {
1.911     bisitz   7341:   background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
                   7342:   color:$font;
1.957     onken    7343:   border: 0;
1.741     harmsja  7344: }
1.693     droeschl 7345: 
1.870     tempelho 7346: 
1.862     bisitz   7347: ul.LC_CourseBreadcrumbs {
                   7348:   background: $sidebg;
1.1020    raeburn  7349:   height: 2em;
1.862     bisitz   7350:   padding-left: 10px;
1.1020    raeburn  7351:   margin: 0;
1.862     bisitz   7352:   list-style-position: inside;
                   7353: }
                   7354: 
1.911     bisitz   7355: ol#LC_MenuBreadcrumbs,
1.862     bisitz   7356: ol#LC_PathBreadcrumbs {
1.911     bisitz   7357:   padding-left: 10px;
                   7358:   margin: 0;
1.933     droeschl 7359:   height: 2.5em;  /* equal to #LC_breadcrumbs line-height */
1.693     droeschl 7360: }
                   7361: 
1.911     bisitz   7362: ol#LC_MenuBreadcrumbs li,
                   7363: ol#LC_PathBreadcrumbs li,
1.862     bisitz   7364: ul.LC_CourseBreadcrumbs li {
1.911     bisitz   7365:   display: inline;
1.933     droeschl 7366:   white-space: normal;  
1.693     droeschl 7367: }
                   7368: 
1.823     bisitz   7369: ol#LC_MenuBreadcrumbs li a,
1.862     bisitz   7370: ul.LC_CourseBreadcrumbs li a {
1.911     bisitz   7371:   text-decoration: none;
                   7372:   font-size:90%;
1.693     droeschl 7373: }
1.795     www      7374: 
1.969     droeschl 7375: ol#LC_MenuBreadcrumbs h1 {
                   7376:   display: inline;
                   7377:   font-size: 90%;
                   7378:   line-height: 2.5em;
                   7379:   margin: 0;
                   7380:   padding: 0;
                   7381: }
                   7382: 
1.795     www      7383: ol#LC_PathBreadcrumbs li a {
1.911     bisitz   7384:   text-decoration:none;
                   7385:   font-size:100%;
                   7386:   font-weight:bold;
1.693     droeschl 7387: }
1.795     www      7388: 
1.840     bisitz   7389: .LC_Box {
1.911     bisitz   7390:   border: solid 1px $lg_border_color;
                   7391:   padding: 0 10px 10px 10px;
1.746     neumanie 7392: }
1.795     www      7393: 
1.1020    raeburn  7394: .LC_DocsBox {
                   7395:   border: solid 1px $lg_border_color;
                   7396:   padding: 0 0 10px 10px;
                   7397: }
                   7398: 
1.795     www      7399: .LC_AboutMe_Image {
1.911     bisitz   7400:   float:left;
                   7401:   margin-right:10px;
1.747     neumanie 7402: }
1.795     www      7403: 
                   7404: .LC_Clear_AboutMe_Image {
1.911     bisitz   7405:   clear:left;
1.747     neumanie 7406: }
1.795     www      7407: 
1.721     harmsja  7408: dl.LC_ListStyleClean dt {
1.911     bisitz   7409:   padding-right: 5px;
                   7410:   display: table-header-group;
1.693     droeschl 7411: }
                   7412: 
1.721     harmsja  7413: dl.LC_ListStyleClean dd {
1.911     bisitz   7414:   display: table-row;
1.693     droeschl 7415: }
                   7416: 
1.721     harmsja  7417: .LC_ListStyleClean,
                   7418: .LC_ListStyleSimple,
                   7419: .LC_ListStyleNormal,
1.795     www      7420: .LC_ListStyleSpecial {
1.911     bisitz   7421:   /* display:block; */
                   7422:   list-style-position: inside;
                   7423:   list-style-type: none;
                   7424:   overflow: hidden;
                   7425:   padding: 0;
1.693     droeschl 7426: }
                   7427: 
1.721     harmsja  7428: .LC_ListStyleSimple li,
                   7429: .LC_ListStyleSimple dd,
                   7430: .LC_ListStyleNormal li,
                   7431: .LC_ListStyleNormal dd,
                   7432: .LC_ListStyleSpecial li,
1.795     www      7433: .LC_ListStyleSpecial dd {
1.911     bisitz   7434:   margin: 0;
                   7435:   padding: 5px 5px 5px 10px;
                   7436:   clear: both;
1.693     droeschl 7437: }
                   7438: 
1.721     harmsja  7439: .LC_ListStyleClean li,
                   7440: .LC_ListStyleClean dd {
1.911     bisitz   7441:   padding-top: 0;
                   7442:   padding-bottom: 0;
1.693     droeschl 7443: }
                   7444: 
1.721     harmsja  7445: .LC_ListStyleSimple dd,
1.795     www      7446: .LC_ListStyleSimple li {
1.911     bisitz   7447:   border-bottom: solid 1px $lg_border_color;
1.693     droeschl 7448: }
                   7449: 
1.721     harmsja  7450: .LC_ListStyleSpecial li,
                   7451: .LC_ListStyleSpecial dd {
1.911     bisitz   7452:   list-style-type: none;
                   7453:   background-color: RGB(220, 220, 220);
                   7454:   margin-bottom: 4px;
1.693     droeschl 7455: }
                   7456: 
1.721     harmsja  7457: table.LC_SimpleTable {
1.911     bisitz   7458:   margin:5px;
                   7459:   border:solid 1px $lg_border_color;
1.795     www      7460: }
1.693     droeschl 7461: 
1.721     harmsja  7462: table.LC_SimpleTable tr {
1.911     bisitz   7463:   padding: 0;
                   7464:   border:solid 1px $lg_border_color;
1.693     droeschl 7465: }
1.795     www      7466: 
                   7467: table.LC_SimpleTable thead {
1.911     bisitz   7468:   background:rgb(220,220,220);
1.693     droeschl 7469: }
                   7470: 
1.721     harmsja  7471: div.LC_columnSection {
1.911     bisitz   7472:   display: block;
                   7473:   clear: both;
                   7474:   overflow: hidden;
                   7475:   margin: 0;
1.693     droeschl 7476: }
                   7477: 
1.721     harmsja  7478: div.LC_columnSection>* {
1.911     bisitz   7479:   float: left;
                   7480:   margin: 10px 20px 10px 0;
                   7481:   overflow:hidden;
1.693     droeschl 7482: }
1.721     harmsja  7483: 
1.795     www      7484: table em {
1.911     bisitz   7485:   font-weight: bold;
                   7486:   font-style: normal;
1.748     schulted 7487: }
1.795     www      7488: 
1.779     bisitz   7489: table.LC_tableBrowseRes,
1.795     www      7490: table.LC_tableOfContent {
1.911     bisitz   7491:   border:none;
                   7492:   border-spacing: 1px;
                   7493:   padding: 3px;
                   7494:   background-color: #FFFFFF;
                   7495:   font-size: 90%;
1.753     droeschl 7496: }
1.789     droeschl 7497: 
1.911     bisitz   7498: table.LC_tableOfContent {
                   7499:   border-collapse: collapse;
1.789     droeschl 7500: }
                   7501: 
1.771     droeschl 7502: table.LC_tableBrowseRes a,
1.768     schulted 7503: table.LC_tableOfContent a {
1.911     bisitz   7504:   background-color: transparent;
                   7505:   text-decoration: none;
1.753     droeschl 7506: }
                   7507: 
1.795     www      7508: table.LC_tableOfContent img {
1.911     bisitz   7509:   border: none;
                   7510:   height: 1.3em;
                   7511:   vertical-align: text-bottom;
                   7512:   margin-right: 0.3em;
1.753     droeschl 7513: }
1.757     schulted 7514: 
1.795     www      7515: a#LC_content_toolbar_firsthomework {
1.911     bisitz   7516:   background-image:url(/res/adm/pages/open-first-problem.gif);
1.774     ehlerst  7517: }
                   7518: 
1.795     www      7519: a#LC_content_toolbar_everything {
1.911     bisitz   7520:   background-image:url(/res/adm/pages/show-all.gif);
1.774     ehlerst  7521: }
                   7522: 
1.795     www      7523: a#LC_content_toolbar_uncompleted {
1.911     bisitz   7524:   background-image:url(/res/adm/pages/show-incomplete-problems.gif);
1.774     ehlerst  7525: }
                   7526: 
1.795     www      7527: #LC_content_toolbar_clearbubbles {
1.911     bisitz   7528:   background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
1.774     ehlerst  7529: }
                   7530: 
1.795     www      7531: a#LC_content_toolbar_changefolder {
1.911     bisitz   7532:   background : url(/res/adm/pages/close-all-folders.gif) top center ;
1.757     schulted 7533: }
                   7534: 
1.795     www      7535: a#LC_content_toolbar_changefolder_toggled {
1.911     bisitz   7536:   background-image:url(/res/adm/pages/open-all-folders.gif);
1.757     schulted 7537: }
                   7538: 
1.1043    raeburn  7539: a#LC_content_toolbar_edittoplevel {
                   7540:   background-image:url(/res/adm/pages/edittoplevel.gif);
                   7541: }
                   7542: 
1.795     www      7543: ul#LC_toolbar li a:hover {
1.911     bisitz   7544:   background-position: bottom center;
1.757     schulted 7545: }
                   7546: 
1.795     www      7547: ul#LC_toolbar {
1.911     bisitz   7548:   padding: 0;
                   7549:   margin: 2px;
                   7550:   list-style:none;
                   7551:   position:relative;
                   7552:   background-color:white;
1.1082    raeburn  7553:   overflow: auto;
1.757     schulted 7554: }
                   7555: 
1.795     www      7556: ul#LC_toolbar li {
1.911     bisitz   7557:   border:1px solid white;
                   7558:   padding: 0;
                   7559:   margin: 0;
                   7560:   float: left;
                   7561:   display:inline;
                   7562:   vertical-align:middle;
1.1082    raeburn  7563:   white-space: nowrap;
1.911     bisitz   7564: }
1.757     schulted 7565: 
1.783     amueller 7566: 
1.795     www      7567: a.LC_toolbarItem {
1.911     bisitz   7568:   display:block;
                   7569:   padding: 0;
                   7570:   margin: 0;
                   7571:   height: 32px;
                   7572:   width: 32px;
                   7573:   color:white;
                   7574:   border: none;
                   7575:   background-repeat:no-repeat;
                   7576:   background-color:transparent;
1.757     schulted 7577: }
                   7578: 
1.915     droeschl 7579: ul.LC_funclist {
                   7580:     margin: 0;
                   7581:     padding: 0.5em 1em 0.5em 0;
                   7582: }
                   7583: 
1.933     droeschl 7584: ul.LC_funclist > li:first-child {
                   7585:     font-weight:bold; 
                   7586:     margin-left:0.8em;
                   7587: }
                   7588: 
1.915     droeschl 7589: ul.LC_funclist + ul.LC_funclist {
                   7590:     /* 
                   7591:        left border as a seperator if we have more than
                   7592:        one list 
                   7593:     */
                   7594:     border-left: 1px solid $sidebg;
                   7595:     /* 
                   7596:        this hides the left border behind the border of the 
                   7597:        outer box if element is wrapped to the next 'line' 
                   7598:     */
                   7599:     margin-left: -1px;
                   7600: }
                   7601: 
1.843     bisitz   7602: ul.LC_funclist li {
1.915     droeschl 7603:   display: inline;
1.782     bisitz   7604:   white-space: nowrap;
1.915     droeschl 7605:   margin: 0 0 0 25px;
                   7606:   line-height: 150%;
1.782     bisitz   7607: }
                   7608: 
1.974     wenzelju 7609: .LC_hidden {
                   7610:   display: none;
                   7611: }
                   7612: 
1.1030    www      7613: .LCmodal-overlay {
                   7614: 		position:fixed;
                   7615: 		top:0;
                   7616: 		right:0;
                   7617: 		bottom:0;
                   7618: 		left:0;
                   7619: 		height:100%;
                   7620: 		width:100%;
                   7621: 		margin:0;
                   7622: 		padding:0;
                   7623: 		background:#999;
                   7624: 		opacity:.75;
                   7625: 		filter: alpha(opacity=75);
                   7626: 		-moz-opacity: 0.75;
                   7627: 		z-index:101;
                   7628: }
                   7629: 
                   7630: * html .LCmodal-overlay {   
                   7631: 		position: absolute;
                   7632: 		height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
                   7633: }
                   7634: 
                   7635: .LCmodal-window {
                   7636: 		position:fixed;
                   7637: 		top:50%;
                   7638: 		left:50%;
                   7639: 		margin:0;
                   7640: 		padding:0;
                   7641: 		z-index:102;
                   7642: 	}
                   7643: 
                   7644: * html .LCmodal-window {
                   7645: 		position:absolute;
                   7646: }
                   7647: 
                   7648: .LCclose-window {
                   7649: 		position:absolute;
                   7650: 		width:32px;
                   7651: 		height:32px;
                   7652: 		right:8px;
                   7653: 		top:8px;
                   7654: 		background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
                   7655: 		text-indent:-99999px;
                   7656: 		overflow:hidden;
                   7657: 		cursor:pointer;
                   7658: }
                   7659: 
1.1100    raeburn  7660: /*
1.1231    damieng  7661:   styles used for response display
                   7662: */
                   7663: div.LC_radiofoil, div.LC_rankfoil {
                   7664:   margin: .5em 0em .5em 0em;
                   7665: }
                   7666: table.LC_itemgroup {
                   7667:   margin-top: 1em;
                   7668: }
                   7669: 
                   7670: /*
1.1100    raeburn  7671:   styles used by TTH when "Default set of options to pass to tth/m
                   7672:   when converting TeX" in course settings has been set
                   7673: 
                   7674:   option passed: -t
                   7675: 
                   7676: */
                   7677: 
                   7678: td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
                   7679: td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
                   7680: td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
                   7681: td div.norm {line-height:normal;}
                   7682: 
                   7683: /*
                   7684:   option passed -y3
                   7685: */
                   7686: 
                   7687: span.roman {font-family: serif; font-style: normal; font-weight: normal;}
                   7688: span.overacc2 {position: relative;  left: .8em; top: -1.2ex;}
                   7689: span.overacc1 {position: relative;  left: .6em; top: -1.2ex;}
                   7690: 
1.1230    damieng  7691: /*
                   7692:   sections with roles, for content only
                   7693: */
                   7694: section[class^="role-"] {
                   7695:   padding-left: 10px;
                   7696:   padding-right: 5px;
                   7697:   margin-top: 8px;
                   7698:   margin-bottom: 8px;
                   7699:   border: 1px solid #2A4;
                   7700:   border-radius: 5px;
                   7701:   box-shadow: 0px 1px 1px #BBB;
                   7702: }
                   7703: section[class^="role-"]>h1 {
                   7704:   position: relative;
                   7705:   margin: 0px;
                   7706:   padding-top: 10px;
                   7707:   padding-left: 40px;
                   7708: }
                   7709: section[class^="role-"]>h1:before {
                   7710:   position: absolute;
                   7711:   left: -5px;
                   7712:   top: 5px;
                   7713: }
                   7714: section.role-activity>h1:before {
                   7715:   content:url('/adm/daxe/images/section_icons/activity.png');
                   7716: }
                   7717: section.role-advice>h1:before {
                   7718:   content:url('/adm/daxe/images/section_icons/advice.png');
                   7719: }
                   7720: section.role-bibliography>h1:before {
                   7721:   content:url('/adm/daxe/images/section_icons/bibliography.png');
                   7722: }
                   7723: section.role-citation>h1:before {
                   7724:   content:url('/adm/daxe/images/section_icons/citation.png');
                   7725: }
                   7726: section.role-conclusion>h1:before {
                   7727:   content:url('/adm/daxe/images/section_icons/conclusion.png');
                   7728: }
                   7729: section.role-definition>h1:before {
                   7730:   content:url('/adm/daxe/images/section_icons/definition.png');
                   7731: }
                   7732: section.role-demonstration>h1:before {
                   7733:   content:url('/adm/daxe/images/section_icons/demonstration.png');
                   7734: }
                   7735: section.role-example>h1:before {
                   7736:   content:url('/adm/daxe/images/section_icons/example.png');
                   7737: }
                   7738: section.role-explanation>h1:before {
                   7739:   content:url('/adm/daxe/images/section_icons/explanation.png');
                   7740: }
                   7741: section.role-introduction>h1:before {
                   7742:   content:url('/adm/daxe/images/section_icons/introduction.png');
                   7743: }
                   7744: section.role-method>h1:before {
                   7745:   content:url('/adm/daxe/images/section_icons/method.png');
                   7746: }
                   7747: section.role-more_information>h1:before {
                   7748:   content:url('/adm/daxe/images/section_icons/more_information.png');
                   7749: }
                   7750: section.role-objectives>h1:before {
                   7751:   content:url('/adm/daxe/images/section_icons/objectives.png');
                   7752: }
                   7753: section.role-prerequisites>h1:before {
                   7754:   content:url('/adm/daxe/images/section_icons/prerequisites.png');
                   7755: }
                   7756: section.role-remark>h1:before {
                   7757:   content:url('/adm/daxe/images/section_icons/remark.png');
                   7758: }
                   7759: section.role-reminder>h1:before {
                   7760:   content:url('/adm/daxe/images/section_icons/reminder.png');
                   7761: }
                   7762: section.role-summary>h1:before {
                   7763:   content:url('/adm/daxe/images/section_icons/summary.png');
                   7764: }
                   7765: section.role-syntax>h1:before {
                   7766:   content:url('/adm/daxe/images/section_icons/syntax.png');
                   7767: }
                   7768: section.role-warning>h1:before {
                   7769:   content:url('/adm/daxe/images/section_icons/warning.png');
                   7770: }
                   7771: 
1.343     albertel 7772: END
                   7773: }
                   7774: 
1.306     albertel 7775: =pod
                   7776: 
                   7777: =item * &headtag()
                   7778: 
                   7779: Returns a uniform footer for LON-CAPA web pages.
                   7780: 
1.307     albertel 7781: Inputs: $title - optional title for the head
                   7782:         $head_extra - optional extra HTML to put inside the <head>
1.315     albertel 7783:         $args - optional arguments
1.319     albertel 7784:             force_register - if is true call registerurl so the remote is 
                   7785:                              informed
1.415     albertel 7786:             redirect       -> array ref of
                   7787:                                    1- seconds before redirect occurs
                   7788:                                    2- url to redirect to
                   7789:                                    3- whether the side effect should occur
1.315     albertel 7790:                            (side effect of setting 
                   7791:                                $env{'internal.head.redirect'} to the url 
                   7792:                                redirected too)
1.352     albertel 7793:             domain         -> force to color decorate a page for a specific
                   7794:                                domain
                   7795:             function       -> force usage of a specific rolish color scheme
                   7796:             bgcolor        -> override the default page bgcolor
1.460     albertel 7797:             no_auto_mt_title
                   7798:                            -> prevent &mt()ing the title arg
1.464     albertel 7799: 
1.306     albertel 7800: =cut
                   7801: 
                   7802: sub headtag {
1.313     albertel 7803:     my ($title,$head_extra,$args) = @_;
1.306     albertel 7804:     
1.363     albertel 7805:     my $function = $args->{'function'} || &get_users_function();
                   7806:     my $domain   = $args->{'domain'}   || &determinedomain();
                   7807:     my $bgcolor  = $args->{'bgcolor'}  || &designparm($function.'.pgbg',$domain);
1.1154    raeburn  7808:     my $httphost = $args->{'use_absolute'};
1.418     albertel 7809:     my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458     albertel 7810: 		   $Apache::lonnet::perlvar{'lonVersion'},
1.531     albertel 7811: 		   #time(),
1.418     albertel 7812: 		   $env{'environment.color.timestamp'},
1.363     albertel 7813: 		   $function,$domain,$bgcolor);
                   7814: 
1.369     www      7815:     $url = '/adm/css/'.&escape($url).'.css';
1.363     albertel 7816: 
1.308     albertel 7817:     my $result =
                   7818: 	'<head>'.
1.1160    raeburn  7819: 	&font_settings($args);
1.319     albertel 7820: 
1.1188    raeburn  7821:     my $inhibitprint;
                   7822:     if ($args->{'print_suppress'}) {
                   7823:         $inhibitprint = &print_suppression();
                   7824:     }
1.1064    raeburn  7825: 
1.461     albertel 7826:     if (!$args->{'frameset'}) {
                   7827: 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
                   7828:     }
1.962     droeschl 7829:     if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
                   7830:         $result .= Apache::lonxml::display_title();
1.319     albertel 7831:     }
1.436     albertel 7832:     if (!$args->{'no_nav_bar'} 
                   7833: 	&& !$args->{'only_body'}
                   7834: 	&& !$args->{'frameset'}) {
1.1154    raeburn  7835: 	$result .= &help_menu_js($httphost);
1.1032    www      7836:         $result.=&modal_window();
1.1038    www      7837:         $result.=&togglebox_script();
1.1034    www      7838:         $result.=&wishlist_window();
1.1041    www      7839:         $result.=&LCprogressbarUpdate_script();
1.1034    www      7840:     } else {
                   7841:         if ($args->{'add_modal'}) {
                   7842:            $result.=&modal_window();
                   7843:         }
                   7844:         if ($args->{'add_wishlist'}) {
                   7845:            $result.=&wishlist_window();
                   7846:         }
1.1038    www      7847:         if ($args->{'add_togglebox'}) {
                   7848:            $result.=&togglebox_script();
                   7849:         }
1.1041    www      7850:         if ($args->{'add_progressbar'}) {
                   7851:            $result.=&LCprogressbarUpdate_script();
                   7852:         }
1.436     albertel 7853:     }
1.314     albertel 7854:     if (ref($args->{'redirect'})) {
1.414     albertel 7855: 	my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315     albertel 7856: 	$url = &Apache::lonenc::check_encrypt($url);
1.414     albertel 7857: 	if (!$inhibit_continue) {
                   7858: 	    $env{'internal.head.redirect'} = $url;
                   7859: 	}
1.313     albertel 7860: 	$result.=<<ADDMETA
                   7861: <meta http-equiv="pragma" content="no-cache" />
1.344     albertel 7862: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313     albertel 7863: ADDMETA
1.1210    raeburn  7864:     } else {
                   7865:         unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) {
                   7866:             my $requrl = $env{'request.uri'};
                   7867:             if ($requrl eq '') {
                   7868:                 $requrl = $ENV{'REQUEST_URI'};
                   7869:                 $requrl =~ s/\?.+$//;
                   7870:             }
                   7871:             unless (($requrl =~ m{^/adm/(?:switchserver|login|authenticate|logout|groupsort|cleanup|helper|slotrequest|grades)(\?|$)}) ||
                   7872:                     (($requrl =~ m{^/res/}) && (($env{'form.submitted'} eq 'scantron') ||
                   7873:                      ($env{'form.grade_symb'}) || ($Apache::lonhomework::scantronmode)))) {
                   7874:                 my $dom_in_use = $Apache::lonnet::perlvar{'lonDefDomain'};
                   7875:                 unless (&Apache::lonnet::allowed('mau',$dom_in_use)) {
                   7876:                     my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use);
                   7877:                     if (ref($domdefs{'offloadnow'}) eq 'HASH') {
                   7878:                         my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
                   7879:                         if ($domdefs{'offloadnow'}{$lonhost}) {
                   7880:                             my $newserver = &Apache::lonnet::spareserver(30000,undef,1,$dom_in_use);
                   7881:                             if (($newserver) && ($newserver ne $lonhost)) {
                   7882:                                 my $numsec = 5;
                   7883:                                 my $timeout = $numsec * 1000;
                   7884:                                 my ($newurl,$locknum,%locks,$msg);
                   7885:                                 if ($env{'request.role.adv'}) {
                   7886:                                     ($locknum,%locks) = &Apache::lonnet::get_locks();
                   7887:                                 }
                   7888:                                 my $disable_submit = 0;
                   7889:                                 if ($requrl =~ /$LONCAPA::assess_re/) {
                   7890:                                     $disable_submit = 1;
                   7891:                                 }
                   7892:                                 if ($locknum) {
                   7893:                                     my @lockinfo = sort(values(%locks));
                   7894:                                     $msg = &mt('Once the following tasks are complete: ')."\\n".
                   7895:                                            join(", ",sort(values(%locks)))."\\n".
                   7896:                                            &mt('your session will be transferred to a different server, after you click "Roles".');
                   7897:                                 } else {
                   7898:                                     if (($requrl =~ m{^/res/}) && ($env{'form.submitted'} =~ /^part_/)) {
                   7899:                                         $msg = &mt('Your LON-CAPA submission has been recorded')."\\n";
                   7900:                                     }
                   7901:                                     $msg .= &mt('Your current LON-CAPA session will be transferred to a different server in [quant,_1,second].',$numsec);
                   7902:                                     $newurl = '/adm/switchserver?otherserver='.$newserver;
                   7903:                                     if (($env{'request.role'}) && ($env{'request.role'} ne 'cm')) {
                   7904:                                         $newurl .= '&role='.$env{'request.role'};
                   7905:                                     }
                   7906:                                     if ($env{'request.symb'}) {
                   7907:                                         $newurl .= '&symb='.$env{'request.symb'};
                   7908:                                     } else {
                   7909:                                         $newurl .= '&origurl='.$requrl;
                   7910:                                     }
                   7911:                                 }
1.1222    damieng  7912:                                 &js_escape(\$msg);
1.1210    raeburn  7913:                                 $result.=<<OFFLOAD
                   7914: <meta http-equiv="pragma" content="no-cache" />
                   7915: <script type="text/javascript">
1.1215    raeburn  7916: // <![CDATA[
1.1210    raeburn  7917: function LC_Offload_Now() {
                   7918:     var dest = "$newurl";
                   7919:     if (dest != '') {
                   7920:         window.location.href="$newurl";
                   7921:     }
                   7922: }
1.1214    raeburn  7923: \$(document).ready(function () {
                   7924:     window.alert('$msg');
                   7925:     if ($disable_submit) {
1.1210    raeburn  7926:         \$(".LC_hwk_submit").prop("disabled", true);
                   7927:         \$( ".LC_textline" ).prop( "readonly", "readonly");
1.1214    raeburn  7928:     }
                   7929:     setTimeout('LC_Offload_Now()', $timeout);
                   7930: });
1.1215    raeburn  7931: // ]]>
1.1210    raeburn  7932: </script>
                   7933: OFFLOAD
                   7934:                             }
                   7935:                         }
                   7936:                     }
                   7937:                 }
                   7938:             }
                   7939:         }
1.313     albertel 7940:     }
1.306     albertel 7941:     if (!defined($title)) {
                   7942: 	$title = 'The LearningOnline Network with CAPA';
                   7943:     }
1.460     albertel 7944:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
                   7945:     $result .= '<title> LON-CAPA '.$title.'</title>'
1.1168    raeburn  7946: 	.'<link rel="stylesheet" type="text/css" href="'.$url.'"';
                   7947:     if (!$args->{'frameset'}) {
                   7948:         $result .= ' /';
                   7949:     }
                   7950:     $result .= '>' 
1.1064    raeburn  7951:         .$inhibitprint
1.414     albertel 7952: 	.$head_extra;
1.1137    raeburn  7953:     if ($env{'browser.mobile'}) {
                   7954:         $result .= '
                   7955: <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
                   7956: <meta name="apple-mobile-web-app-capable" content="yes" />';
                   7957:     }
1.962     droeschl 7958:     return $result.'</head>';
1.306     albertel 7959: }
                   7960: 
                   7961: =pod
                   7962: 
1.340     albertel 7963: =item * &font_settings()
                   7964: 
                   7965: Returns neccessary <meta> to set the proper encoding
                   7966: 
1.1160    raeburn  7967: Inputs: optional reference to HASH -- $args passed to &headtag()
1.340     albertel 7968: 
                   7969: =cut
                   7970: 
                   7971: sub font_settings {
1.1160    raeburn  7972:     my ($args) = @_;
1.340     albertel 7973:     my $headerstring='';
1.1160    raeburn  7974:     if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
                   7975:         ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
1.1168    raeburn  7976:         $headerstring.=
                   7977:             '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"';
                   7978:         if (!$args->{'frameset'}) {
                   7979: 	    $headerstring.= ' /';
                   7980:         }
                   7981: 	$headerstring .= '>'."\n";
1.340     albertel 7982:     }
                   7983:     return $headerstring;
                   7984: }
                   7985: 
1.341     albertel 7986: =pod
                   7987: 
1.1064    raeburn  7988: =item * &print_suppression()
                   7989: 
                   7990: In course context returns css which causes the body to be blank when media="print",
                   7991: if printout generation is unavailable for the current resource.
                   7992: 
                   7993: This could be because:
                   7994: 
                   7995: (a) printstartdate is in the future
                   7996: 
                   7997: (b) printenddate is in the past
                   7998: 
                   7999: (c) there is an active exam block with "printout"
                   8000: functionality blocked
                   8001: 
                   8002: Users with pav, pfo or evb privileges are exempt.
                   8003: 
                   8004: Inputs: none
                   8005: 
                   8006: =cut
                   8007: 
                   8008: 
                   8009: sub print_suppression {
                   8010:     my $noprint;
                   8011:     if ($env{'request.course.id'}) {
                   8012:         my $scope = $env{'request.course.id'};
                   8013:         if ((&Apache::lonnet::allowed('pav',$scope)) ||
                   8014:             (&Apache::lonnet::allowed('pfo',$scope))) {
                   8015:             return;
                   8016:         }
                   8017:         if ($env{'request.course.sec'} ne '') {
                   8018:             $scope .= "/$env{'request.course.sec'}";
                   8019:             if ((&Apache::lonnet::allowed('pav',$scope)) ||
                   8020:                 (&Apache::lonnet::allowed('pfo',$scope))) {
1.1065    raeburn  8021:                 return;
1.1064    raeburn  8022:             }
                   8023:         }
                   8024:         my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   8025:         my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1189    raeburn  8026:         my $blocked = &blocking_status('printout',$cnum,$cdom,undef,1);
1.1064    raeburn  8027:         if ($blocked) {
                   8028:             my $checkrole = "cm./$cdom/$cnum";
                   8029:             if ($env{'request.course.sec'} ne '') {
                   8030:                 $checkrole .= "/$env{'request.course.sec'}";
                   8031:             }
                   8032:             unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
                   8033:                     ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
                   8034:                 $noprint = 1;
                   8035:             }
                   8036:         }
                   8037:         unless ($noprint) {
                   8038:             my $symb = &Apache::lonnet::symbread();
                   8039:             if ($symb ne '') {
                   8040:                 my $navmap = Apache::lonnavmaps::navmap->new();
                   8041:                 if (ref($navmap)) {
                   8042:                     my $res = $navmap->getBySymb($symb);
                   8043:                     if (ref($res)) {
                   8044:                         if (!$res->resprintable()) {
                   8045:                             $noprint = 1;
                   8046:                         }
                   8047:                     }
                   8048:                 }
                   8049:             }
                   8050:         }
                   8051:         if ($noprint) {
                   8052:             return <<"ENDSTYLE";
                   8053: <style type="text/css" media="print">
                   8054:     body { display:none }
                   8055: </style>
                   8056: ENDSTYLE
                   8057:         }
                   8058:     }
                   8059:     return;
                   8060: }
                   8061: 
                   8062: =pod
                   8063: 
1.341     albertel 8064: =item * &xml_begin()
                   8065: 
                   8066: Returns the needed doctype and <html>
                   8067: 
                   8068: Inputs: none
                   8069: 
                   8070: =cut
                   8071: 
                   8072: sub xml_begin {
1.1168    raeburn  8073:     my ($is_frameset) = @_;
1.341     albertel 8074:     my $output='';
                   8075: 
                   8076:     if ($env{'browser.mathml'}) {
                   8077: 	$output='<?xml version="1.0"?>'
                   8078:             #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
                   8079: #            .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
                   8080:             
                   8081: #	    .'<!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">] >'
                   8082: 	    .'<!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">'
                   8083:             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" ' 
                   8084: 	    .'xmlns="http://www.w3.org/1999/xhtml">';
1.1168    raeburn  8085:     } elsif ($is_frameset) {
                   8086:         $output='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'."\n".
                   8087:                 '<html>'."\n";
1.341     albertel 8088:     } else {
1.1168    raeburn  8089: 	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n".
                   8090:                 '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
1.341     albertel 8091:     }
                   8092:     return $output;
                   8093: }
1.340     albertel 8094: 
                   8095: =pod
                   8096: 
1.306     albertel 8097: =item * &start_page()
                   8098: 
                   8099: Returns a complete <html> .. <body> section for LON-CAPA web pages.
                   8100: 
1.648     raeburn  8101: Inputs:
                   8102: 
                   8103: =over 4
                   8104: 
                   8105: $title - optional title for the page
                   8106: 
                   8107: $head_extra - optional extra HTML to incude inside the <head>
                   8108: 
                   8109: $args - additional optional args supported are:
                   8110: 
                   8111: =over 8
                   8112: 
                   8113:              only_body      -> is true will set &bodytag() onlybodytag
1.317     albertel 8114:                                     arg on
1.814     bisitz   8115:              no_nav_bar     -> is true will set &bodytag() no_nav_bar arg on
1.648     raeburn  8116:              add_entries    -> additional attributes to add to the  <body>
                   8117:              domain         -> force to color decorate a page for a 
1.317     albertel 8118:                                     specific domain
1.648     raeburn  8119:              function       -> force usage of a specific rolish color
1.317     albertel 8120:                                     scheme
1.648     raeburn  8121:              redirect       -> see &headtag()
                   8122:              bgcolor        -> override the default page bg color
                   8123:              js_ready       -> return a string ready for being used in 
1.317     albertel 8124:                                     a javascript writeln
1.648     raeburn  8125:              html_encode    -> return a string ready for being used in 
1.320     albertel 8126:                                     a html attribute
1.648     raeburn  8127:              force_register -> if is true will turn on the &bodytag()
1.317     albertel 8128:                                     $forcereg arg
1.648     raeburn  8129:              frameset       -> if true will start with a <frameset>
1.330     albertel 8130:                                     rather than <body>
1.648     raeburn  8131:              skip_phases    -> hash ref of 
1.338     albertel 8132:                                     head -> skip the <html><head> generation
                   8133:                                     body -> skip all <body> generation
1.648     raeburn  8134:              no_auto_mt_title -> prevent &mt()ing the title arg
1.867     kalberla 8135:              bread_crumbs ->             Array containing breadcrumbs
1.983     raeburn  8136:              bread_crumbs_component ->  if exists show it as headline else show only the breadcrumbs
1.1096    raeburn  8137:              group          -> includes the current group, if page is for a 
                   8138:                                specific group  
1.361     albertel 8139: 
1.648     raeburn  8140: =back
1.460     albertel 8141: 
1.648     raeburn  8142: =back
1.562     albertel 8143: 
1.306     albertel 8144: =cut
                   8145: 
                   8146: sub start_page {
1.309     albertel 8147:     my ($title,$head_extra,$args) = @_;
1.318     albertel 8148:     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.319     albertel 8149: 
1.315     albertel 8150:     $env{'internal.start_page'}++;
1.1096    raeburn  8151:     my ($result,@advtools);
1.964     droeschl 8152: 
1.338     albertel 8153:     if (! exists($args->{'skip_phases'}{'head'}) ) {
1.1168    raeburn  8154:         $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
1.338     albertel 8155:     }
                   8156:     
                   8157:     if (! exists($args->{'skip_phases'}{'body'}) ) {
                   8158: 	if ($args->{'frameset'}) {
                   8159: 	    my $attr_string = &make_attr_string($args->{'force_register'},
                   8160: 						$args->{'add_entries'});
                   8161: 	    $result .= "\n<frameset $attr_string>\n";
1.831     bisitz   8162:         } else {
                   8163:             $result .=
                   8164:                 &bodytag($title, 
                   8165:                          $args->{'function'},       $args->{'add_entries'},
                   8166:                          $args->{'only_body'},      $args->{'domain'},
                   8167:                          $args->{'force_register'}, $args->{'no_nav_bar'},
1.1096    raeburn  8168:                          $args->{'bgcolor'},        $args,
                   8169:                          \@advtools);
1.831     bisitz   8170:         }
1.330     albertel 8171:     }
1.338     albertel 8172: 
1.315     albertel 8173:     if ($args->{'js_ready'}) {
1.713     kaisler  8174: 		$result = &js_ready($result);
1.315     albertel 8175:     }
1.320     albertel 8176:     if ($args->{'html_encode'}) {
1.713     kaisler  8177: 		$result = &html_encode($result);
                   8178:     }
                   8179: 
1.813     bisitz   8180:     # Preparation for new and consistent functionlist at top of screen
                   8181:     # if ($args->{'functionlist'}) {
                   8182:     #            $result .= &build_functionlist();
                   8183:     #}
                   8184: 
1.964     droeschl 8185:     # Don't add anything more if only_body wanted or in const space
                   8186:     return $result if    $args->{'only_body'} 
                   8187:                       || $env{'request.state'} eq 'construct';
1.813     bisitz   8188: 
                   8189:     #Breadcrumbs
1.758     kaisler  8190:     if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
                   8191: 		&Apache::lonhtmlcommon::clear_breadcrumbs();
                   8192: 		#if any br links exists, add them to the breadcrumbs
                   8193: 		if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {         
                   8194: 			foreach my $crumb (@{$args->{'bread_crumbs'}}){
                   8195: 				&Apache::lonhtmlcommon::add_breadcrumb($crumb);
                   8196: 			}
                   8197: 		}
1.1096    raeburn  8198:                 # if @advtools array contains items add then to the breadcrumbs
                   8199:                 if (@advtools > 0) {
                   8200:                     &Apache::lonmenu::advtools_crumbs(@advtools);
                   8201:                 }
1.758     kaisler  8202: 
                   8203: 		#if bread_crumbs_component exists show it as headline else show only the breadcrumbs
                   8204: 		if(exists($args->{'bread_crumbs_component'})){
                   8205: 			$result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'});
1.1237  ! raeburn  8206: 		} elsif ($args->{'crstype'} eq 'Placement') {
        !          8207: 			$result .= &Apache::lonhtmlcommon::breadcrumbs('','','','','','','','','',
        !          8208:                                                                        $args->{'crstype'});
        !          8209:                 } else {
1.758     kaisler  8210: 			$result .= &Apache::lonhtmlcommon::breadcrumbs();
                   8211: 		}
1.320     albertel 8212:     }
1.315     albertel 8213:     return $result;
1.306     albertel 8214: }
                   8215: 
                   8216: sub end_page {
1.315     albertel 8217:     my ($args) = @_;
                   8218:     $env{'internal.end_page'}++;
1.330     albertel 8219:     my $result;
1.335     albertel 8220:     if ($args->{'discussion'}) {
                   8221: 	my ($target,$parser);
                   8222: 	if (ref($args->{'discussion'})) {
                   8223: 	    ($target,$parser) =($args->{'discussion'}{'target'},
                   8224: 				$args->{'discussion'}{'parser'});
                   8225: 	}
                   8226: 	$result .= &Apache::lonxml::xmlend($target,$parser);
                   8227:     }
1.330     albertel 8228:     if ($args->{'frameset'}) {
                   8229: 	$result .= '</frameset>';
                   8230:     } else {
1.635     raeburn  8231: 	$result .= &endbodytag($args);
1.330     albertel 8232:     }
1.1080    raeburn  8233:     unless ($args->{'notbody'}) {
                   8234:         $result .= "\n</html>";
                   8235:     }
1.330     albertel 8236: 
1.315     albertel 8237:     if ($args->{'js_ready'}) {
1.317     albertel 8238: 	$result = &js_ready($result);
1.315     albertel 8239:     }
1.335     albertel 8240: 
1.320     albertel 8241:     if ($args->{'html_encode'}) {
                   8242: 	$result = &html_encode($result);
                   8243:     }
1.335     albertel 8244: 
1.315     albertel 8245:     return $result;
                   8246: }
                   8247: 
1.1034    www      8248: sub wishlist_window {
                   8249:     return(<<'ENDWISHLIST');
1.1046    raeburn  8250: <script type="text/javascript">
1.1034    www      8251: // <![CDATA[
                   8252: // <!-- BEGIN LON-CAPA Internal
                   8253: function set_wishlistlink(title, path) {
                   8254:     if (!title) {
                   8255:         title = document.title;
                   8256:         title = title.replace(/^LON-CAPA /,'');
                   8257:     }
1.1175    raeburn  8258:     title = encodeURIComponent(title);
1.1203    raeburn  8259:     title = title.replace("'","\\\'");
1.1034    www      8260:     if (!path) {
                   8261:         path = location.pathname;
                   8262:     }
1.1175    raeburn  8263:     path = encodeURIComponent(path);
1.1203    raeburn  8264:     path = path.replace("'","\\\'");
1.1034    www      8265:     Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path,
                   8266:                       'wishlistNewLink','width=560,height=350,scrollbars=0');
                   8267: }
                   8268: // END LON-CAPA Internal -->
                   8269: // ]]>
                   8270: </script>
                   8271: ENDWISHLIST
                   8272: }
                   8273: 
1.1030    www      8274: sub modal_window {
                   8275:     return(<<'ENDMODAL');
1.1046    raeburn  8276: <script type="text/javascript">
1.1030    www      8277: // <![CDATA[
                   8278: // <!-- BEGIN LON-CAPA Internal
                   8279: var modalWindow = {
                   8280: 	parent:"body",
                   8281: 	windowId:null,
                   8282: 	content:null,
                   8283: 	width:null,
                   8284: 	height:null,
                   8285: 	close:function()
                   8286: 	{
                   8287: 	        $(".LCmodal-window").remove();
                   8288: 	        $(".LCmodal-overlay").remove();
                   8289: 	},
                   8290: 	open:function()
                   8291: 	{
                   8292: 		var modal = "";
                   8293: 		modal += "<div class=\"LCmodal-overlay\"></div>";
                   8294: 		modal += "<div id=\"" + this.windowId + "\" class=\"LCmodal-window\" style=\"width:" + this.width + "px; height:" + this.height + "px; margin-top:-" + (this.height / 2) + "px; margin-left:-" + (this.width / 2) + "px;\">";
                   8295: 		modal += this.content;
                   8296: 		modal += "</div>";	
                   8297: 
                   8298: 		$(this.parent).append(modal);
                   8299: 
                   8300: 		$(".LCmodal-window").append("<a class=\"LCclose-window\"></a>");
                   8301: 		$(".LCclose-window").click(function(){modalWindow.close();});
                   8302: 		$(".LCmodal-overlay").click(function(){modalWindow.close();});
                   8303: 	}
                   8304: };
1.1140    raeburn  8305: 	var openMyModal = function(source,width,height,scrolling,transparency,style)
1.1030    www      8306: 	{
1.1203    raeburn  8307:                 source = source.replace("'","&#39;");
1.1030    www      8308: 		modalWindow.windowId = "myModal";
                   8309: 		modalWindow.width = width;
                   8310: 		modalWindow.height = height;
1.1196    raeburn  8311: 		modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'></iframe>";
1.1030    www      8312: 		modalWindow.open();
1.1208    raeburn  8313: 	};
1.1030    www      8314: // END LON-CAPA Internal -->
                   8315: // ]]>
                   8316: </script>
                   8317: ENDMODAL
                   8318: }
                   8319: 
                   8320: sub modal_link {
1.1140    raeburn  8321:     my ($link,$linktext,$width,$height,$target,$scrolling,$title,$transparency,$style)=@_;
1.1030    www      8322:     unless ($width) { $width=480; }
                   8323:     unless ($height) { $height=400; }
1.1031    www      8324:     unless ($scrolling) { $scrolling='yes'; }
1.1140    raeburn  8325:     unless ($transparency) { $transparency='true'; }
                   8326: 
1.1074    raeburn  8327:     my $target_attr;
                   8328:     if (defined($target)) {
                   8329:         $target_attr = 'target="'.$target.'"';
                   8330:     }
                   8331:     return <<"ENDLINK";
1.1140    raeburn  8332: <a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">
1.1074    raeburn  8333:            $linktext</a>
                   8334: ENDLINK
1.1030    www      8335: }
                   8336: 
1.1032    www      8337: sub modal_adhoc_script {
                   8338:     my ($funcname,$width,$height,$content)=@_;
                   8339:     return (<<ENDADHOC);
1.1046    raeburn  8340: <script type="text/javascript">
1.1032    www      8341: // <![CDATA[
                   8342:         var $funcname = function()
                   8343:         {
                   8344:                 modalWindow.windowId = "myModal";
                   8345:                 modalWindow.width = $width;
                   8346:                 modalWindow.height = $height;
                   8347:                 modalWindow.content = '$content';
                   8348:                 modalWindow.open();
                   8349:         };  
                   8350: // ]]>
                   8351: </script>
                   8352: ENDADHOC
                   8353: }
                   8354: 
1.1041    www      8355: sub modal_adhoc_inner {
                   8356:     my ($funcname,$width,$height,$content)=@_;
                   8357:     my $innerwidth=$width-20;
                   8358:     $content=&js_ready(
1.1140    raeburn  8359:                  &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
                   8360:                  &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).
                   8361:                  $content.
1.1041    www      8362:                  &end_scrollbox().
1.1140    raeburn  8363:                  &end_page()
1.1041    www      8364:              );
                   8365:     return &modal_adhoc_script($funcname,$width,$height,$content);
                   8366: }
                   8367: 
                   8368: sub modal_adhoc_window {
                   8369:     my ($funcname,$width,$height,$content,$linktext)=@_;
                   8370:     return &modal_adhoc_inner($funcname,$width,$height,$content).
                   8371:            "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
                   8372: }
                   8373: 
                   8374: sub modal_adhoc_launch {
                   8375:     my ($funcname,$width,$height,$content)=@_;
                   8376:     return &modal_adhoc_inner($funcname,$width,$height,$content).(<<ENDLAUNCH);
                   8377: <script type="text/javascript">
                   8378: // <![CDATA[
                   8379: $funcname();
                   8380: // ]]>
                   8381: </script>
                   8382: ENDLAUNCH
                   8383: }
                   8384: 
                   8385: sub modal_adhoc_close {
                   8386:     return (<<ENDCLOSE);
                   8387: <script type="text/javascript">
                   8388: // <![CDATA[
                   8389: modalWindow.close();
                   8390: // ]]>
                   8391: </script>
                   8392: ENDCLOSE
                   8393: }
                   8394: 
1.1038    www      8395: sub togglebox_script {
                   8396:    return(<<ENDTOGGLE);
                   8397: <script type="text/javascript"> 
                   8398: // <![CDATA[
                   8399: function LCtoggleDisplay(id,hidetext,showtext) {
                   8400:    link = document.getElementById(id + "link").childNodes[0];
                   8401:    with (document.getElementById(id).style) {
                   8402:       if (display == "none" ) {
                   8403:           display = "inline";
                   8404:           link.nodeValue = hidetext;
                   8405:         } else {
                   8406:           display = "none";
                   8407:           link.nodeValue = showtext;
                   8408:        }
                   8409:    }
                   8410: }
                   8411: // ]]>
                   8412: </script>
                   8413: ENDTOGGLE
                   8414: }
                   8415: 
1.1039    www      8416: sub start_togglebox {
                   8417:     my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
                   8418:     unless ($heading) { $heading=''; } else { $heading.=' '; }
                   8419:     unless ($showtext) { $showtext=&mt('show'); }
                   8420:     unless ($hidetext) { $hidetext=&mt('hide'); }
                   8421:     unless ($headerbg) { $headerbg='#FFFFFF'; }
                   8422:     return &start_data_table().
                   8423:            &start_data_table_header_row().
                   8424:            '<td bgcolor="'.$headerbg.'">'.$heading.
                   8425:            '[<a id="'.$id.'link" href="javascript:LCtoggleDisplay(\''.$id.'\',\''.$hidetext.'\',\''.
                   8426:            $showtext.'\')">'.$showtext.'</a>]</td>'.
                   8427:            &end_data_table_header_row().
                   8428:            '<tr id="'.$id.'" style="display:none""><td>';
                   8429: }
                   8430: 
                   8431: sub end_togglebox {
                   8432:     return '</td></tr>'.&end_data_table();
                   8433: }
                   8434: 
1.1041    www      8435: sub LCprogressbar_script {
1.1045    www      8436:    my ($id)=@_;
1.1041    www      8437:    return(<<ENDPROGRESS);
                   8438: <script type="text/javascript">
                   8439: // <![CDATA[
1.1045    www      8440: \$('#progressbar$id').progressbar({
1.1041    www      8441:   value: 0,
                   8442:   change: function(event, ui) {
                   8443:     var newVal = \$(this).progressbar('option', 'value');
                   8444:     \$('.pblabel', this).text(LCprogressTxt);
                   8445:   }
                   8446: });
                   8447: // ]]>
                   8448: </script>
                   8449: ENDPROGRESS
                   8450: }
                   8451: 
                   8452: sub LCprogressbarUpdate_script {
                   8453:    return(<<ENDPROGRESSUPDATE);
                   8454: <style type="text/css">
                   8455: .ui-progressbar { position:relative; }
                   8456: .pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
                   8457: </style>
                   8458: <script type="text/javascript">
                   8459: // <![CDATA[
1.1045    www      8460: var LCprogressTxt='---';
                   8461: 
                   8462: function LCupdateProgress(percent,progresstext,id) {
1.1041    www      8463:    LCprogressTxt=progresstext;
1.1045    www      8464:    \$('#progressbar'+id).progressbar('value',percent);
1.1041    www      8465: }
                   8466: // ]]>
                   8467: </script>
                   8468: ENDPROGRESSUPDATE
                   8469: }
                   8470: 
1.1042    www      8471: my $LClastpercent;
1.1045    www      8472: my $LCidcnt;
                   8473: my $LCcurrentid;
1.1042    www      8474: 
1.1041    www      8475: sub LCprogressbar {
1.1042    www      8476:     my ($r)=(@_);
                   8477:     $LClastpercent=0;
1.1045    www      8478:     $LCidcnt++;
                   8479:     $LCcurrentid=$$.'_'.$LCidcnt;
1.1041    www      8480:     my $starting=&mt('Starting');
                   8481:     my $content=(<<ENDPROGBAR);
1.1045    www      8482:   <div id="progressbar$LCcurrentid">
1.1041    www      8483:     <span class="pblabel">$starting</span>
                   8484:   </div>
                   8485: ENDPROGBAR
1.1045    www      8486:     &r_print($r,$content.&LCprogressbar_script($LCcurrentid));
1.1041    www      8487: }
                   8488: 
                   8489: sub LCprogressbarUpdate {
1.1042    www      8490:     my ($r,$val,$text)=@_;
                   8491:     unless ($val) { 
                   8492:        if ($LClastpercent) {
                   8493:            $val=$LClastpercent;
                   8494:        } else {
                   8495:            $val=0;
                   8496:        }
                   8497:     }
1.1041    www      8498:     if ($val<0) { $val=0; }
                   8499:     if ($val>100) { $val=0; }
1.1042    www      8500:     $LClastpercent=$val;
1.1041    www      8501:     unless ($text) { $text=$val.'%'; }
                   8502:     $text=&js_ready($text);
1.1044    www      8503:     &r_print($r,<<ENDUPDATE);
1.1041    www      8504: <script type="text/javascript">
                   8505: // <![CDATA[
1.1045    www      8506: LCupdateProgress($val,'$text','$LCcurrentid');
1.1041    www      8507: // ]]>
                   8508: </script>
                   8509: ENDUPDATE
1.1035    www      8510: }
                   8511: 
1.1042    www      8512: sub LCprogressbarClose {
                   8513:     my ($r)=@_;
                   8514:     $LClastpercent=0;
1.1044    www      8515:     &r_print($r,<<ENDCLOSE);
1.1042    www      8516: <script type="text/javascript">
                   8517: // <![CDATA[
1.1045    www      8518: \$("#progressbar$LCcurrentid").hide('slow'); 
1.1042    www      8519: // ]]>
                   8520: </script>
                   8521: ENDCLOSE
1.1044    www      8522: }
                   8523: 
                   8524: sub r_print {
                   8525:     my ($r,$to_print)=@_;
                   8526:     if ($r) {
                   8527:       $r->print($to_print);
                   8528:       $r->rflush();
                   8529:     } else {
                   8530:       print($to_print);
                   8531:     }
1.1042    www      8532: }
                   8533: 
1.320     albertel 8534: sub html_encode {
                   8535:     my ($result) = @_;
                   8536: 
1.322     albertel 8537:     $result = &HTML::Entities::encode($result,'<>&"');
1.320     albertel 8538:     
                   8539:     return $result;
                   8540: }
1.1044    www      8541: 
1.317     albertel 8542: sub js_ready {
                   8543:     my ($result) = @_;
                   8544: 
1.323     albertel 8545:     $result =~ s/[\n\r]/ /xmsg;
                   8546:     $result =~ s/\\/\\\\/xmsg;
                   8547:     $result =~ s/'/\\'/xmsg;
1.372     albertel 8548:     $result =~ s{</}{<\\/}xmsg;
1.317     albertel 8549:     
                   8550:     return $result;
                   8551: }
                   8552: 
1.315     albertel 8553: sub validate_page {
                   8554:     if (  exists($env{'internal.start_page'})
1.316     albertel 8555: 	  &&     $env{'internal.start_page'} > 1) {
                   8556: 	&Apache::lonnet::logthis('start_page called multiple times '.
1.318     albertel 8557: 				 $env{'internal.start_page'}.' '.
1.316     albertel 8558: 				 $ENV{'request.filename'});
1.315     albertel 8559:     }
                   8560:     if (  exists($env{'internal.end_page'})
1.316     albertel 8561: 	  &&     $env{'internal.end_page'} > 1) {
                   8562: 	&Apache::lonnet::logthis('end_page called multiple times '.
1.318     albertel 8563: 				 $env{'internal.end_page'}.' '.
1.316     albertel 8564: 				 $env{'request.filename'});
1.315     albertel 8565:     }
                   8566:     if (     exists($env{'internal.start_page'})
                   8567: 	&& ! exists($env{'internal.end_page'})) {
1.316     albertel 8568: 	&Apache::lonnet::logthis('start_page called without end_page '.
                   8569: 				 $env{'request.filename'});
1.315     albertel 8570:     }
                   8571:     if (   ! exists($env{'internal.start_page'})
                   8572: 	&&   exists($env{'internal.end_page'})) {
1.316     albertel 8573: 	&Apache::lonnet::logthis('end_page called without start_page'.
                   8574: 				 $env{'request.filename'});
1.315     albertel 8575:     }
1.306     albertel 8576: }
1.315     albertel 8577: 
1.996     www      8578: 
                   8579: sub start_scrollbox {
1.1140    raeburn  8580:     my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready) = @_;
1.998     raeburn  8581:     unless ($outerwidth) { $outerwidth='520px'; }
                   8582:     unless ($width) { $width='500px'; }
                   8583:     unless ($height) { $height='200px'; }
1.1075    raeburn  8584:     my ($table_id,$div_id,$tdcol);
1.1018    raeburn  8585:     if ($id ne '') {
1.1140    raeburn  8586:         $table_id = ' id="table_'.$id.'"';
1.1137    raeburn  8587:         $div_id = ' id="div_'.$id.'"';
1.1018    raeburn  8588:     }
1.1075    raeburn  8589:     if ($bgcolor ne '') {
                   8590:         $tdcol = "background-color: $bgcolor;";
                   8591:     }
1.1137    raeburn  8592:     my $nicescroll_js;
                   8593:     if ($env{'browser.mobile'}) {
1.1140    raeburn  8594:         $nicescroll_js = &nicescroll_javascript('div_'.$id,$cursor,$needjsready);
                   8595:     }
                   8596:     return <<"END";
                   8597: $nicescroll_js
                   8598: 
                   8599: <table style="width: $outerwidth; border: 1px solid none;"$table_id><tr><td style="width: $width;$tdcol">
                   8600: <div style="overflow:auto; width:$width; height:$height;"$div_id>
                   8601: END
                   8602: }
                   8603: 
                   8604: sub end_scrollbox {
                   8605:     return '</div></td></tr></table>';
                   8606: }
                   8607: 
                   8608: sub nicescroll_javascript {
                   8609:     my ($id,$cursor,$needjsready,$framecheck,$location) = @_;
                   8610:     my %options;
                   8611:     if (ref($cursor) eq 'HASH') {
                   8612:         %options = %{$cursor};
                   8613:     }
                   8614:     unless ($options{'railalign'} =~ /^left|right$/) {
                   8615:         $options{'railalign'} = 'left';
                   8616:     }
                   8617:     unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
                   8618:         my $function  = &get_users_function();
                   8619:         $options{'cursorcolor'} = &designparm($function.'.sidebg',$env{'request.role.domain'});
1.1138    raeburn  8620:         unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
1.1140    raeburn  8621:             $options{'cursorcolor'} = '#00F';
1.1138    raeburn  8622:         }
1.1140    raeburn  8623:     }
                   8624:     if ($options{'cursoropacity'} =~ /^[\d.]+$/) {
                   8625:         unless ($options{'cursoropacity'} >= 0.0 && $options{'cursoropacity'} <=1.0) {
1.1138    raeburn  8626:             $options{'cursoropacity'}='1.0';
                   8627:         }
1.1140    raeburn  8628:     } else {
                   8629:         $options{'cursoropacity'}='1.0';
                   8630:     }
                   8631:     if ($options{'cursorfixedheight'} eq 'none') {
                   8632:         delete($options{'cursorfixedheight'});
                   8633:     } else {
                   8634:         unless ($options{'cursorfixedheight'} =~ /^\d+$/) { $options{'cursorfixedheight'}='50'; }
                   8635:     }
                   8636:     unless ($options{'railoffset'} =~ /^{[\w\:\d\-,]+}$/) {
                   8637:         delete($options{'railoffset'});
                   8638:     }
                   8639:     my @niceoptions;
                   8640:     while (my($key,$value) = each(%options)) {
                   8641:         if ($value =~ /^\{.+\}$/) {
                   8642:             push(@niceoptions,$key.':'.$value);
1.1138    raeburn  8643:         } else {
1.1140    raeburn  8644:             push(@niceoptions,$key.':"'.$value.'"');
1.1138    raeburn  8645:         }
1.1140    raeburn  8646:     }
                   8647:     my $nicescroll_js = '
1.1137    raeburn  8648: $(document).ready(
1.1140    raeburn  8649:       function() {
                   8650:           $("#'.$id.'").niceScroll({'.join(',',@niceoptions).'});
                   8651:       }
1.1137    raeburn  8652: );
                   8653: ';
1.1140    raeburn  8654:     if ($framecheck) {
                   8655:         $nicescroll_js .= '
                   8656: function expand_div(caller) {
                   8657:     if (top === self) {
                   8658:         document.getElementById("'.$id.'").style.width = "auto";
                   8659:         document.getElementById("'.$id.'").style.height = "auto";
                   8660:     } else {
                   8661:         try {
                   8662:             if (parent.frames) {
                   8663:                 if (parent.frames.length > 1) {
                   8664:                     var framesrc = parent.frames[1].location.href;
                   8665:                     var currsrc = framesrc.replace(/\#.*$/,"");
                   8666:                     if ((caller == "search") || (currsrc == "'.$location.'")) {
                   8667:                         document.getElementById("'.$id.'").style.width = "auto";
                   8668:                         document.getElementById("'.$id.'").style.height = "auto";
                   8669:                     }
                   8670:                 }
                   8671:             }
                   8672:         } catch (e) {
                   8673:             return;
                   8674:         }
1.1137    raeburn  8675:     }
1.1140    raeburn  8676:     return;
1.996     www      8677: }
1.1140    raeburn  8678: ';
                   8679:     }
                   8680:     if ($needjsready) {
                   8681:         $nicescroll_js = '
                   8682: <script type="text/javascript">'."\n".$nicescroll_js."\n</script>\n";
                   8683:     } else {
                   8684:         $nicescroll_js = &Apache::lonhtmlcommon::scripttag($nicescroll_js);
                   8685:     }
                   8686:     return $nicescroll_js;
1.996     www      8687: }
                   8688: 
1.318     albertel 8689: sub simple_error_page {
1.1150    bisitz   8690:     my ($r,$title,$msg,$args) = @_;
1.1151    raeburn  8691:     if (ref($args) eq 'HASH') {
                   8692:         if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
                   8693:     } else {
                   8694:         $msg = &mt($msg);
                   8695:     }
1.1150    bisitz   8696: 
1.318     albertel 8697:     my $page =
                   8698: 	&Apache::loncommon::start_page($title).
1.1150    bisitz   8699: 	'<p class="LC_error">'.$msg.'</p>'.
1.318     albertel 8700: 	&Apache::loncommon::end_page();
                   8701:     if (ref($r)) {
                   8702: 	$r->print($page);
1.327     albertel 8703: 	return;
1.318     albertel 8704:     }
                   8705:     return $page;
                   8706: }
1.347     albertel 8707: 
                   8708: {
1.610     albertel 8709:     my @row_count;
1.961     onken    8710: 
                   8711:     sub start_data_table_count {
                   8712:         unshift(@row_count, 0);
                   8713:         return;
                   8714:     }
                   8715: 
                   8716:     sub end_data_table_count {
                   8717:         shift(@row_count);
                   8718:         return;
                   8719:     }
                   8720: 
1.347     albertel 8721:     sub start_data_table {
1.1018    raeburn  8722: 	my ($add_class,$id) = @_;
1.422     albertel 8723: 	my $css_class = (join(' ','LC_data_table',$add_class));
1.1018    raeburn  8724:         my $table_id;
                   8725:         if (defined($id)) {
                   8726:             $table_id = ' id="'.$id.'"';
                   8727:         }
1.961     onken    8728: 	&start_data_table_count();
1.1018    raeburn  8729: 	return '<table class="'.$css_class.'"'.$table_id.'>'."\n";
1.347     albertel 8730:     }
                   8731: 
                   8732:     sub end_data_table {
1.961     onken    8733: 	&end_data_table_count();
1.389     albertel 8734: 	return '</table>'."\n";;
1.347     albertel 8735:     }
                   8736: 
                   8737:     sub start_data_table_row {
1.974     wenzelju 8738: 	my ($add_class, $id) = @_;
1.610     albertel 8739: 	$row_count[0]++;
                   8740: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900     bisitz   8741: 	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
1.974     wenzelju 8742:         $id = (' id="'.$id.'"') unless ($id eq '');
                   8743:         return  '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.347     albertel 8744:     }
1.471     banghart 8745:     
                   8746:     sub continue_data_table_row {
1.974     wenzelju 8747: 	my ($add_class, $id) = @_;
1.610     albertel 8748: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.974     wenzelju 8749: 	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
                   8750:         $id = (' id="'.$id.'"') unless ($id eq '');
                   8751:         return  '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.471     banghart 8752:     }
1.347     albertel 8753: 
                   8754:     sub end_data_table_row {
1.389     albertel 8755: 	return '</tr>'."\n";;
1.347     albertel 8756:     }
1.367     www      8757: 
1.421     albertel 8758:     sub start_data_table_empty_row {
1.707     bisitz   8759: #	$row_count[0]++;
1.421     albertel 8760: 	return  '<tr class="LC_empty_row" >'."\n";;
                   8761:     }
                   8762: 
                   8763:     sub end_data_table_empty_row {
                   8764: 	return '</tr>'."\n";;
                   8765:     }
                   8766: 
1.367     www      8767:     sub start_data_table_header_row {
1.389     albertel 8768: 	return  '<tr class="LC_header_row">'."\n";;
1.367     www      8769:     }
                   8770: 
                   8771:     sub end_data_table_header_row {
1.389     albertel 8772: 	return '</tr>'."\n";;
1.367     www      8773:     }
1.890     droeschl 8774: 
                   8775:     sub data_table_caption {
                   8776:         my $caption = shift;
                   8777:         return "<caption class=\"LC_caption\">$caption</caption>";
                   8778:     }
1.347     albertel 8779: }
                   8780: 
1.548     albertel 8781: =pod
                   8782: 
                   8783: =item * &inhibit_menu_check($arg)
                   8784: 
                   8785: Checks for a inhibitmenu state and generates output to preserve it
                   8786: 
                   8787: Inputs:         $arg - can be any of
                   8788:                      - undef - in which case the return value is a string 
                   8789:                                to add  into arguments list of a uri
                   8790:                      - 'input' - in which case the return value is a HTML
                   8791:                                  <form> <input> field of type hidden to
                   8792:                                  preserve the value
                   8793:                      - a url - in which case the return value is the url with
                   8794:                                the neccesary cgi args added to preserve the
                   8795:                                inhibitmenu state
                   8796:                      - a ref to a url - no return value, but the string is
                   8797:                                         updated to include the neccessary cgi
                   8798:                                         args to preserve the inhibitmenu state
                   8799: 
                   8800: =cut
                   8801: 
                   8802: sub inhibit_menu_check {
                   8803:     my ($arg) = @_;
                   8804:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   8805:     if ($arg eq 'input') {
                   8806: 	if ($env{'form.inhibitmenu'}) {
                   8807: 	    return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
                   8808: 	} else {
                   8809: 	    return
                   8810: 	}
                   8811:     }
                   8812:     if ($env{'form.inhibitmenu'}) {
                   8813: 	if (ref($arg)) {
                   8814: 	    $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   8815: 	} elsif ($arg eq '') {
                   8816: 	    $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
                   8817: 	} else {
                   8818: 	    $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   8819: 	}
                   8820:     }
                   8821:     if (!ref($arg)) {
                   8822: 	return $arg;
                   8823:     }
                   8824: }
                   8825: 
1.251     albertel 8826: ###############################################
1.182     matthew  8827: 
                   8828: =pod
                   8829: 
1.549     albertel 8830: =back
                   8831: 
                   8832: =head1 User Information Routines
                   8833: 
                   8834: =over 4
                   8835: 
1.405     albertel 8836: =item * &get_users_function()
1.182     matthew  8837: 
                   8838: Used by &bodytag to determine the current users primary role.
                   8839: Returns either 'student','coordinator','admin', or 'author'.
                   8840: 
                   8841: =cut
                   8842: 
                   8843: ###############################################
                   8844: sub get_users_function {
1.815     tempelho 8845:     my $function = 'norole';
1.818     tempelho 8846:     if ($env{'request.role'}=~/^(st)/) {
                   8847:         $function='student';
                   8848:     }
1.907     raeburn  8849:     if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/) {
1.182     matthew  8850:         $function='coordinator';
                   8851:     }
1.258     albertel 8852:     if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182     matthew  8853:         $function='admin';
                   8854:     }
1.826     bisitz   8855:     if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.1025    raeburn  8856:         ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
1.182     matthew  8857:         $function='author';
                   8858:     }
                   8859:     return $function;
1.54      www      8860: }
1.99      www      8861: 
                   8862: ###############################################
                   8863: 
1.233     raeburn  8864: =pod
                   8865: 
1.821     raeburn  8866: =item * &show_course()
                   8867: 
                   8868: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
                   8869: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
                   8870: 
                   8871: Inputs:
                   8872: None
                   8873: 
                   8874: Outputs:
                   8875: Scalar: 1 if 'Course' to be used, 0 otherwise.
                   8876: 
                   8877: =cut
                   8878: 
                   8879: ###############################################
                   8880: sub show_course {
                   8881:     my $course = !$env{'user.adv'};
                   8882:     if (!$env{'user.adv'}) {
                   8883:         foreach my $env (keys(%env)) {
                   8884:             next if ($env !~ m/^user\.priv\./);
                   8885:             if ($env !~ m/^user\.priv\.(?:st|cm)/) {
                   8886:                 $course = 0;
                   8887:                 last;
                   8888:             }
                   8889:         }
                   8890:     }
                   8891:     return $course;
                   8892: }
                   8893: 
                   8894: ###############################################
                   8895: 
                   8896: =pod
                   8897: 
1.542     raeburn  8898: =item * &check_user_status()
1.274     raeburn  8899: 
                   8900: Determines current status of supplied role for a
                   8901: specific user. Roles can be active, previous or future.
                   8902: 
                   8903: Inputs: 
                   8904: user's domain, user's username, course's domain,
1.375     raeburn  8905: course's number, optional section ID.
1.274     raeburn  8906: 
                   8907: Outputs:
                   8908: role status: active, previous or future. 
                   8909: 
                   8910: =cut
                   8911: 
                   8912: sub check_user_status {
1.412     raeburn  8913:     my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.1073    raeburn  8914:     my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
1.1202    raeburn  8915:     my @uroles = keys(%userinfo);
1.274     raeburn  8916:     my $srchstr;
                   8917:     my $active_chk = 'none';
1.412     raeburn  8918:     my $now = time;
1.274     raeburn  8919:     if (@uroles > 0) {
1.908     raeburn  8920:         if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
1.274     raeburn  8921:             $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
                   8922:         } else {
1.412     raeburn  8923:             $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
                   8924:         }
                   8925:         if (grep/^\Q$srchstr\E$/,@uroles) {
1.274     raeburn  8926:             my $role_end = 0;
                   8927:             my $role_start = 0;
                   8928:             $active_chk = 'active';
1.412     raeburn  8929:             if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
                   8930:                 $role_end = $1;
                   8931:                 if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
                   8932:                     $role_start = $1;
1.274     raeburn  8933:                 }
                   8934:             }
                   8935:             if ($role_start > 0) {
1.412     raeburn  8936:                 if ($now < $role_start) {
1.274     raeburn  8937:                     $active_chk = 'future';
                   8938:                 }
                   8939:             }
                   8940:             if ($role_end > 0) {
1.412     raeburn  8941:                 if ($now > $role_end) {
1.274     raeburn  8942:                     $active_chk = 'previous';
                   8943:                 }
                   8944:             }
                   8945:         }
                   8946:     }
                   8947:     return $active_chk;
                   8948: }
                   8949: 
                   8950: ###############################################
                   8951: 
                   8952: =pod
                   8953: 
1.405     albertel 8954: =item * &get_sections()
1.233     raeburn  8955: 
                   8956: Determines all the sections for a course including
                   8957: sections with students and sections containing other roles.
1.419     raeburn  8958: Incoming parameters: 
                   8959: 
                   8960: 1. domain
                   8961: 2. course number 
                   8962: 3. reference to array containing roles for which sections should 
                   8963: be gathered (optional).
                   8964: 4. reference to array containing status types for which sections 
                   8965: should be gathered (optional).
                   8966: 
                   8967: If the third argument is undefined, sections are gathered for any role. 
                   8968: If the fourth argument is undefined, sections are gathered for any status.
                   8969: Permissible values are 'active' or 'future' or 'previous'.
1.233     raeburn  8970:  
1.374     raeburn  8971: Returns section hash (keys are section IDs, values are
                   8972: number of users in each section), subject to the
1.419     raeburn  8973: optional roles filter, optional status filter 
1.233     raeburn  8974: 
                   8975: =cut
                   8976: 
                   8977: ###############################################
                   8978: sub get_sections {
1.419     raeburn  8979:     my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366     albertel 8980:     if (!defined($cdom) || !defined($cnum)) {
                   8981:         my $cid =  $env{'request.course.id'};
                   8982: 
                   8983: 	return if (!defined($cid));
                   8984: 
                   8985:         $cdom = $env{'course.'.$cid.'.domain'};
                   8986:         $cnum = $env{'course.'.$cid.'.num'};
                   8987:     }
                   8988: 
                   8989:     my %sectioncount;
1.419     raeburn  8990:     my $now = time;
1.240     albertel 8991: 
1.1118    raeburn  8992:     my $check_students = 1;
                   8993:     my $only_students = 0;
                   8994:     if (ref($possible_roles) eq 'ARRAY') {
                   8995:         if (grep(/^st$/,@{$possible_roles})) {
                   8996:             if (@{$possible_roles} == 1) {
                   8997:                 $only_students = 1;
                   8998:             }
                   8999:         } else {
                   9000:             $check_students = 0;
                   9001:         }
                   9002:     }
                   9003: 
                   9004:     if ($check_students) { 
1.276     albertel 9005: 	my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240     albertel 9006: 	my $sec_index = &Apache::loncoursedata::CL_SECTION();
                   9007: 	my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419     raeburn  9008:         my $start_index = &Apache::loncoursedata::CL_START();
                   9009:         my $end_index = &Apache::loncoursedata::CL_END();
                   9010:         my $status;
1.366     albertel 9011: 	while (my ($student,$data) = each(%$classlist)) {
1.419     raeburn  9012: 	    my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
                   9013: 				                     $data->[$status_index],
                   9014:                                                      $data->[$start_index],
                   9015:                                                      $data->[$end_index]);
                   9016:             if ($stu_status eq 'Active') {
                   9017:                 $status = 'active';
                   9018:             } elsif ($end < $now) {
                   9019:                 $status = 'previous';
                   9020:             } elsif ($start > $now) {
                   9021:                 $status = 'future';
                   9022:             } 
                   9023: 	    if ($section ne '-1' && $section !~ /^\s*$/) {
                   9024:                 if ((!defined($possible_status)) || (($status ne '') && 
                   9025:                     (grep/^\Q$status\E$/,@{$possible_status}))) { 
                   9026: 		    $sectioncount{$section}++;
                   9027:                 }
1.240     albertel 9028: 	    }
                   9029: 	}
                   9030:     }
1.1118    raeburn  9031:     if ($only_students) {
                   9032:         return %sectioncount;
                   9033:     }
1.240     albertel 9034:     my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   9035:     foreach my $user (sort(keys(%courseroles))) {
                   9036: 	if ($user !~ /^(\w{2})/) { next; }
                   9037: 	my ($role) = ($user =~ /^(\w{2})/);
                   9038: 	if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419     raeburn  9039: 	my ($section,$status);
1.240     albertel 9040: 	if ($role eq 'cr' &&
                   9041: 	    $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
                   9042: 	    $section=$1;
                   9043: 	}
                   9044: 	if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
                   9045: 	if (!defined($section) || $section eq '-1') { next; }
1.419     raeburn  9046:         my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
                   9047:         if ($end == -1 && $start == -1) {
                   9048:             next; #deleted role
                   9049:         }
                   9050:         if (!defined($possible_status)) { 
                   9051:             $sectioncount{$section}++;
                   9052:         } else {
                   9053:             if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
                   9054:                 $status = 'active';
                   9055:             } elsif ($end < $now) {
                   9056:                 $status = 'future';
                   9057:             } elsif ($start > $now) {
                   9058:                 $status = 'previous';
                   9059:             }
                   9060:             if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
                   9061:                 $sectioncount{$section}++;
                   9062:             }
                   9063:         }
1.233     raeburn  9064:     }
1.366     albertel 9065:     return %sectioncount;
1.233     raeburn  9066: }
                   9067: 
1.274     raeburn  9068: ###############################################
1.294     raeburn  9069: 
                   9070: =pod
1.405     albertel 9071: 
                   9072: =item * &get_course_users()
                   9073: 
1.275     raeburn  9074: Retrieves usernames:domains for users in the specified course
                   9075: with specific role(s), and access status. 
                   9076: 
                   9077: Incoming parameters:
1.277     albertel 9078: 1. course domain
                   9079: 2. course number
                   9080: 3. access status: users must have - either active, 
1.275     raeburn  9081: previous, future, or all.
1.277     albertel 9082: 4. reference to array of permissible roles
1.288     raeburn  9083: 5. reference to array of section restrictions (optional)
                   9084: 6. reference to results object (hash of hashes).
                   9085: 7. reference to optional userdata hash
1.609     raeburn  9086: 8. reference to optional statushash
1.630     raeburn  9087: 9. flag if privileged users (except those set to unhide in
                   9088:    course settings) should be excluded    
1.609     raeburn  9089: Keys of top level results hash are roles.
1.275     raeburn  9090: Keys of inner hashes are username:domain, with 
                   9091: values set to access type.
1.288     raeburn  9092: Optional userdata hash returns an array with arguments in the 
                   9093: same order as loncoursedata::get_classlist() for student data.
                   9094: 
1.609     raeburn  9095: Optional statushash returns
                   9096: 
1.288     raeburn  9097: Entries for end, start, section and status are blank because
                   9098: of the possibility of multiple values for non-student roles.
                   9099: 
1.275     raeburn  9100: =cut
1.405     albertel 9101: 
1.275     raeburn  9102: ###############################################
1.405     albertel 9103: 
1.275     raeburn  9104: sub get_course_users {
1.630     raeburn  9105:     my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288     raeburn  9106:     my %idx = ();
1.419     raeburn  9107:     my %seclists;
1.288     raeburn  9108: 
                   9109:     $idx{udom} = &Apache::loncoursedata::CL_SDOM();
                   9110:     $idx{uname} =  &Apache::loncoursedata::CL_SNAME();
                   9111:     $idx{end} = &Apache::loncoursedata::CL_END();
                   9112:     $idx{start} = &Apache::loncoursedata::CL_START();
                   9113:     $idx{id} = &Apache::loncoursedata::CL_ID();
                   9114:     $idx{section} = &Apache::loncoursedata::CL_SECTION();
                   9115:     $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
                   9116:     $idx{status} = &Apache::loncoursedata::CL_STATUS();
                   9117: 
1.290     albertel 9118:     if (grep(/^st$/,@{$roles})) {
1.276     albertel 9119:         my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278     raeburn  9120:         my $now = time;
1.277     albertel 9121:         foreach my $student (keys(%{$classlist})) {
1.288     raeburn  9122:             my $match = 0;
1.412     raeburn  9123:             my $secmatch = 0;
1.419     raeburn  9124:             my $section = $$classlist{$student}[$idx{section}];
1.609     raeburn  9125:             my $status = $$classlist{$student}[$idx{status}];
1.419     raeburn  9126:             if ($section eq '') {
                   9127:                 $section = 'none';
                   9128:             }
1.291     albertel 9129:             if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 9130:                 if (grep(/^all$/,@{$sections})) {
1.412     raeburn  9131:                     $secmatch = 1;
                   9132:                 } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420     albertel 9133:                     if (grep(/^none$/,@{$sections})) {
1.412     raeburn  9134:                         $secmatch = 1;
                   9135:                     }
                   9136:                 } else {  
1.419     raeburn  9137: 		    if (grep(/^\Q$section\E$/,@{$sections})) {
1.412     raeburn  9138: 		        $secmatch = 1;
                   9139:                     }
1.290     albertel 9140: 		}
1.412     raeburn  9141:                 if (!$secmatch) {
                   9142:                     next;
                   9143:                 }
1.419     raeburn  9144:             }
1.275     raeburn  9145:             if (defined($$types{'active'})) {
1.288     raeburn  9146:                 if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275     raeburn  9147:                     push(@{$$users{st}{$student}},'active');
1.288     raeburn  9148:                     $match = 1;
1.275     raeburn  9149:                 }
                   9150:             }
                   9151:             if (defined($$types{'previous'})) {
1.609     raeburn  9152:                 if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275     raeburn  9153:                     push(@{$$users{st}{$student}},'previous');
1.288     raeburn  9154:                     $match = 1;
1.275     raeburn  9155:                 }
                   9156:             }
                   9157:             if (defined($$types{'future'})) {
1.609     raeburn  9158:                 if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275     raeburn  9159:                     push(@{$$users{st}{$student}},'future');
1.288     raeburn  9160:                     $match = 1;
1.275     raeburn  9161:                 }
                   9162:             }
1.609     raeburn  9163:             if ($match) {
                   9164:                 push(@{$seclists{$student}},$section);
                   9165:                 if (ref($userdata) eq 'HASH') {
                   9166:                     $$userdata{$student} = $$classlist{$student};
                   9167:                 }
                   9168:                 if (ref($statushash) eq 'HASH') {
                   9169:                     $statushash->{$student}{'st'}{$section} = $status;
                   9170:                 }
1.288     raeburn  9171:             }
1.275     raeburn  9172:         }
                   9173:     }
1.412     raeburn  9174:     if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439     raeburn  9175:         my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   9176:         my $now = time;
1.609     raeburn  9177:         my %displaystatus = ( previous => 'Expired',
                   9178:                               active   => 'Active',
                   9179:                               future   => 'Future',
                   9180:                             );
1.1121    raeburn  9181:         my (%nothide,@possdoms);
1.630     raeburn  9182:         if ($hidepriv) {
                   9183:             my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
                   9184:             foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
                   9185:                 if ($user !~ /:/) {
                   9186:                     $nothide{join(':',split(/[\@]/,$user))}=1;
                   9187:                 } else {
                   9188:                     $nothide{$user} = 1;
                   9189:                 }
                   9190:             }
1.1121    raeburn  9191:             my @possdoms = ($cdom);
                   9192:             if ($coursehash{'checkforpriv'}) {
                   9193:                 push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
                   9194:             }
1.630     raeburn  9195:         }
1.439     raeburn  9196:         foreach my $person (sort(keys(%coursepersonnel))) {
1.288     raeburn  9197:             my $match = 0;
1.412     raeburn  9198:             my $secmatch = 0;
1.439     raeburn  9199:             my $status;
1.412     raeburn  9200:             my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275     raeburn  9201:             $user =~ s/:$//;
1.439     raeburn  9202:             my ($end,$start) = split(/:/,$coursepersonnel{$person});
                   9203:             if ($end == -1 || $start == -1) {
                   9204:                 next;
                   9205:             }
                   9206:             if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
                   9207:                 (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412     raeburn  9208:                 my ($uname,$udom) = split(/:/,$user);
                   9209:                 if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 9210:                     if (grep(/^all$/,@{$sections})) {
1.412     raeburn  9211:                         $secmatch = 1;
                   9212:                     } elsif ($usec eq '') {
1.420     albertel 9213:                         if (grep(/^none$/,@{$sections})) {
1.412     raeburn  9214:                             $secmatch = 1;
                   9215:                         }
                   9216:                     } else {
                   9217:                         if (grep(/^\Q$usec\E$/,@{$sections})) {
                   9218:                             $secmatch = 1;
                   9219:                         }
                   9220:                     }
                   9221:                     if (!$secmatch) {
                   9222:                         next;
                   9223:                     }
1.288     raeburn  9224:                 }
1.419     raeburn  9225:                 if ($usec eq '') {
                   9226:                     $usec = 'none';
                   9227:                 }
1.275     raeburn  9228:                 if ($uname ne '' && $udom ne '') {
1.630     raeburn  9229:                     if ($hidepriv) {
1.1121    raeburn  9230:                         if ((&Apache::lonnet::privileged($uname,$udom,\@possdoms)) &&
1.630     raeburn  9231:                             (!$nothide{$uname.':'.$udom})) {
                   9232:                             next;
                   9233:                         }
                   9234:                     }
1.503     raeburn  9235:                     if ($end > 0 && $end < $now) {
1.439     raeburn  9236:                         $status = 'previous';
                   9237:                     } elsif ($start > $now) {
                   9238:                         $status = 'future';
                   9239:                     } else {
                   9240:                         $status = 'active';
                   9241:                     }
1.277     albertel 9242:                     foreach my $type (keys(%{$types})) { 
1.275     raeburn  9243:                         if ($status eq $type) {
1.420     albertel 9244:                             if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419     raeburn  9245:                                 push(@{$$users{$role}{$user}},$type);
                   9246:                             }
1.288     raeburn  9247:                             $match = 1;
                   9248:                         }
                   9249:                     }
1.419     raeburn  9250:                     if (($match) && (ref($userdata) eq 'HASH')) {
                   9251:                         if (!exists($$userdata{$uname.':'.$udom})) {
                   9252: 			    &get_user_info($udom,$uname,\%idx,$userdata);
                   9253:                         }
1.420     albertel 9254:                         if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419     raeburn  9255:                             push(@{$seclists{$uname.':'.$udom}},$usec);
                   9256:                         }
1.609     raeburn  9257:                         if (ref($statushash) eq 'HASH') {
                   9258:                             $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
                   9259:                         }
1.275     raeburn  9260:                     }
                   9261:                 }
                   9262:             }
                   9263:         }
1.290     albertel 9264:         if (grep(/^ow$/,@{$roles})) {
1.279     raeburn  9265:             if ((defined($cdom)) && (defined($cnum))) {
                   9266:                 my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
                   9267:                 if ( defined($csettings{'internal.courseowner'}) ) {
                   9268:                     my $owner = $csettings{'internal.courseowner'};
1.609     raeburn  9269:                     next if ($owner eq '');
                   9270:                     my ($ownername,$ownerdom);
                   9271:                     if ($owner =~ /^([^:]+):([^:]+)$/) {
                   9272:                         $ownername = $1;
                   9273:                         $ownerdom = $2;
                   9274:                     } else {
                   9275:                         $ownername = $owner;
                   9276:                         $ownerdom = $cdom;
                   9277:                         $owner = $ownername.':'.$ownerdom;
1.439     raeburn  9278:                     }
                   9279:                     @{$$users{'ow'}{$owner}} = 'any';
1.290     albertel 9280:                     if (defined($userdata) && 
1.609     raeburn  9281: 			!exists($$userdata{$owner})) {
                   9282: 			&get_user_info($ownerdom,$ownername,\%idx,$userdata);
                   9283:                         if (!grep(/^none$/,@{$seclists{$owner}})) {
                   9284:                             push(@{$seclists{$owner}},'none');
                   9285:                         }
                   9286:                         if (ref($statushash) eq 'HASH') {
                   9287:                             $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419     raeburn  9288:                         }
1.290     albertel 9289: 		    }
1.279     raeburn  9290:                 }
                   9291:             }
                   9292:         }
1.419     raeburn  9293:         foreach my $user (keys(%seclists)) {
                   9294:             @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
                   9295:             $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
                   9296:         }
1.275     raeburn  9297:     }
                   9298:     return;
                   9299: }
                   9300: 
1.288     raeburn  9301: sub get_user_info {
                   9302:     my ($udom,$uname,$idx,$userdata) = @_;
1.289     albertel 9303:     $$userdata{$uname.':'.$udom}[$$idx{fullname}] = 
                   9304: 	&plainname($uname,$udom,'lastname');
1.291     albertel 9305:     $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297     raeburn  9306:     $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609     raeburn  9307:     my %idhash =  &Apache::lonnet::idrget($udom,($uname));
                   9308:     $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname}; 
1.288     raeburn  9309:     return;
                   9310: }
1.275     raeburn  9311: 
1.472     raeburn  9312: ###############################################
                   9313: 
                   9314: =pod
                   9315: 
                   9316: =item * &get_user_quota()
                   9317: 
1.1134    raeburn  9318: Retrieves quota assigned for storage of user files.
                   9319: Default is to report quota for portfolio files.
1.472     raeburn  9320: 
                   9321: Incoming parameters:
                   9322: 1. user's username
                   9323: 2. user's domain
1.1134    raeburn  9324: 3. quota name - portfolio, author, or course
1.1136    raeburn  9325:    (if no quota name provided, defaults to portfolio).
1.1237  ! raeburn  9326: 4. crstype - official, unofficial, textbook, placement or community, 
        !          9327:    if quota name is course
1.472     raeburn  9328: 
                   9329: Returns:
1.1163    raeburn  9330: 1. Disk quota (in MB) assigned to student.
1.536     raeburn  9331: 2. (Optional) Type of setting: custom or default
                   9332:    (individually assigned or default for user's 
                   9333:    institutional status).
                   9334: 3. (Optional) - User's institutional status (e.g., faculty, staff
                   9335:    or student - types as defined in localenroll::inst_usertypes 
                   9336:    for user's domain, which determines default quota for user.
                   9337: 4. (Optional) - Default quota which would apply to the user.
1.472     raeburn  9338: 
                   9339: If a value has been stored in the user's environment, 
1.536     raeburn  9340: it will return that, otherwise it returns the maximal default
1.1134    raeburn  9341: defined for the user's institutional status(es) in the domain.
1.472     raeburn  9342: 
                   9343: =cut
                   9344: 
                   9345: ###############################################
                   9346: 
                   9347: 
                   9348: sub get_user_quota {
1.1136    raeburn  9349:     my ($uname,$udom,$quotaname,$crstype) = @_;
1.536     raeburn  9350:     my ($quota,$quotatype,$settingstatus,$defquota);
1.472     raeburn  9351:     if (!defined($udom)) {
                   9352:         $udom = $env{'user.domain'};
                   9353:     }
                   9354:     if (!defined($uname)) {
                   9355:         $uname = $env{'user.name'};
                   9356:     }
                   9357:     if (($udom eq '' || $uname eq '') ||
                   9358:         ($udom eq 'public') && ($uname eq 'public')) {
                   9359:         $quota = 0;
1.536     raeburn  9360:         $quotatype = 'default';
                   9361:         $defquota = 0; 
1.472     raeburn  9362:     } else {
1.536     raeburn  9363:         my $inststatus;
1.1134    raeburn  9364:         if ($quotaname eq 'course') {
                   9365:             if (($env{'course.'.$udom.'_'.$uname.'.num'} eq $uname) &&
                   9366:                 ($env{'course.'.$udom.'_'.$uname.'.domain'} eq $udom)) {
                   9367:                 $quota = $env{'course.'.$udom.'_'.$uname.'.internal.uploadquota'};
                   9368:             } else {
                   9369:                 my %cenv = &Apache::lonnet::coursedescription("$udom/$uname");
                   9370:                 $quota = $cenv{'internal.uploadquota'};
                   9371:             }
1.536     raeburn  9372:         } else {
1.1134    raeburn  9373:             if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
                   9374:                 if ($quotaname eq 'author') {
                   9375:                     $quota = $env{'environment.authorquota'};
                   9376:                 } else {
                   9377:                     $quota = $env{'environment.portfolioquota'};
                   9378:                 }
                   9379:                 $inststatus = $env{'environment.inststatus'};
                   9380:             } else {
                   9381:                 my %userenv = 
                   9382:                     &Apache::lonnet::get('environment',['portfolioquota',
                   9383:                                          'authorquota','inststatus'],$udom,$uname);
                   9384:                 my ($tmp) = keys(%userenv);
                   9385:                 if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   9386:                     if ($quotaname eq 'author') {
                   9387:                         $quota = $userenv{'authorquota'};
                   9388:                     } else {
                   9389:                         $quota = $userenv{'portfolioquota'};
                   9390:                     }
                   9391:                     $inststatus = $userenv{'inststatus'};
                   9392:                 } else {
                   9393:                     undef(%userenv);
                   9394:                 }
                   9395:             }
                   9396:         }
                   9397:         if ($quota eq '' || wantarray) {
                   9398:             if ($quotaname eq 'course') {
                   9399:                 my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
1.1165    raeburn  9400:                 if (($crstype eq 'official') || ($crstype eq 'unofficial') || 
1.1237  ! raeburn  9401:                     ($crstype eq 'community') || ($crstype eq 'textbook') ||
        !          9402:                     ($crstype eq 'placement')) { 
1.1136    raeburn  9403:                     $defquota = $domdefs{$crstype.'quota'};
                   9404:                 }
                   9405:                 if ($defquota eq '') {
                   9406:                     $defquota = 500;
                   9407:                 }
1.1134    raeburn  9408:             } else {
                   9409:                 ($defquota,$settingstatus) = &default_quota($udom,$inststatus,$quotaname);
                   9410:             }
                   9411:             if ($quota eq '') {
                   9412:                 $quota = $defquota;
                   9413:                 $quotatype = 'default';
                   9414:             } else {
                   9415:                 $quotatype = 'custom';
                   9416:             }
1.472     raeburn  9417:         }
                   9418:     }
1.536     raeburn  9419:     if (wantarray) {
                   9420:         return ($quota,$quotatype,$settingstatus,$defquota);
                   9421:     } else {
                   9422:         return $quota;
                   9423:     }
1.472     raeburn  9424: }
                   9425: 
                   9426: ###############################################
                   9427: 
                   9428: =pod
                   9429: 
                   9430: =item * &default_quota()
                   9431: 
1.536     raeburn  9432: Retrieves default quota assigned for storage of user portfolio files,
                   9433: given an (optional) user's institutional status.
1.472     raeburn  9434: 
                   9435: Incoming parameters:
1.1142    raeburn  9436: 
1.472     raeburn  9437: 1. domain
1.536     raeburn  9438: 2. (Optional) institutional status(es).  This is a : separated list of 
                   9439:    status types (e.g., faculty, staff, student etc.)
                   9440:    which apply to the user for whom the default is being retrieved.
                   9441:    If the institutional status string in undefined, the domain
1.1134    raeburn  9442:    default quota will be returned.
                   9443: 3.  quota name - portfolio, author, or course
                   9444:    (if no quota name provided, defaults to portfolio).
1.472     raeburn  9445: 
                   9446: Returns:
1.1142    raeburn  9447: 
1.1163    raeburn  9448: 1. Default disk quota (in MB) for user portfolios in the domain.
1.536     raeburn  9449: 2. (Optional) institutional type which determined the value of the
                   9450:    default quota.
1.472     raeburn  9451: 
                   9452: If a value has been stored in the domain's configuration db,
                   9453: it will return that, otherwise it returns 20 (for backwards 
                   9454: compatibility with domains which have not set up a configuration
1.1163    raeburn  9455: db file; the original statically defined portfolio quota was 20 MB). 
1.472     raeburn  9456: 
1.536     raeburn  9457: If the user's status includes multiple types (e.g., staff and student),
                   9458: the largest default quota which applies to the user determines the
                   9459: default quota returned.
                   9460: 
1.472     raeburn  9461: =cut
                   9462: 
                   9463: ###############################################
                   9464: 
                   9465: 
                   9466: sub default_quota {
1.1134    raeburn  9467:     my ($udom,$inststatus,$quotaname) = @_;
1.536     raeburn  9468:     my ($defquota,$settingstatus);
                   9469:     my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622     raeburn  9470:                                             ['quotas'],$udom);
1.1134    raeburn  9471:     my $key = 'defaultquota';
                   9472:     if ($quotaname eq 'author') {
                   9473:         $key = 'authorquota';
                   9474:     }
1.622     raeburn  9475:     if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536     raeburn  9476:         if ($inststatus ne '') {
1.765     raeburn  9477:             my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536     raeburn  9478:             foreach my $item (@statuses) {
1.1134    raeburn  9479:                 if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
                   9480:                     if ($quotahash{'quotas'}{$key}{$item} ne '') {
1.711     raeburn  9481:                         if ($defquota eq '') {
1.1134    raeburn  9482:                             $defquota = $quotahash{'quotas'}{$key}{$item};
1.711     raeburn  9483:                             $settingstatus = $item;
1.1134    raeburn  9484:                         } elsif ($quotahash{'quotas'}{$key}{$item} > $defquota) {
                   9485:                             $defquota = $quotahash{'quotas'}{$key}{$item};
1.711     raeburn  9486:                             $settingstatus = $item;
                   9487:                         }
                   9488:                     }
1.1134    raeburn  9489:                 } elsif ($key eq 'defaultquota') {
1.711     raeburn  9490:                     if ($quotahash{'quotas'}{$item} ne '') {
                   9491:                         if ($defquota eq '') {
                   9492:                             $defquota = $quotahash{'quotas'}{$item};
                   9493:                             $settingstatus = $item;
                   9494:                         } elsif ($quotahash{'quotas'}{$item} > $defquota) {
                   9495:                             $defquota = $quotahash{'quotas'}{$item};
                   9496:                             $settingstatus = $item;
                   9497:                         }
1.536     raeburn  9498:                     }
                   9499:                 }
                   9500:             }
                   9501:         }
                   9502:         if ($defquota eq '') {
1.1134    raeburn  9503:             if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
                   9504:                 $defquota = $quotahash{'quotas'}{$key}{'default'};
                   9505:             } elsif ($key eq 'defaultquota') {
1.711     raeburn  9506:                 $defquota = $quotahash{'quotas'}{'default'};
                   9507:             }
1.536     raeburn  9508:             $settingstatus = 'default';
1.1139    raeburn  9509:             if ($defquota eq '') {
                   9510:                 if ($quotaname eq 'author') {
                   9511:                     $defquota = 500;
                   9512:                 }
                   9513:             }
1.536     raeburn  9514:         }
                   9515:     } else {
                   9516:         $settingstatus = 'default';
1.1134    raeburn  9517:         if ($quotaname eq 'author') {
                   9518:             $defquota = 500;
                   9519:         } else {
                   9520:             $defquota = 20;
                   9521:         }
1.536     raeburn  9522:     }
                   9523:     if (wantarray) {
                   9524:         return ($defquota,$settingstatus);
1.472     raeburn  9525:     } else {
1.536     raeburn  9526:         return $defquota;
1.472     raeburn  9527:     }
                   9528: }
                   9529: 
1.1135    raeburn  9530: ###############################################
                   9531: 
                   9532: =pod
                   9533: 
1.1136    raeburn  9534: =item * &excess_filesize_warning()
1.1135    raeburn  9535: 
                   9536: Returns warning message if upload of file to authoring space, or copying
1.1136    raeburn  9537: of existing file within authoring space will cause quota for the authoring
1.1146    raeburn  9538: space to be exceeded.
1.1136    raeburn  9539: 
                   9540: Same, if upload of a file directly to a course/community via Course Editor
1.1137    raeburn  9541: will cause quota for uploaded content for the course to be exceeded.
1.1135    raeburn  9542: 
1.1165    raeburn  9543: Inputs: 7 
1.1136    raeburn  9544: 1. username or coursenum
1.1135    raeburn  9545: 2. domain
1.1136    raeburn  9546: 3. context ('author' or 'course')
1.1135    raeburn  9547: 4. filename of file for which action is being requested
                   9548: 5. filesize (kB) of file
                   9549: 6. action being taken: copy or upload.
1.1237  ! raeburn  9550: 7. quotatype (in course context -- official, unofficial, textbook, placement or community).
1.1135    raeburn  9551: 
                   9552: Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
1.1142    raeburn  9553:          otherwise return null.
                   9554: 
                   9555: =back
1.1135    raeburn  9556: 
                   9557: =cut
                   9558: 
1.1136    raeburn  9559: sub excess_filesize_warning {
1.1165    raeburn  9560:     my ($uname,$udom,$context,$filename,$filesize,$action,$quotatype) = @_;
1.1136    raeburn  9561:     my $current_disk_usage = 0;
1.1165    raeburn  9562:     my $disk_quota = &get_user_quota($uname,$udom,$context,$quotatype); #expressed in MB
1.1136    raeburn  9563:     if ($context eq 'author') {
                   9564:         my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname";
                   9565:         $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace);
                   9566:     } else {
                   9567:         foreach my $subdir ('docs','supplemental') {
                   9568:             $current_disk_usage += &Apache::lonnet::diskusage($udom,$uname,"userfiles/$subdir",1);
                   9569:         }
                   9570:     }
1.1135    raeburn  9571:     $disk_quota = int($disk_quota * 1000);
                   9572:     if (($current_disk_usage + $filesize) > $disk_quota) {
1.1179    bisitz   9573:         return '<p class="LC_warning">'.
1.1135    raeburn  9574:                 &mt("Unable to $action [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.",
1.1179    bisitz   9575:                     '<span class="LC_filename">'.$filename.'</span>',$filesize).'</p>'.
                   9576:                '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
1.1135    raeburn  9577:                             $disk_quota,$current_disk_usage).
                   9578:                '</p>';
                   9579:     }
                   9580:     return;
                   9581: }
                   9582: 
                   9583: ###############################################
                   9584: 
                   9585: 
1.1136    raeburn  9586: 
                   9587: 
1.384     raeburn  9588: sub get_secgrprole_info {
                   9589:     my ($cdom,$cnum,$needroles,$type)  = @_;
                   9590:     my %sections_count = &get_sections($cdom,$cnum);
                   9591:     my @sections =  (sort {$a <=> $b} keys(%sections_count));
                   9592:     my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
                   9593:     my @groups = sort(keys(%curr_groups));
                   9594:     my $allroles = [];
                   9595:     my $rolehash;
                   9596:     my $accesshash = {
                   9597:                      active => 'Currently has access',
                   9598:                      future => 'Will have future access',
                   9599:                      previous => 'Previously had access',
                   9600:                   };
                   9601:     if ($needroles) {
                   9602:         $rolehash = {'all' => 'all'};
1.385     albertel 9603:         my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   9604: 	if (&Apache::lonnet::error(%user_roles)) {
                   9605: 	    undef(%user_roles);
                   9606: 	}
                   9607:         foreach my $item (keys(%user_roles)) {
1.384     raeburn  9608:             my ($role)=split(/\:/,$item,2);
                   9609:             if ($role eq 'cr') { next; }
                   9610:             if ($role =~ /^cr/) {
                   9611:                 $$rolehash{$role} = (split('/',$role))[3];
                   9612:             } else {
                   9613:                 $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
                   9614:             }
                   9615:         }
                   9616:         foreach my $key (sort(keys(%{$rolehash}))) {
                   9617:             push(@{$allroles},$key);
                   9618:         }
                   9619:         push (@{$allroles},'st');
                   9620:         $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
                   9621:     }
                   9622:     return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
                   9623: }
                   9624: 
1.555     raeburn  9625: sub user_picker {
1.994     raeburn  9626:     my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context) = @_;
1.555     raeburn  9627:     my $currdom = $dom;
                   9628:     my %curr_selected = (
                   9629:                         srchin => 'dom',
1.580     raeburn  9630:                         srchby => 'lastname',
1.555     raeburn  9631:                       );
                   9632:     my $srchterm;
1.625     raeburn  9633:     if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555     raeburn  9634:         if ($srch->{'srchby'} ne '') {
                   9635:             $curr_selected{'srchby'} = $srch->{'srchby'};
                   9636:         }
                   9637:         if ($srch->{'srchin'} ne '') {
                   9638:             $curr_selected{'srchin'} = $srch->{'srchin'};
                   9639:         }
                   9640:         if ($srch->{'srchtype'} ne '') {
                   9641:             $curr_selected{'srchtype'} = $srch->{'srchtype'};
                   9642:         }
                   9643:         if ($srch->{'srchdomain'} ne '') {
                   9644:             $currdom = $srch->{'srchdomain'};
                   9645:         }
                   9646:         $srchterm = $srch->{'srchterm'};
                   9647:     }
1.1222    damieng  9648:     my %html_lt=&Apache::lonlocal::texthash(
1.573     raeburn  9649:                     'usr'       => 'Search criteria',
1.563     raeburn  9650:                     'doma'      => 'Domain/institution to search',
1.558     albertel 9651:                     'uname'     => 'username',
                   9652:                     'lastname'  => 'last name',
1.555     raeburn  9653:                     'lastfirst' => 'last name, first name',
1.558     albertel 9654:                     'crs'       => 'in this course',
1.576     raeburn  9655:                     'dom'       => 'in selected LON-CAPA domain', 
1.558     albertel 9656:                     'alc'       => 'all LON-CAPA',
1.573     raeburn  9657:                     'instd'     => 'in institutional directory for selected domain',
1.558     albertel 9658:                     'exact'     => 'is',
                   9659:                     'contains'  => 'contains',
1.569     raeburn  9660:                     'begins'    => 'begins with',
1.1222    damieng  9661:                                        );
                   9662:     my %js_lt=&Apache::lonlocal::texthash(
1.571     raeburn  9663:                     'youm'      => "You must include some text to search for.",
                   9664:                     'thte'      => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
                   9665:                     'thet'      => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
                   9666:                     'yomc'      => "You must choose a domain when using an institutional directory search.",
                   9667:                     'ymcd'      => "You must choose a domain when using a domain search.",
                   9668:                     'whus'      => "When using searching by last,first you must include a comma as separator between last name and first name.",
                   9669:                     'whse'      => "When searching by last,first you must include at least one character in the first name.",
                   9670:                      'thfo'     => "The following need to be corrected before the search can be run:",
1.555     raeburn  9671:                                        );
1.1222    damieng  9672:     &html_escape(\%html_lt);
                   9673:     &js_escape(\%js_lt);
1.563     raeburn  9674:     my $domform = &select_dom_form($currdom,'srchdomain',1,1);
                   9675:     my $srchinsel = ' <select name="srchin">';
1.555     raeburn  9676: 
                   9677:     my @srchins = ('crs','dom','alc','instd');
                   9678: 
                   9679:     foreach my $option (@srchins) {
                   9680:         # FIXME 'alc' option unavailable until 
                   9681:         #       loncreateuser::print_user_query_page()
                   9682:         #       has been completed.
                   9683:         next if ($option eq 'alc');
1.880     raeburn  9684:         next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));  
1.555     raeburn  9685:         next if ($option eq 'crs' && !$env{'request.course.id'});
1.563     raeburn  9686:         if ($curr_selected{'srchin'} eq $option) {
                   9687:             $srchinsel .= ' 
1.1222    damieng  9688:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.563     raeburn  9689:         } else {
                   9690:             $srchinsel .= '
1.1222    damieng  9691:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.563     raeburn  9692:         }
1.555     raeburn  9693:     }
1.563     raeburn  9694:     $srchinsel .= "\n  </select>\n";
1.555     raeburn  9695: 
                   9696:     my $srchbysel =  ' <select name="srchby">';
1.580     raeburn  9697:     foreach my $option ('lastname','lastfirst','uname') {
1.555     raeburn  9698:         if ($curr_selected{'srchby'} eq $option) {
                   9699:             $srchbysel .= '
1.1222    damieng  9700:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555     raeburn  9701:         } else {
                   9702:             $srchbysel .= '
1.1222    damieng  9703:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555     raeburn  9704:          }
                   9705:     }
                   9706:     $srchbysel .= "\n  </select>\n";
                   9707: 
                   9708:     my $srchtypesel = ' <select name="srchtype">';
1.580     raeburn  9709:     foreach my $option ('begins','contains','exact') {
1.555     raeburn  9710:         if ($curr_selected{'srchtype'} eq $option) {
                   9711:             $srchtypesel .= '
1.1222    damieng  9712:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555     raeburn  9713:         } else {
                   9714:             $srchtypesel .= '
1.1222    damieng  9715:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555     raeburn  9716:         }
                   9717:     }
                   9718:     $srchtypesel .= "\n  </select>\n";
                   9719: 
1.558     albertel 9720:     my ($newuserscript,$new_user_create);
1.994     raeburn  9721:     my $context_dom = $env{'request.role.domain'};
                   9722:     if ($context eq 'requestcrs') {
                   9723:         if ($env{'form.coursedom'} ne '') { 
                   9724:             $context_dom = $env{'form.coursedom'};
                   9725:         }
                   9726:     }
1.556     raeburn  9727:     if ($forcenewuser) {
1.576     raeburn  9728:         if (ref($srch) eq 'HASH') {
1.994     raeburn  9729:             if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
1.627     raeburn  9730:                 if ($cancreate) {
                   9731:                     $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>';
                   9732:                 } else {
1.799     bisitz   9733:                     my $helplink = 'javascript:helpMenu('."'display'".')';
1.627     raeburn  9734:                     my %usertypetext = (
                   9735:                         official   => 'institutional',
                   9736:                         unofficial => 'non-institutional',
                   9737:                     );
1.799     bisitz   9738:                     $new_user_create = '<p class="LC_warning">'
                   9739:                                       .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
                   9740:                                       .' '
                   9741:                                       .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
                   9742:                                           ,'<a href="'.$helplink.'">','</a>')
                   9743:                                       .'</p><br />';
1.627     raeburn  9744:                 }
1.576     raeburn  9745:             }
                   9746:         }
                   9747: 
1.556     raeburn  9748:         $newuserscript = <<"ENDSCRIPT";
                   9749: 
1.570     raeburn  9750: function setSearch(createnew,callingForm) {
1.556     raeburn  9751:     if (createnew == 1) {
1.570     raeburn  9752:         for (var i=0; i<callingForm.srchby.length; i++) {
                   9753:             if (callingForm.srchby.options[i].value == 'uname') {
                   9754:                 callingForm.srchby.selectedIndex = i;
1.556     raeburn  9755:             }
                   9756:         }
1.570     raeburn  9757:         for (var i=0; i<callingForm.srchin.length; i++) {
                   9758:             if ( callingForm.srchin.options[i].value == 'dom') {
                   9759: 		callingForm.srchin.selectedIndex = i;
1.556     raeburn  9760:             }
                   9761:         }
1.570     raeburn  9762:         for (var i=0; i<callingForm.srchtype.length; i++) {
                   9763:             if (callingForm.srchtype.options[i].value == 'exact') {
                   9764:                 callingForm.srchtype.selectedIndex = i;
1.556     raeburn  9765:             }
                   9766:         }
1.570     raeburn  9767:         for (var i=0; i<callingForm.srchdomain.length; i++) {
1.994     raeburn  9768:             if (callingForm.srchdomain.options[i].value == '$context_dom') {
1.570     raeburn  9769:                 callingForm.srchdomain.selectedIndex = i;
1.556     raeburn  9770:             }
                   9771:         }
                   9772:     }
                   9773: }
                   9774: ENDSCRIPT
1.558     albertel 9775: 
1.556     raeburn  9776:     }
                   9777: 
1.555     raeburn  9778:     my $output = <<"END_BLOCK";
1.556     raeburn  9779: <script type="text/javascript">
1.824     bisitz   9780: // <![CDATA[
1.570     raeburn  9781: function validateEntry(callingForm) {
1.558     albertel 9782: 
1.556     raeburn  9783:     var checkok = 1;
1.558     albertel 9784:     var srchin;
1.570     raeburn  9785:     for (var i=0; i<callingForm.srchin.length; i++) {
                   9786: 	if ( callingForm.srchin[i].checked ) {
                   9787: 	    srchin = callingForm.srchin[i].value;
1.558     albertel 9788: 	}
                   9789:     }
                   9790: 
1.570     raeburn  9791:     var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
                   9792:     var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
                   9793:     var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
                   9794:     var srchterm =  callingForm.srchterm.value;
                   9795:     var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556     raeburn  9796:     var msg = "";
                   9797: 
                   9798:     if (srchterm == "") {
                   9799:         checkok = 0;
1.1222    damieng  9800:         msg += "$js_lt{'youm'}\\n";
1.556     raeburn  9801:     }
                   9802: 
1.569     raeburn  9803:     if (srchtype== 'begins') {
                   9804:         if (srchterm.length < 2) {
                   9805:             checkok = 0;
1.1222    damieng  9806:             msg += "$js_lt{'thte'}\\n";
1.569     raeburn  9807:         }
                   9808:     }
                   9809: 
1.556     raeburn  9810:     if (srchtype== 'contains') {
                   9811:         if (srchterm.length < 3) {
                   9812:             checkok = 0;
1.1222    damieng  9813:             msg += "$js_lt{'thet'}\\n";
1.556     raeburn  9814:         }
                   9815:     }
                   9816:     if (srchin == 'instd') {
                   9817:         if (srchdomain == '') {
                   9818:             checkok = 0;
1.1222    damieng  9819:             msg += "$js_lt{'yomc'}\\n";
1.556     raeburn  9820:         }
                   9821:     }
                   9822:     if (srchin == 'dom') {
                   9823:         if (srchdomain == '') {
                   9824:             checkok = 0;
1.1222    damieng  9825:             msg += "$js_lt{'ymcd'}\\n";
1.556     raeburn  9826:         }
                   9827:     }
                   9828:     if (srchby == 'lastfirst') {
                   9829:         if (srchterm.indexOf(",") == -1) {
                   9830:             checkok = 0;
1.1222    damieng  9831:             msg += "$js_lt{'whus'}\\n";
1.556     raeburn  9832:         }
                   9833:         if (srchterm.indexOf(",") == srchterm.length -1) {
                   9834:             checkok = 0;
1.1222    damieng  9835:             msg += "$js_lt{'whse'}\\n";
1.556     raeburn  9836:         }
                   9837:     }
                   9838:     if (checkok == 0) {
1.1222    damieng  9839:         alert("$js_lt{'thfo'}\\n"+msg);
1.556     raeburn  9840:         return;
                   9841:     }
                   9842:     if (checkok == 1) {
1.570     raeburn  9843:         callingForm.submit();
1.556     raeburn  9844:     }
                   9845: }
                   9846: 
                   9847: $newuserscript
                   9848: 
1.824     bisitz   9849: // ]]>
1.556     raeburn  9850: </script>
1.558     albertel 9851: 
                   9852: $new_user_create
                   9853: 
1.555     raeburn  9854: END_BLOCK
1.558     albertel 9855: 
1.876     raeburn  9856:     $output .= &Apache::lonhtmlcommon::start_pick_box().
1.1222    damieng  9857:                &Apache::lonhtmlcommon::row_title($html_lt{'doma'}).
1.876     raeburn  9858:                $domform.
                   9859:                &Apache::lonhtmlcommon::row_closure().
1.1222    damieng  9860:                &Apache::lonhtmlcommon::row_title($html_lt{'usr'}).
1.876     raeburn  9861:                $srchbysel.
                   9862:                $srchtypesel. 
                   9863:                '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
                   9864:                $srchinsel.
                   9865:                &Apache::lonhtmlcommon::row_closure(1). 
                   9866:                &Apache::lonhtmlcommon::end_pick_box().
                   9867:                '<br />';
1.555     raeburn  9868:     return $output;
                   9869: }
                   9870: 
1.612     raeburn  9871: sub user_rule_check {
1.615     raeburn  9872:     my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.1226    raeburn  9873:     my ($response,%inst_response);
1.612     raeburn  9874:     if (ref($usershash) eq 'HASH') {
1.1226    raeburn  9875:         if (keys(%{$usershash}) > 1) {
                   9876:             my (%by_username,%by_id,%userdoms);
                   9877:             my $checkid; 
                   9878:             if (ref($checks) eq 'HASH') {
                   9879:                 if ((!defined($checks->{'username'})) && (defined($checks->{'id'}))) {
                   9880:                     $checkid = 1;
                   9881:                 }
                   9882:             }
                   9883:             foreach my $user (keys(%{$usershash})) {
                   9884:                 my ($uname,$udom) = split(/:/,$user);
                   9885:                 if ($checkid) {
                   9886:                     if (ref($usershash->{$user}) eq 'HASH') {
                   9887:                         if ($usershash->{$user}->{'id'} ne '') {
1.1227    raeburn  9888:                             $by_id{$udom}{$usershash->{$user}->{'id'}} = $uname; 
1.1226    raeburn  9889:                             $userdoms{$udom} = 1;
1.1227    raeburn  9890:                             if (ref($inst_results) eq 'HASH') {
                   9891:                                 $inst_results->{$uname.':'.$udom} = {};
                   9892:                             }
1.1226    raeburn  9893:                         }
                   9894:                     }
                   9895:                 } else {
                   9896:                     $by_username{$udom}{$uname} = 1;
                   9897:                     $userdoms{$udom} = 1;
1.1227    raeburn  9898:                     if (ref($inst_results) eq 'HASH') {
                   9899:                         $inst_results->{$uname.':'.$udom} = {};
                   9900:                     }
1.1226    raeburn  9901:                 }
                   9902:             }
                   9903:             foreach my $udom (keys(%userdoms)) {
                   9904:                 if (!$got_rules->{$udom}) {
                   9905:                     my %domconfig = &Apache::lonnet::get_dom('configuration',
                   9906:                                                              ['usercreation'],$udom);
                   9907:                     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   9908:                         foreach my $item ('username','id') {
                   9909:                             if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
1.1227    raeburn  9910:                                 $$curr_rules{$udom}{$item} =
                   9911:                                     $domconfig{'usercreation'}{$item.'_rule'};
1.1226    raeburn  9912:                             }
                   9913:                         }
                   9914:                     }
                   9915:                     $got_rules->{$udom} = 1;
                   9916:                 }
1.612     raeburn  9917:             }
1.1226    raeburn  9918:             if ($checkid) {
                   9919:                 foreach my $udom (keys(%by_id)) {
                   9920:                     my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_id{$udom},'id');
                   9921:                     if ($outcome eq 'ok') {
1.1227    raeburn  9922:                         foreach my $id (keys(%{$by_id{$udom}})) {
                   9923:                             my $uname = $by_id{$udom}{$id};
                   9924:                             $inst_response{$uname.':'.$udom} = $outcome;
                   9925:                         }
1.1226    raeburn  9926:                         if (ref($results) eq 'HASH') {
                   9927:                             foreach my $uname (keys(%{$results})) {
1.1227    raeburn  9928:                                 if (exists($inst_response{$uname.':'.$udom})) {
                   9929:                                     $inst_response{$uname.':'.$udom} = $outcome;
                   9930:                                     $inst_results->{$uname.':'.$udom} = $results->{$uname};
                   9931:                                 }
1.1226    raeburn  9932:                             }
                   9933:                         }
                   9934:                     }
1.612     raeburn  9935:                 }
1.615     raeburn  9936:             } else {
1.1226    raeburn  9937:                 foreach my $udom (keys(%by_username)) {
                   9938:                     my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_username{$udom});
                   9939:                     if ($outcome eq 'ok') {
1.1227    raeburn  9940:                         foreach my $uname (keys(%{$by_username{$udom}})) {
                   9941:                             $inst_response{$uname.':'.$udom} = $outcome;
                   9942:                         }
1.1226    raeburn  9943:                         if (ref($results) eq 'HASH') {
                   9944:                             foreach my $uname (keys(%{$results})) {
                   9945:                                 $inst_results->{$uname.':'.$udom} = $results->{$uname};
                   9946:                             }
                   9947:                         }
                   9948:                     }
                   9949:                 }
1.612     raeburn  9950:             }
1.1226    raeburn  9951:         } elsif (keys(%{$usershash}) == 1) {
                   9952:             my $user = (keys(%{$usershash}))[0];
                   9953:             my ($uname,$udom) = split(/:/,$user);
                   9954:             if (($udom ne '') && ($uname ne '')) {
                   9955:                 if (ref($usershash->{$user}) eq 'HASH') {
                   9956:                     if (ref($checks) eq 'HASH') {
                   9957:                         if (defined($checks->{'username'})) {
                   9958:                             ($inst_response{$user},%{$inst_results->{$user}}) = 
                   9959:                                 &Apache::lonnet::get_instuser($udom,$uname);
                   9960:                         } elsif (defined($checks->{'id'})) {
                   9961:                             if ($usershash->{$user}->{'id'} ne '') {
                   9962:                                 ($inst_response{$user},%{$inst_results->{$user}}) =
                   9963:                                     &Apache::lonnet::get_instuser($udom,undef,
                   9964:                                                                   $usershash->{$user}->{'id'});
                   9965:                             } else {
                   9966:                                 ($inst_response{$user},%{$inst_results->{$user}}) =
                   9967:                                     &Apache::lonnet::get_instuser($udom,$uname);
                   9968:                             }
1.585     raeburn  9969:                         }
1.1226    raeburn  9970:                     } else {
                   9971:                        ($inst_response{$user},%{$inst_results->{$user}}) =
                   9972:                             &Apache::lonnet::get_instuser($udom,$uname);
                   9973:                        return;
                   9974:                     }
                   9975:                     if (!$got_rules->{$udom}) {
                   9976:                         my %domconfig = &Apache::lonnet::get_dom('configuration',
                   9977:                                                                  ['usercreation'],$udom);
                   9978:                         if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   9979:                             foreach my $item ('username','id') {
                   9980:                                 if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
                   9981:                                    $$curr_rules{$udom}{$item} = 
                   9982:                                        $domconfig{'usercreation'}{$item.'_rule'};
                   9983:                                 }
                   9984:                             }
                   9985:                         }
                   9986:                         $got_rules->{$udom} = 1;
1.585     raeburn  9987:                     }
                   9988:                 }
1.1226    raeburn  9989:             } else {
                   9990:                 return;
                   9991:             }
                   9992:         } else {
                   9993:             return;
                   9994:         }
                   9995:         foreach my $user (keys(%{$usershash})) {
                   9996:             my ($uname,$udom) = split(/:/,$user);
                   9997:             next if (($udom eq '') || ($uname eq ''));
                   9998:             my $id;
1.1227    raeburn  9999:             if (ref($inst_results) eq 'HASH') {
                   10000:                 if (ref($inst_results->{$user}) eq 'HASH') {
                   10001:                     $id = $inst_results->{$user}->{'id'};
                   10002:                 }
                   10003:             }
                   10004:             if ($id eq '') { 
                   10005:                 if (ref($usershash->{$user})) {
                   10006:                     $id = $usershash->{$user}->{'id'};
                   10007:                 }
1.585     raeburn  10008:             }
1.612     raeburn  10009:             foreach my $item (keys(%{$checks})) {
                   10010:                 if (ref($$curr_rules{$udom}) eq 'HASH') {
                   10011:                     if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
                   10012:                         if (@{$$curr_rules{$udom}{$item}} > 0) {
1.1226    raeburn  10013:                             my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,
                   10014:                                                                              $$curr_rules{$udom}{$item});
1.612     raeburn  10015:                             foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
                   10016:                                 if ($rule_check{$rule}) {
                   10017:                                     $$rulematch{$user}{$item} = $rule;
1.1226    raeburn  10018:                                     if ($inst_response{$user} eq 'ok') {
1.615     raeburn  10019:                                         if (ref($inst_results) eq 'HASH') {
                   10020:                                             if (ref($inst_results->{$user}) eq 'HASH') {
                   10021:                                                 if (keys(%{$inst_results->{$user}}) == 0) {
                   10022:                                                     $$alerts{$item}{$udom}{$uname} = 1;
1.1227    raeburn  10023:                                                 } elsif ($item eq 'id') {
                   10024:                                                     if ($inst_results->{$user}->{'id'} eq '') {
                   10025:                                                         $$alerts{$item}{$udom}{$uname} = 1;
                   10026:                                                     }
1.615     raeburn  10027:                                                 }
1.612     raeburn  10028:                                             }
                   10029:                                         }
1.615     raeburn  10030:                                     }
                   10031:                                     last;
1.585     raeburn  10032:                                 }
                   10033:                             }
                   10034:                         }
                   10035:                     }
                   10036:                 }
                   10037:             }
                   10038:         }
                   10039:     }
1.612     raeburn  10040:     return;
                   10041: }
                   10042: 
                   10043: sub user_rule_formats {
                   10044:     my ($domain,$domdesc,$curr_rules,$check) = @_;
                   10045:     my %text = ( 
                   10046:                  'username' => 'Usernames',
                   10047:                  'id'       => 'IDs',
                   10048:                );
                   10049:     my $output;
                   10050:     my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
                   10051:     if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
                   10052:         if (@{$ruleorder} > 0) {
1.1102    raeburn  10053:             $output = '<br />'.
                   10054:                       &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
                   10055:                           '<span class="LC_cusr_emph">','</span>',$domdesc).
                   10056:                       ' <ul>';
1.612     raeburn  10057:             foreach my $rule (@{$ruleorder}) {
                   10058:                 if (ref($curr_rules) eq 'ARRAY') {
                   10059:                     if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
                   10060:                         if (ref($rules->{$rule}) eq 'HASH') {
                   10061:                             $output .= '<li>'.$rules->{$rule}{'name'}.': '.
                   10062:                                         $rules->{$rule}{'desc'}.'</li>';
                   10063:                         }
                   10064:                     }
                   10065:                 }
                   10066:             }
                   10067:             $output .= '</ul>';
                   10068:         }
                   10069:     }
                   10070:     return $output;
                   10071: }
                   10072: 
                   10073: sub instrule_disallow_msg {
1.615     raeburn  10074:     my ($checkitem,$domdesc,$count,$mode) = @_;
1.612     raeburn  10075:     my $response;
                   10076:     my %text = (
                   10077:                   item   => 'username',
                   10078:                   items  => 'usernames',
                   10079:                   match  => 'matches',
                   10080:                   do     => 'does',
                   10081:                   action => 'a username',
                   10082:                   one    => 'one',
                   10083:                );
                   10084:     if ($count > 1) {
                   10085:         $text{'item'} = 'usernames';
                   10086:         $text{'match'} ='match';
                   10087:         $text{'do'} = 'do';
                   10088:         $text{'action'} = 'usernames',
                   10089:         $text{'one'} = 'ones';
                   10090:     }
                   10091:     if ($checkitem eq 'id') {
                   10092:         $text{'items'} = 'IDs';
                   10093:         $text{'item'} = 'ID';
                   10094:         $text{'action'} = 'an ID';
1.615     raeburn  10095:         if ($count > 1) {
                   10096:             $text{'item'} = 'IDs';
                   10097:             $text{'action'} = 'IDs';
                   10098:         }
1.612     raeburn  10099:     }
1.674     bisitz   10100:     $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for [_1], but the $text{'item'} $text{'do'} not exist in the institutional directory.",'<span class="LC_cusr_emph">'.$domdesc.'</span>').'<br />';
1.615     raeburn  10101:     if ($mode eq 'upload') {
                   10102:         if ($checkitem eq 'username') {
                   10103:             $response .= &mt("You will need to modify your upload file so it will include $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
                   10104:         } elsif ($checkitem eq 'id') {
1.674     bisitz   10105:             $response .= &mt("Either upload a file which includes $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or when associating fields with data columns, omit an association for the Student/Employee ID field.");
1.615     raeburn  10106:         }
1.669     raeburn  10107:     } elsif ($mode eq 'selfcreate') {
                   10108:         if ($checkitem eq 'id') {
                   10109:             $response .= &mt("You must either choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or leave the ID field blank.");
                   10110:         }
1.615     raeburn  10111:     } else {
                   10112:         if ($checkitem eq 'username') {
                   10113:             $response .= &mt("You must choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
                   10114:         } elsif ($checkitem eq 'id') {
                   10115:             $response .= &mt("You must either choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or leave the ID field blank.");
                   10116:         }
1.612     raeburn  10117:     }
                   10118:     return $response;
1.585     raeburn  10119: }
                   10120: 
1.624     raeburn  10121: sub personal_data_fieldtitles {
                   10122:     my %fieldtitles = &Apache::lonlocal::texthash (
                   10123:                         id => 'Student/Employee ID',
                   10124:                         permanentemail => 'E-mail address',
                   10125:                         lastname => 'Last Name',
                   10126:                         firstname => 'First Name',
                   10127:                         middlename => 'Middle Name',
                   10128:                         generation => 'Generation',
                   10129:                         gen => 'Generation',
1.765     raeburn  10130:                         inststatus => 'Affiliation',
1.624     raeburn  10131:                    );
                   10132:     return %fieldtitles;
                   10133: }
                   10134: 
1.642     raeburn  10135: sub sorted_inst_types {
                   10136:     my ($dom) = @_;
1.1185    raeburn  10137:     my ($usertypes,$order);
                   10138:     my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
                   10139:     if (ref($domdefaults{'inststatus'}) eq 'HASH') {
                   10140:         $usertypes = $domdefaults{'inststatus'}{'inststatustypes'};
                   10141:         $order = $domdefaults{'inststatus'}{'inststatusorder'};
                   10142:     } else {
                   10143:         ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
                   10144:     }
1.642     raeburn  10145:     my $othertitle = &mt('All users');
                   10146:     if ($env{'request.course.id'}) {
1.668     raeburn  10147:         $othertitle  = &mt('Any users');
1.642     raeburn  10148:     }
                   10149:     my @types;
                   10150:     if (ref($order) eq 'ARRAY') {
                   10151:         @types = @{$order};
                   10152:     }
                   10153:     if (@types == 0) {
                   10154:         if (ref($usertypes) eq 'HASH') {
                   10155:             @types = sort(keys(%{$usertypes}));
                   10156:         }
                   10157:     }
                   10158:     if (keys(%{$usertypes}) > 0) {
                   10159:         $othertitle = &mt('Other users');
                   10160:     }
                   10161:     return ($othertitle,$usertypes,\@types);
                   10162: }
                   10163: 
1.645     raeburn  10164: sub get_institutional_codes {
                   10165:     my ($settings,$allcourses,$LC_code) = @_;
                   10166: # Get complete list of course sections to update
                   10167:     my @currsections = ();
                   10168:     my @currxlists = ();
                   10169:     my $coursecode = $$settings{'internal.coursecode'};
                   10170: 
                   10171:     if ($$settings{'internal.sectionnums'} ne '') {
                   10172:         @currsections = split(/,/,$$settings{'internal.sectionnums'});
                   10173:     }
                   10174: 
                   10175:     if ($$settings{'internal.crosslistings'} ne '') {
                   10176:         @currxlists = split(/,/,$$settings{'internal.crosslistings'});
                   10177:     }
                   10178: 
                   10179:     if (@currxlists > 0) {
                   10180:         foreach (@currxlists) {
                   10181:             if (m/^([^:]+):(\w*)$/) {
                   10182:                 unless (grep/^$1$/,@{$allcourses}) {
                   10183:                     push @{$allcourses},$1;
                   10184:                     $$LC_code{$1} = $2;
                   10185:                 }
                   10186:             }
                   10187:         }
                   10188:     }
                   10189:  
                   10190:     if (@currsections > 0) {
                   10191:         foreach (@currsections) {
                   10192:             if (m/^(\w+):(\w*)$/) {
                   10193:                 my $sec = $coursecode.$1;
                   10194:                 my $lc_sec = $2;
                   10195:                 unless (grep/^$sec$/,@{$allcourses}) {
                   10196:                     push @{$allcourses},$sec;
                   10197:                     $$LC_code{$sec} = $lc_sec;
                   10198:                 }
                   10199:             }
                   10200:         }
                   10201:     }
                   10202:     return;
                   10203: }
                   10204: 
1.971     raeburn  10205: sub get_standard_codeitems {
                   10206:     return ('Year','Semester','Department','Number','Section');
                   10207: }
                   10208: 
1.112     bowersj2 10209: =pod
                   10210: 
1.780     raeburn  10211: =head1 Slot Helpers
                   10212: 
                   10213: =over 4
                   10214: 
                   10215: =item * sorted_slots()
                   10216: 
1.1040    raeburn  10217: Sorts an array of slot names in order of an optional sort key,
                   10218: default sort is by slot start time (earliest first). 
1.780     raeburn  10219: 
                   10220: Inputs:
                   10221: 
                   10222: =over 4
                   10223: 
                   10224: slotsarr  - Reference to array of unsorted slot names.
                   10225: 
                   10226: slots     - Reference to hash of hash, where outer hash keys are slot names.
                   10227: 
1.1040    raeburn  10228: sortkey   - Name of key in inner hash to be sorted on (e.g., starttime).
                   10229: 
1.549     albertel 10230: =back
                   10231: 
1.780     raeburn  10232: Returns:
                   10233: 
                   10234: =over 4
                   10235: 
1.1040    raeburn  10236: sorted   - An array of slot names sorted by a specified sort key 
                   10237:            (default sort key is start time of the slot).
1.780     raeburn  10238: 
                   10239: =back
                   10240: 
                   10241: =cut
                   10242: 
                   10243: 
                   10244: sub sorted_slots {
1.1040    raeburn  10245:     my ($slotsarr,$slots,$sortkey) = @_;
                   10246:     if ($sortkey eq '') {
                   10247:         $sortkey = 'starttime';
                   10248:     }
1.780     raeburn  10249:     my @sorted;
                   10250:     if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
                   10251:         @sorted =
                   10252:             sort {
                   10253:                      if (ref($slots->{$a}) && ref($slots->{$b})) {
1.1040    raeburn  10254:                          return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
1.780     raeburn  10255:                      }
                   10256:                      if (ref($slots->{$a})) { return -1;}
                   10257:                      if (ref($slots->{$b})) { return 1;}
                   10258:                      return 0;
                   10259:                  } @{$slotsarr};
                   10260:     }
                   10261:     return @sorted;
                   10262: }
                   10263: 
1.1040    raeburn  10264: =pod
                   10265: 
                   10266: =item * get_future_slots()
                   10267: 
                   10268: Inputs:
                   10269: 
                   10270: =over 4
                   10271: 
                   10272: cnum - course number
                   10273: 
                   10274: cdom - course domain
                   10275: 
                   10276: now - current UNIX time
                   10277: 
                   10278: symb - optional symb
                   10279: 
                   10280: =back
                   10281: 
                   10282: Returns:
                   10283: 
                   10284: =over 4
                   10285: 
                   10286: sorted_reservable - ref to array of student_schedulable slots currently 
                   10287:                     reservable, ordered by end date of reservation period.
                   10288: 
                   10289: reservable_now - ref to hash of student_schedulable slots currently
                   10290:                  reservable.
                   10291: 
                   10292:     Keys in inner hash are:
                   10293:     (a) symb: either blank or symb to which slot use is restricted.
                   10294:     (b) endreserve: end date of reservation period. 
                   10295: 
                   10296: sorted_future - ref to array of student_schedulable slots reservable in
                   10297:                 the future, ordered by start date of reservation period.
                   10298: 
                   10299: future_reservable - ref to hash of student_schedulable slots reservable
                   10300:                     in the future.
                   10301: 
                   10302:     Keys in inner hash are:
                   10303:     (a) symb: either blank or symb to which slot use is restricted.
                   10304:     (b) startreserve:  start date of reservation period.
                   10305: 
                   10306: =back
                   10307: 
                   10308: =cut
                   10309: 
                   10310: sub get_future_slots {
                   10311:     my ($cnum,$cdom,$now,$symb) = @_;
1.1229    raeburn  10312:     my $map;
                   10313:     if ($symb) {
                   10314:         ($map) = &Apache::lonnet::decode_symb($symb);
                   10315:     }
1.1040    raeburn  10316:     my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
                   10317:     my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
                   10318:     foreach my $slot (keys(%slots)) {
                   10319:         next unless($slots{$slot}->{'type'} eq 'schedulable_student');
                   10320:         if ($symb) {
1.1229    raeburn  10321:             if ($slots{$slot}->{'symb'} ne '') {
                   10322:                 my $canuse;
                   10323:                 my %oksymbs;
                   10324:                 my @slotsymbs = split(/\s*,\s*/,$slots{$slot}->{'symb'});
                   10325:                 map { $oksymbs{$_} = 1; } @slotsymbs;
                   10326:                 if ($oksymbs{$symb}) {
                   10327:                     $canuse = 1;
                   10328:                 } else {
                   10329:                     foreach my $item (@slotsymbs) {
                   10330:                         if ($item =~ /\.(page|sequence)$/) {
                   10331:                             (undef,undef,my $sloturl) = &Apache::lonnet::decode_symb($item);
                   10332:                             if (($map ne '') && ($map eq $sloturl)) {
                   10333:                                 $canuse = 1;
                   10334:                                 last;
                   10335:                             }
                   10336:                         }
                   10337:                     }
                   10338:                 }
                   10339:                 next unless ($canuse);
                   10340:             }
1.1040    raeburn  10341:         }
                   10342:         if (($slots{$slot}->{'starttime'} > $now) &&
                   10343:             ($slots{$slot}->{'endtime'} > $now)) {
                   10344:             if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
                   10345:                 my $userallowed = 0;
                   10346:                 if ($slots{$slot}->{'allowedsections'}) {
                   10347:                     my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
                   10348:                     if (!defined($env{'request.role.sec'})
                   10349:                         && grep(/^No section assigned$/,@allowed_sec)) {
                   10350:                         $userallowed=1;
                   10351:                     } else {
                   10352:                         if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
                   10353:                             $userallowed=1;
                   10354:                         }
                   10355:                     }
                   10356:                     unless ($userallowed) {
                   10357:                         if (defined($env{'request.course.groups'})) {
                   10358:                             my @groups = split(/:/,$env{'request.course.groups'});
                   10359:                             foreach my $group (@groups) {
                   10360:                                 if (grep(/^\Q$group\E$/,@allowed_sec)) {
                   10361:                                     $userallowed=1;
                   10362:                                     last;
                   10363:                                 }
                   10364:                             }
                   10365:                         }
                   10366:                     }
                   10367:                 }
                   10368:                 if ($slots{$slot}->{'allowedusers'}) {
                   10369:                     my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
                   10370:                     my $user = $env{'user.name'}.':'.$env{'user.domain'};
                   10371:                     if (grep(/^\Q$user\E$/,@allowed_users)) {
                   10372:                         $userallowed = 1;
                   10373:                     }
                   10374:                 }
                   10375:                 next unless($userallowed);
                   10376:             }
                   10377:             my $startreserve = $slots{$slot}->{'startreserve'};
                   10378:             my $endreserve = $slots{$slot}->{'endreserve'};
                   10379:             my $symb = $slots{$slot}->{'symb'};
                   10380:             if (($startreserve < $now) &&
                   10381:                 (!$endreserve || $endreserve > $now)) {
                   10382:                 my $lastres = $endreserve;
                   10383:                 if (!$lastres) {
                   10384:                     $lastres = $slots{$slot}->{'starttime'};
                   10385:                 }
                   10386:                 $reservable_now{$slot} = {
                   10387:                                            symb       => $symb,
                   10388:                                            endreserve => $lastres
                   10389:                                          };
                   10390:             } elsif (($startreserve > $now) &&
                   10391:                      (!$endreserve || $endreserve > $startreserve)) {
                   10392:                 $future_reservable{$slot} = {
                   10393:                                               symb         => $symb,
                   10394:                                               startreserve => $startreserve
                   10395:                                             };
                   10396:             }
                   10397:         }
                   10398:     }
                   10399:     my @unsorted_reservable = keys(%reservable_now);
                   10400:     if (@unsorted_reservable > 0) {
                   10401:         @sorted_reservable = 
                   10402:             &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
                   10403:     }
                   10404:     my @unsorted_future = keys(%future_reservable);
                   10405:     if (@unsorted_future > 0) {
                   10406:         @sorted_future =
                   10407:             &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
                   10408:     }
                   10409:     return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
                   10410: }
1.780     raeburn  10411: 
                   10412: =pod
                   10413: 
1.1057    foxr     10414: =back
                   10415: 
1.549     albertel 10416: =head1 HTTP Helpers
                   10417: 
                   10418: =over 4
                   10419: 
1.648     raeburn  10420: =item * &get_unprocessed_cgi($query,$possible_names)
1.112     bowersj2 10421: 
1.258     albertel 10422: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112     bowersj2 10423: $query.  The parameters listed in $possible_names (an array reference),
1.258     albertel 10424: will be set in $env{'form.name'} if they do not already exist.
1.112     bowersj2 10425: 
                   10426: Typically called with $ENV{'QUERY_STRING'} as the first parameter.  
                   10427: $possible_names is an ref to an array of form element names.  As an example:
                   10428: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258     albertel 10429: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112     bowersj2 10430: 
                   10431: =cut
1.1       albertel 10432: 
1.6       albertel 10433: sub get_unprocessed_cgi {
1.25      albertel 10434:   my ($query,$possible_names)= @_;
1.26      matthew  10435:   # $Apache::lonxml::debug=1;
1.356     albertel 10436:   foreach my $pair (split(/&/,$query)) {
                   10437:     my ($name, $value) = split(/=/,$pair);
1.369     www      10438:     $name = &unescape($name);
1.25      albertel 10439:     if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
                   10440:       $value =~ tr/+/ /;
                   10441:       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258     albertel 10442:       unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25      albertel 10443:     }
1.16      harris41 10444:   }
1.6       albertel 10445: }
                   10446: 
1.112     bowersj2 10447: =pod
                   10448: 
1.648     raeburn  10449: =item * &cacheheader() 
1.112     bowersj2 10450: 
                   10451: returns cache-controlling header code
                   10452: 
                   10453: =cut
                   10454: 
1.7       albertel 10455: sub cacheheader {
1.258     albertel 10456:     unless ($env{'request.method'} eq 'GET') { return ''; }
1.216     albertel 10457:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
                   10458:     my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7       albertel 10459:                 <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
                   10460:                 <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216     albertel 10461:     return $output;
1.7       albertel 10462: }
                   10463: 
1.112     bowersj2 10464: =pod
                   10465: 
1.648     raeburn  10466: =item * &no_cache($r) 
1.112     bowersj2 10467: 
                   10468: specifies header code to not have cache
                   10469: 
                   10470: =cut
                   10471: 
1.9       albertel 10472: sub no_cache {
1.216     albertel 10473:     my ($r) = @_;
                   10474:     if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258     albertel 10475: 	$env{'request.method'} ne 'GET') { return ''; }
1.216     albertel 10476:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
                   10477:     $r->no_cache(1);
                   10478:     $r->header_out("Expires" => $date);
                   10479:     $r->header_out("Pragma" => "no-cache");
1.123     www      10480: }
                   10481: 
                   10482: sub content_type {
1.181     albertel 10483:     my ($r,$type,$charset) = @_;
1.299     foxr     10484:     if ($r) {
                   10485: 	#  Note that printout.pl calls this with undef for $r.
                   10486: 	&no_cache($r);
                   10487:     }
1.258     albertel 10488:     if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181     albertel 10489:     unless ($charset) {
                   10490: 	$charset=&Apache::lonlocal::current_encoding;
                   10491:     }
                   10492:     if ($charset) { $type.='; charset='.$charset; }
                   10493:     if ($r) {
                   10494: 	$r->content_type($type);
                   10495:     } else {
                   10496: 	print("Content-type: $type\n\n");
                   10497:     }
1.9       albertel 10498: }
1.25      albertel 10499: 
1.112     bowersj2 10500: =pod
                   10501: 
1.648     raeburn  10502: =item * &add_to_env($name,$value) 
1.112     bowersj2 10503: 
1.258     albertel 10504: adds $name to the %env hash with value
1.112     bowersj2 10505: $value, if $name already exists, the entry is converted to an array
                   10506: reference and $value is added to the array.
                   10507: 
                   10508: =cut
                   10509: 
1.25      albertel 10510: sub add_to_env {
                   10511:   my ($name,$value)=@_;
1.258     albertel 10512:   if (defined($env{$name})) {
                   10513:     if (ref($env{$name})) {
1.25      albertel 10514:       #already have multiple values
1.258     albertel 10515:       push(@{ $env{$name} },$value);
1.25      albertel 10516:     } else {
                   10517:       #first time seeing multiple values, convert hash entry to an arrayref
1.258     albertel 10518:       my $first=$env{$name};
                   10519:       undef($env{$name});
                   10520:       push(@{ $env{$name} },$first,$value);
1.25      albertel 10521:     }
                   10522:   } else {
1.258     albertel 10523:     $env{$name}=$value;
1.25      albertel 10524:   }
1.31      albertel 10525: }
1.149     albertel 10526: 
                   10527: =pod
                   10528: 
1.648     raeburn  10529: =item * &get_env_multiple($name) 
1.149     albertel 10530: 
1.258     albertel 10531: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149     albertel 10532: values may be defined and end up as an array ref.
                   10533: 
                   10534: returns an array of values
                   10535: 
                   10536: =cut
                   10537: 
                   10538: sub get_env_multiple {
                   10539:     my ($name) = @_;
                   10540:     my @values;
1.258     albertel 10541:     if (defined($env{$name})) {
1.149     albertel 10542:         # exists is it an array
1.258     albertel 10543:         if (ref($env{$name})) {
                   10544:             @values=@{ $env{$name} };
1.149     albertel 10545:         } else {
1.258     albertel 10546:             $values[0]=$env{$name};
1.149     albertel 10547:         }
                   10548:     }
                   10549:     return(@values);
                   10550: }
                   10551: 
1.660     raeburn  10552: sub ask_for_embedded_content {
                   10553:     my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
1.1071    raeburn  10554:     my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
1.1085    raeburn  10555:         %currsubfile,%unused,$rem);
1.1071    raeburn  10556:     my $counter = 0;
                   10557:     my $numnew = 0;
1.987     raeburn  10558:     my $numremref = 0;
                   10559:     my $numinvalid = 0;
                   10560:     my $numpathchg = 0;
                   10561:     my $numexisting = 0;
1.1071    raeburn  10562:     my $numunused = 0;
                   10563:     my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
1.1156    raeburn  10564:         $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap);
1.1071    raeburn  10565:     my $heading = &mt('Upload embedded files');
                   10566:     my $buttontext = &mt('Upload');
                   10567: 
1.1085    raeburn  10568:     if ($env{'request.course.id'}) {
1.1123    raeburn  10569:         if ($actionurl eq '/adm/dependencies') {
                   10570:             $navmap = Apache::lonnavmaps::navmap->new();
                   10571:         }
                   10572:         $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   10573:         $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1085    raeburn  10574:     }
1.1123    raeburn  10575:     if (($actionurl eq '/adm/portfolio') || 
                   10576:         ($actionurl eq '/adm/coursegrp_portfolio')) {
1.984     raeburn  10577:         my $current_path='/';
                   10578:         if ($env{'form.currentpath'}) {
                   10579:             $current_path = $env{'form.currentpath'};
                   10580:         }
                   10581:         if ($actionurl eq '/adm/coursegrp_portfolio') {
1.1123    raeburn  10582:             $udom = $cdom;
                   10583:             $uname = $cnum;
1.984     raeburn  10584:             $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
                   10585:         } else {
                   10586:             $udom = $env{'user.domain'};
                   10587:             $uname = $env{'user.name'};
                   10588:             $url = '/userfiles/portfolio';
                   10589:         }
1.987     raeburn  10590:         $toplevel = $url.'/';
1.984     raeburn  10591:         $url .= $current_path;
                   10592:         $getpropath = 1;
1.987     raeburn  10593:     } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
                   10594:              ($actionurl eq '/adm/imsimport')) { 
1.1022    www      10595:         my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
1.1026    raeburn  10596:         $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
1.987     raeburn  10597:         $toplevel = $url;
1.984     raeburn  10598:         if ($rest ne '') {
1.987     raeburn  10599:             $url .= $rest;
                   10600:         }
                   10601:     } elsif ($actionurl eq '/adm/coursedocs') {
                   10602:         if (ref($args) eq 'HASH') {
1.1071    raeburn  10603:             $url = $args->{'docs_url'};
                   10604:             $toplevel = $url;
1.1084    raeburn  10605:             if ($args->{'context'} eq 'paste') {
                   10606:                 ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
                   10607:                 ($path) = 
                   10608:                     ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
                   10609:                 $fileloc = &Apache::lonnet::filelocation('',$toplevel);
                   10610:                 $fileloc =~ s{^/}{};
                   10611:             }
1.1071    raeburn  10612:         }
1.1084    raeburn  10613:     } elsif ($actionurl eq '/adm/dependencies')  {
1.1071    raeburn  10614:         if ($env{'request.course.id'} ne '') {
                   10615:             if (ref($args) eq 'HASH') {
                   10616:                 $url = $args->{'docs_url'};
                   10617:                 $title = $args->{'docs_title'};
1.1126    raeburn  10618:                 $toplevel = $url; 
                   10619:                 unless ($toplevel =~ m{^/}) {
                   10620:                     $toplevel = "/$url";
                   10621:                 }
1.1085    raeburn  10622:                 ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
1.1126    raeburn  10623:                 if ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E)}) {
                   10624:                     $path = $1;
                   10625:                 } else {
                   10626:                     ($path) =
                   10627:                         ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
                   10628:                 }
1.1195    raeburn  10629:                 if ($toplevel=~/^\/*(uploaded|editupload)/) {
                   10630:                     $fileloc = $toplevel;
                   10631:                     $fileloc=~ s/^\s*(\S+)\s*$/$1/;
                   10632:                     my ($udom,$uname,$fname) =
                   10633:                         ($fileloc=~ m{^/+(?:uploaded|editupload)/+($match_domain)/+($match_name)/+(.*)$});
                   10634:                     $fileloc = propath($udom,$uname).'/userfiles/'.$fname;
                   10635:                 } else {
                   10636:                     $fileloc = &Apache::lonnet::filelocation('',$toplevel);
                   10637:                 }
1.1071    raeburn  10638:                 $fileloc =~ s{^/}{};
                   10639:                 ($filename) = ($fileloc =~ m{.+/([^/]+)$});
                   10640:                 $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
                   10641:             }
1.987     raeburn  10642:         }
1.1123    raeburn  10643:     } elsif ($actionurl eq "/public/$cdom/$cnum/syllabus") {
                   10644:         $udom = $cdom;
                   10645:         $uname = $cnum;
                   10646:         $url = "/uploaded/$cdom/$cnum/portfolio/syllabus";
                   10647:         $toplevel = $url;
                   10648:         $path = $url;
                   10649:         $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';
                   10650:         $fileloc =~ s{^/}{};
1.987     raeburn  10651:     }
1.1126    raeburn  10652:     foreach my $file (keys(%{$allfiles})) {
                   10653:         my $embed_file;
                   10654:         if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {
                   10655:             $embed_file = $1;
                   10656:         } else {
                   10657:             $embed_file = $file;
                   10658:         }
1.1158    raeburn  10659:         my ($absolutepath,$cleaned_file);
                   10660:         if ($embed_file =~ m{^\w+://}) {
                   10661:             $cleaned_file = $embed_file;
1.1147    raeburn  10662:             $newfiles{$cleaned_file} = 1;
                   10663:             $mapping{$cleaned_file} = $embed_file;
1.987     raeburn  10664:         } else {
1.1158    raeburn  10665:             $cleaned_file = &clean_path($embed_file);
1.987     raeburn  10666:             if ($embed_file =~ m{^/}) {
                   10667:                 $absolutepath = $embed_file;
                   10668:             }
1.1147    raeburn  10669:             if ($cleaned_file =~ m{/}) {
                   10670:                 my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$});
1.987     raeburn  10671:                 $path = &check_for_traversal($path,$url,$toplevel);
                   10672:                 my $item = $fname;
                   10673:                 if ($path ne '') {
                   10674:                     $item = $path.'/'.$fname;
                   10675:                     $subdependencies{$path}{$fname} = 1;
                   10676:                 } else {
                   10677:                     $dependencies{$item} = 1;
                   10678:                 }
                   10679:                 if ($absolutepath) {
                   10680:                     $mapping{$item} = $absolutepath;
                   10681:                 } else {
                   10682:                     $mapping{$item} = $embed_file;
                   10683:                 }
                   10684:             } else {
                   10685:                 $dependencies{$embed_file} = 1;
                   10686:                 if ($absolutepath) {
1.1147    raeburn  10687:                     $mapping{$cleaned_file} = $absolutepath;
1.987     raeburn  10688:                 } else {
1.1147    raeburn  10689:                     $mapping{$cleaned_file} = $embed_file;
1.987     raeburn  10690:                 }
                   10691:             }
1.984     raeburn  10692:         }
                   10693:     }
1.1071    raeburn  10694:     my $dirptr = 16384;
1.984     raeburn  10695:     foreach my $path (keys(%subdependencies)) {
1.1071    raeburn  10696:         $currsubfile{$path} = {};
1.1123    raeburn  10697:         if (($actionurl eq '/adm/portfolio') || 
                   10698:             ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021    raeburn  10699:             my ($sublistref,$listerror) =
                   10700:                 &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
                   10701:             if (ref($sublistref) eq 'ARRAY') {
                   10702:                 foreach my $line (@{$sublistref}) {
                   10703:                     my ($file_name,$rest) = split(/\&/,$line,2);
1.1071    raeburn  10704:                     $currsubfile{$path}{$file_name} = 1;
1.1021    raeburn  10705:                 }
1.984     raeburn  10706:             }
1.987     raeburn  10707:         } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984     raeburn  10708:             if (opendir(my $dir,$url.'/'.$path)) {
                   10709:                 my @subdir_list = grep(!/^\./,readdir($dir));
1.1071    raeburn  10710:                 map {$currsubfile{$path}{$_} = 1;} @subdir_list;
                   10711:             }
1.1084    raeburn  10712:         } elsif (($actionurl eq '/adm/dependencies') ||
                   10713:                  (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123    raeburn  10714:                   ($args->{'context'} eq 'paste')) ||
                   10715:                  ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071    raeburn  10716:             if ($env{'request.course.id'} ne '') {
1.1123    raeburn  10717:                 my $dir;
                   10718:                 if ($actionurl eq "/public/$cdom/$cnum/syllabus") {
                   10719:                     $dir = $fileloc;
                   10720:                 } else {
                   10721:                     ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
                   10722:                 }
1.1071    raeburn  10723:                 if ($dir ne '') {
                   10724:                     my ($sublistref,$listerror) =
                   10725:                         &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
                   10726:                     if (ref($sublistref) eq 'ARRAY') {
                   10727:                         foreach my $line (@{$sublistref}) {
                   10728:                             my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
                   10729:                                 undef,$mtime)=split(/\&/,$line,12);
                   10730:                             unless (($testdir&$dirptr) ||
                   10731:                                     ($file_name =~ /^\.\.?$/)) {
                   10732:                                 $currsubfile{$path}{$file_name} = [$size,$mtime];
                   10733:                             }
                   10734:                         }
                   10735:                     }
                   10736:                 }
1.984     raeburn  10737:             }
                   10738:         }
                   10739:         foreach my $file (keys(%{$subdependencies{$path}})) {
1.1071    raeburn  10740:             if (exists($currsubfile{$path}{$file})) {
1.987     raeburn  10741:                 my $item = $path.'/'.$file;
                   10742:                 unless ($mapping{$item} eq $item) {
                   10743:                     $pathchanges{$item} = 1;
                   10744:                 }
                   10745:                 $existing{$item} = 1;
                   10746:                 $numexisting ++;
                   10747:             } else {
                   10748:                 $newfiles{$path.'/'.$file} = 1;
1.984     raeburn  10749:             }
                   10750:         }
1.1071    raeburn  10751:         if ($actionurl eq '/adm/dependencies') {
                   10752:             foreach my $path (keys(%currsubfile)) {
                   10753:                 if (ref($currsubfile{$path}) eq 'HASH') {
                   10754:                     foreach my $file (keys(%{$currsubfile{$path}})) {
                   10755:                          unless ($subdependencies{$path}{$file}) {
1.1085    raeburn  10756:                              next if (($rem ne '') &&
                   10757:                                       (($env{"httpref.$rem"."$path/$file"} ne '') ||
                   10758:                                        (ref($navmap) &&
                   10759:                                        (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
                   10760:                                         (($file =~ /^(.*\.s?html?)\.bak$/i) &&
                   10761:                                          ($navmap->getResourceByUrl($rem."$path/$1")))))));
1.1071    raeburn  10762:                              $unused{$path.'/'.$file} = 1; 
                   10763:                          }
                   10764:                     }
                   10765:                 }
                   10766:             }
                   10767:         }
1.984     raeburn  10768:     }
1.987     raeburn  10769:     my %currfile;
1.1123    raeburn  10770:     if (($actionurl eq '/adm/portfolio') ||
                   10771:         ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021    raeburn  10772:         my ($dirlistref,$listerror) =
                   10773:             &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
                   10774:         if (ref($dirlistref) eq 'ARRAY') {
                   10775:             foreach my $line (@{$dirlistref}) {
                   10776:                 my ($file_name,$rest) = split(/\&/,$line,2);
                   10777:                 $currfile{$file_name} = 1;
                   10778:             }
1.984     raeburn  10779:         }
1.987     raeburn  10780:     } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984     raeburn  10781:         if (opendir(my $dir,$url)) {
1.987     raeburn  10782:             my @dir_list = grep(!/^\./,readdir($dir));
1.984     raeburn  10783:             map {$currfile{$_} = 1;} @dir_list;
                   10784:         }
1.1084    raeburn  10785:     } elsif (($actionurl eq '/adm/dependencies') ||
                   10786:              (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123    raeburn  10787:               ($args->{'context'} eq 'paste')) ||
                   10788:              ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071    raeburn  10789:         if ($env{'request.course.id'} ne '') {
                   10790:             my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
                   10791:             if ($dir ne '') {
                   10792:                 my ($dirlistref,$listerror) =
                   10793:                     &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
                   10794:                 if (ref($dirlistref) eq 'ARRAY') {
                   10795:                     foreach my $line (@{$dirlistref}) {
                   10796:                         my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
                   10797:                             $size,undef,$mtime)=split(/\&/,$line,12);
                   10798:                         unless (($testdir&$dirptr) ||
                   10799:                                 ($file_name =~ /^\.\.?$/)) {
                   10800:                             $currfile{$file_name} = [$size,$mtime];
                   10801:                         }
                   10802:                     }
                   10803:                 }
                   10804:             }
                   10805:         }
1.984     raeburn  10806:     }
                   10807:     foreach my $file (keys(%dependencies)) {
1.1071    raeburn  10808:         if (exists($currfile{$file})) {
1.987     raeburn  10809:             unless ($mapping{$file} eq $file) {
                   10810:                 $pathchanges{$file} = 1;
                   10811:             }
                   10812:             $existing{$file} = 1;
                   10813:             $numexisting ++;
                   10814:         } else {
1.984     raeburn  10815:             $newfiles{$file} = 1;
                   10816:         }
                   10817:     }
1.1071    raeburn  10818:     foreach my $file (keys(%currfile)) {
                   10819:         unless (($file eq $filename) ||
                   10820:                 ($file eq $filename.'.bak') ||
                   10821:                 ($dependencies{$file})) {
1.1085    raeburn  10822:             if ($actionurl eq '/adm/dependencies') {
1.1126    raeburn  10823:                 unless ($toplevel =~ m{^\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E}) {
                   10824:                     next if (($rem ne '') &&
                   10825:                              (($env{"httpref.$rem".$file} ne '') ||
                   10826:                               (ref($navmap) &&
                   10827:                               (($navmap->getResourceByUrl($rem.$file) ne '') ||
                   10828:                                (($file =~ /^(.*\.s?html?)\.bak$/i) &&
                   10829:                                 ($navmap->getResourceByUrl($rem.$1)))))));
                   10830:                 }
1.1085    raeburn  10831:             }
1.1071    raeburn  10832:             $unused{$file} = 1;
                   10833:         }
                   10834:     }
1.1084    raeburn  10835:     if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
                   10836:         ($args->{'context'} eq 'paste')) {
                   10837:         $counter = scalar(keys(%existing));
                   10838:         $numpathchg = scalar(keys(%pathchanges));
1.1123    raeburn  10839:         return ($output,$counter,$numpathchg,\%existing);
                   10840:     } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") && 
                   10841:              (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {
                   10842:         $counter = scalar(keys(%existing));
                   10843:         $numpathchg = scalar(keys(%pathchanges));
                   10844:         return ($output,$counter,$numpathchg,\%existing,\%mapping);
1.1084    raeburn  10845:     }
1.984     raeburn  10846:     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
1.1071    raeburn  10847:         if ($actionurl eq '/adm/dependencies') {
                   10848:             next if ($embed_file =~ m{^\w+://});
                   10849:         }
1.660     raeburn  10850:         $upload_output .= &start_data_table_row().
1.1123    raeburn  10851:                           '<td valign="top"><img src="'.&icon($embed_file).'" />&nbsp;'.
1.1071    raeburn  10852:                           '<span class="LC_filename">'.$embed_file.'</span>';
1.987     raeburn  10853:         unless ($mapping{$embed_file} eq $embed_file) {
1.1123    raeburn  10854:             $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.
                   10855:                               &mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
1.987     raeburn  10856:         }
1.1123    raeburn  10857:         $upload_output .= '</td>';
1.1071    raeburn  10858:         if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) { 
1.1123    raeburn  10859:             $upload_output.='<td align="right">'.
                   10860:                             '<span class="LC_info LC_fontsize_medium">'.
                   10861:                             &mt("URL points to web address").'</span>';
1.987     raeburn  10862:             $numremref++;
1.660     raeburn  10863:         } elsif ($args->{'error_on_invalid_names'}
                   10864:             && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
1.1123    raeburn  10865:             $upload_output.='<td align="right"><span class="LC_warning">'.
                   10866:                             &mt('Invalid characters').'</span>';
1.987     raeburn  10867:             $numinvalid++;
1.660     raeburn  10868:         } else {
1.1123    raeburn  10869:             $upload_output .= '<td>'.
                   10870:                               &embedded_file_element('upload_embedded',$counter,
1.987     raeburn  10871:                                                      $embed_file,\%mapping,
1.1071    raeburn  10872:                                                      $allfiles,$codebase,'upload');
                   10873:             $counter ++;
                   10874:             $numnew ++;
1.987     raeburn  10875:         }
                   10876:         $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
                   10877:     }
                   10878:     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
1.1071    raeburn  10879:         if ($actionurl eq '/adm/dependencies') {
                   10880:             my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file);
                   10881:             $modify_output .= &start_data_table_row().
                   10882:                               '<td><a href="'.$path.'/'.$embed_file.'" style="text-decoration:none;">'.
                   10883:                               '<img src="'.&icon($embed_file).'" border="0" />'.
                   10884:                               '&nbsp;<span class="LC_filename">'.$embed_file.'</span></a></td>'.
                   10885:                               '<td>'.$size.'</td>'.
                   10886:                               '<td>'.$mtime.'</td>'.
                   10887:                               '<td><label><input type="checkbox" name="mod_upload_dep" '.
                   10888:                               'onclick="toggleBrowse('."'$counter'".')" id="mod_upload_dep_'.
                   10889:                               $counter.'" value="'.$counter.'" />'.&mt('Yes').'</label>'.
                   10890:                               '<div id="moduploaddep_'.$counter.'" style="display:none;">'.
                   10891:                               &embedded_file_element('upload_embedded',$counter,
                   10892:                                                      $embed_file,\%mapping,
                   10893:                                                      $allfiles,$codebase,'modify').
                   10894:                               '</div></td>'.
                   10895:                               &end_data_table_row()."\n";
                   10896:             $counter ++;
                   10897:         } else {
                   10898:             $upload_output .= &start_data_table_row().
1.1123    raeburn  10899:                               '<td valign="top"><img src="'.&icon($embed_file).'" />&nbsp;'.
                   10900:                               '<span class="LC_filename">'.$embed_file.'</span></td>'.
                   10901:                               '<td align="right"><span class="LC_info LC_fontsize_medium">'.&mt('Already exists').'</span></td>'.
1.1071    raeburn  10902:                               &Apache::loncommon::end_data_table_row()."\n";
                   10903:         }
                   10904:     }
                   10905:     my $delidx = $counter;
                   10906:     foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) {
                   10907:         my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile);
                   10908:         $delete_output .= &start_data_table_row().
                   10909:                           '<td><img src="'.&icon($oldfile).'" />'.
                   10910:                           '&nbsp;<span class="LC_filename">'.$oldfile.'</span></td>'.
                   10911:                           '<td>'.$size.'</td>'.
                   10912:                           '<td>'.$mtime.'</td>'.
                   10913:                           '<td><label><input type="checkbox" name="del_upload_dep" '.
                   10914:                           ' value="'.$delidx.'" />'.&mt('Yes').'</label>'.
                   10915:                           &embedded_file_element('upload_embedded',$delidx,
                   10916:                                                  $oldfile,\%mapping,$allfiles,
                   10917:                                                  $codebase,'delete').'</td>'.
                   10918:                           &end_data_table_row()."\n"; 
                   10919:         $numunused ++;
                   10920:         $delidx ++;
1.987     raeburn  10921:     }
                   10922:     if ($upload_output) {
                   10923:         $upload_output = &start_data_table().
                   10924:                          $upload_output.
                   10925:                          &end_data_table()."\n";
                   10926:     }
1.1071    raeburn  10927:     if ($modify_output) {
                   10928:         $modify_output = &start_data_table().
                   10929:                          &start_data_table_header_row().
                   10930:                          '<th>'.&mt('File').'</th>'.
                   10931:                          '<th>'.&mt('Size (KB)').'</th>'.
                   10932:                          '<th>'.&mt('Modified').'</th>'.
                   10933:                          '<th>'.&mt('Upload replacement?').'</th>'.
                   10934:                          &end_data_table_header_row().
                   10935:                          $modify_output.
                   10936:                          &end_data_table()."\n";
                   10937:     }
                   10938:     if ($delete_output) {
                   10939:         $delete_output = &start_data_table().
                   10940:                          &start_data_table_header_row().
                   10941:                          '<th>'.&mt('File').'</th>'.
                   10942:                          '<th>'.&mt('Size (KB)').'</th>'.
                   10943:                          '<th>'.&mt('Modified').'</th>'.
                   10944:                          '<th>'.&mt('Delete?').'</th>'.
                   10945:                          &end_data_table_header_row().
                   10946:                          $delete_output.
                   10947:                          &end_data_table()."\n";
                   10948:     }
1.987     raeburn  10949:     my $applies = 0;
                   10950:     if ($numremref) {
                   10951:         $applies ++;
                   10952:     }
                   10953:     if ($numinvalid) {
                   10954:         $applies ++;
                   10955:     }
                   10956:     if ($numexisting) {
                   10957:         $applies ++;
                   10958:     }
1.1071    raeburn  10959:     if ($counter || $numunused) {
1.987     raeburn  10960:         $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
                   10961:                   ' method="post" enctype="multipart/form-data">'."\n".
1.1071    raeburn  10962:                   $state.'<h3>'.$heading.'</h3>'; 
                   10963:         if ($actionurl eq '/adm/dependencies') {
                   10964:             if ($numnew) {
                   10965:                 $output .= '<h4>'.&mt('Missing dependencies').'</h4>'.
                   10966:                            '<p>'.&mt('The following files need to be uploaded.').'</p>'."\n".
                   10967:                            $upload_output.'<br />'."\n";
                   10968:             }
                   10969:             if ($numexisting) {
                   10970:                 $output .= '<h4>'.&mt('Uploaded dependencies (in use)').'</h4>'.
                   10971:                            '<p>'.&mt('Upload a new file to replace the one currently in use.').'</p>'."\n".
                   10972:                            $modify_output.'<br />'."\n";
                   10973:                            $buttontext = &mt('Save changes');
                   10974:             }
                   10975:             if ($numunused) {
                   10976:                 $output .= '<h4>'.&mt('Unused files').'</h4>'.
                   10977:                            '<p>'.&mt('The following uploaded files are no longer used.').'</p>'."\n".
                   10978:                            $delete_output.'<br />'."\n";
                   10979:                            $buttontext = &mt('Save changes');
                   10980:             }
                   10981:         } else {
                   10982:             $output .= $upload_output.'<br />'."\n";
                   10983:         }
                   10984:         $output .= '<input type ="hidden" name="number_embedded_items" value="'.
                   10985:                    $counter.'" />'."\n";
                   10986:         if ($actionurl eq '/adm/dependencies') { 
                   10987:             $output .= '<input type ="hidden" name="number_newemb_items" value="'.
                   10988:                        $numnew.'" />'."\n";
                   10989:         } elsif ($actionurl eq '') {
1.987     raeburn  10990:             $output .=  '<input type="hidden" name="phase" value="three" />';
                   10991:         }
                   10992:     } elsif ($applies) {
                   10993:         $output = '<b>'.&mt('Referenced files').'</b>:<br />';
                   10994:         if ($applies > 1) {
                   10995:             $output .=  
1.1123    raeburn  10996:                 &mt('No dependencies need to be uploaded, as one of the following applies to each reference:').'<ul>';
1.987     raeburn  10997:             if ($numremref) {
                   10998:                 $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
                   10999:             }
                   11000:             if ($numinvalid) {
                   11001:                 $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
                   11002:             }
                   11003:             if ($numexisting) {
                   11004:                 $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
                   11005:             }
                   11006:             $output .= '</ul><br />';
                   11007:         } elsif ($numremref) {
                   11008:             $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
                   11009:         } elsif ($numinvalid) {
                   11010:             $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
                   11011:         } elsif ($numexisting) {
                   11012:             $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
                   11013:         }
                   11014:         $output .= $upload_output.'<br />';
                   11015:     }
                   11016:     my ($pathchange_output,$chgcount);
1.1071    raeburn  11017:     $chgcount = $counter;
1.987     raeburn  11018:     if (keys(%pathchanges) > 0) {
                   11019:         foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
1.1071    raeburn  11020:             if ($counter) {
1.987     raeburn  11021:                 $output .= &embedded_file_element('pathchange',$chgcount,
                   11022:                                                   $embed_file,\%mapping,
1.1071    raeburn  11023:                                                   $allfiles,$codebase,'change');
1.987     raeburn  11024:             } else {
                   11025:                 $pathchange_output .= 
                   11026:                     &start_data_table_row().
                   11027:                     '<td><input type ="checkbox" name="namechange" value="'.
                   11028:                     $chgcount.'" checked="checked" /></td>'.
                   11029:                     '<td>'.$mapping{$embed_file}.'</td>'.
                   11030:                     '<td>'.$embed_file.
                   11031:                     &embedded_file_element('pathchange',$numpathchg,$embed_file,
1.1071    raeburn  11032:                                            \%mapping,$allfiles,$codebase,'change').
1.987     raeburn  11033:                     '</td>'.&end_data_table_row();
1.660     raeburn  11034:             }
1.987     raeburn  11035:             $numpathchg ++;
                   11036:             $chgcount ++;
1.660     raeburn  11037:         }
                   11038:     }
1.1127    raeburn  11039:     if (($counter) || ($numunused)) {
1.987     raeburn  11040:         if ($numpathchg) {
                   11041:             $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
                   11042:                        $numpathchg.'" />'."\n";
                   11043:         }
                   11044:         if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') || 
                   11045:             ($actionurl eq '/adm/imsimport')) {
                   11046:             $output .= '<input type="hidden" name="phase" value="three" />'."\n";
                   11047:         } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
                   11048:             $output .= '<input type="hidden" name="action" value="upload_embedded" />';
1.1071    raeburn  11049:         } elsif ($actionurl eq '/adm/dependencies') {
                   11050:             $output .= '<input type="hidden" name="action" value="process_changes" />';
1.987     raeburn  11051:         }
1.1123    raeburn  11052:         $output .= '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n";
1.987     raeburn  11053:     } elsif ($numpathchg) {
                   11054:         my %pathchange = ();
                   11055:         $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
                   11056:         if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
                   11057:             $output .= '<p>'.&mt('or').'</p>'; 
1.1123    raeburn  11058:         }
1.987     raeburn  11059:     }
1.1071    raeburn  11060:     return ($output,$counter,$numpathchg);
1.987     raeburn  11061: }
                   11062: 
1.1147    raeburn  11063: =pod
                   11064: 
                   11065: =item * clean_path($name)
                   11066: 
                   11067: Performs clean-up of directories, subdirectories and filename in an
                   11068: embedded object, referenced in an HTML file which is being uploaded
                   11069: to a course or portfolio, where 
                   11070: "Upload embedded images/multimedia files if HTML file" checkbox was
                   11071: checked.
                   11072: 
                   11073: Clean-up is similar to replacements in lonnet::clean_filename()
                   11074: except each / between sub-directory and next level is preserved.
                   11075: 
                   11076: =cut
                   11077: 
                   11078: sub clean_path {
                   11079:     my ($embed_file) = @_;
                   11080:     $embed_file =~s{^/+}{};
                   11081:     my @contents;
                   11082:     if ($embed_file =~ m{/}) {
                   11083:         @contents = split(/\//,$embed_file);
                   11084:     } else {
                   11085:         @contents = ($embed_file);
                   11086:     }
                   11087:     my $lastidx = scalar(@contents)-1;
                   11088:     for (my $i=0; $i<=$lastidx; $i++) { 
                   11089:         $contents[$i]=~s{\\}{/}g;
                   11090:         $contents[$i]=~s/\s+/\_/g;
                   11091:         $contents[$i]=~s{[^/\w\.\-]}{}g;
                   11092:         if ($i == $lastidx) {
                   11093:             $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g;
                   11094:         }
                   11095:     }
                   11096:     if ($lastidx > 0) {
                   11097:         return join('/',@contents);
                   11098:     } else {
                   11099:         return $contents[0];
                   11100:     }
                   11101: }
                   11102: 
1.987     raeburn  11103: sub embedded_file_element {
1.1071    raeburn  11104:     my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
1.987     raeburn  11105:     return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
                   11106:                    (ref($codebase) eq 'HASH'));
                   11107:     my $output;
1.1071    raeburn  11108:     if (($context eq 'upload_embedded') && ($type ne 'delete')) {
1.987     raeburn  11109:        $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
                   11110:     }
                   11111:     $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
                   11112:                &escape($embed_file).'" />';
                   11113:     unless (($context eq 'upload_embedded') && 
                   11114:             ($mapping->{$embed_file} eq $embed_file)) {
                   11115:         $output .='
                   11116:         <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
                   11117:     }
                   11118:     my $attrib;
                   11119:     if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
                   11120:         $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
                   11121:     }
                   11122:     $output .=
                   11123:         "\n\t\t".
                   11124:         '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
                   11125:         $attrib.'" />';
                   11126:     if (exists($codebase->{$mapping->{$embed_file}})) {
                   11127:         $output .=
                   11128:             "\n\t\t".
                   11129:             '<input name="codebase_'.$num.'" type="hidden" value="'.
                   11130:             &escape($codebase->{$mapping->{$embed_file}}).'" />';
1.984     raeburn  11131:     }
1.987     raeburn  11132:     return $output;
1.660     raeburn  11133: }
                   11134: 
1.1071    raeburn  11135: sub get_dependency_details {
                   11136:     my ($currfile,$currsubfile,$embed_file) = @_;
                   11137:     my ($size,$mtime,$showsize,$showmtime);
                   11138:     if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
                   11139:         if ($embed_file =~ m{/}) {
                   11140:             my ($path,$fname) = split(/\//,$embed_file);
                   11141:             if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
                   11142:                 ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
                   11143:             }
                   11144:         } else {
                   11145:             if (ref($currfile->{$embed_file}) eq 'ARRAY') {
                   11146:                 ($size,$mtime) = @{$currfile->{$embed_file}};
                   11147:             }
                   11148:         }
                   11149:         $showsize = $size/1024.0;
                   11150:         $showsize = sprintf("%.1f",$showsize);
                   11151:         if ($mtime > 0) {
                   11152:             $showmtime = &Apache::lonlocal::locallocaltime($mtime);
                   11153:         }
                   11154:     }
                   11155:     return ($showsize,$showmtime);
                   11156: }
                   11157: 
                   11158: sub ask_embedded_js {
                   11159:     return <<"END";
                   11160: <script type="text/javascript"">
                   11161: // <![CDATA[
                   11162: function toggleBrowse(counter) {
                   11163:     var chkboxid = document.getElementById('mod_upload_dep_'+counter);
                   11164:     var fileid = document.getElementById('embedded_item_'+counter);
                   11165:     var uploaddivid = document.getElementById('moduploaddep_'+counter);
                   11166:     if (chkboxid.checked == true) {
                   11167:         uploaddivid.style.display='block';
                   11168:     } else {
                   11169:         uploaddivid.style.display='none';
                   11170:         fileid.value = '';
                   11171:     }
                   11172: }
                   11173: // ]]>
                   11174: </script>
                   11175: 
                   11176: END
                   11177: }
                   11178: 
1.661     raeburn  11179: sub upload_embedded {
                   11180:     my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
1.987     raeburn  11181:         $current_disk_usage,$hiddenstate,$actionurl) = @_;
                   11182:     my (%pathchange,$output,$modifyform,$footer,$returnflag);
1.661     raeburn  11183:     for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
                   11184:         next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
                   11185:         my $orig_uploaded_filename =
                   11186:             $env{'form.embedded_item_'.$i.'.filename'};
1.987     raeburn  11187:         foreach my $type ('orig','ref','attrib','codebase') {
                   11188:             if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
                   11189:                 $env{'form.embedded_'.$type.'_'.$i} =
                   11190:                     &unescape($env{'form.embedded_'.$type.'_'.$i});
                   11191:             }
                   11192:         }
1.661     raeburn  11193:         my ($path,$fname) =
                   11194:             ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
                   11195:         # no path, whole string is fname
                   11196:         if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
                   11197:         $fname = &Apache::lonnet::clean_filename($fname);
                   11198:         # See if there is anything left
                   11199:         next if ($fname eq '');
                   11200: 
                   11201:         # Check if file already exists as a file or directory.
                   11202:         my ($state,$msg);
                   11203:         if ($context eq 'portfolio') {
                   11204:             my $port_path = $dirpath;
                   11205:             if ($group ne '') {
                   11206:                 $port_path = "groups/$group/$port_path";
                   11207:             }
1.987     raeburn  11208:             ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
                   11209:                                               $fname,$group,'embedded_item_'.$i,
1.661     raeburn  11210:                                               $dir_root,$port_path,$disk_quota,
                   11211:                                               $current_disk_usage,$uname,$udom);
                   11212:             if ($state eq 'will_exceed_quota'
1.984     raeburn  11213:                 || $state eq 'file_locked') {
1.661     raeburn  11214:                 $output .= $msg;
                   11215:                 next;
                   11216:             }
                   11217:         } elsif (($context eq 'author') || ($context eq 'testbank')) {
                   11218:             ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
                   11219:             if ($state eq 'exists') {
                   11220:                 $output .= $msg;
                   11221:                 next;
                   11222:             }
                   11223:         }
                   11224:         # Check if extension is valid
                   11225:         if (($fname =~ /\.(\w+)$/) &&
                   11226:             (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
1.1155    bisitz   11227:             $output .= &mt('Invalid file extension ([_1]) - reserved for internal use.',$1)
                   11228:                       .' '.&mt('Rename the file with a different extension and re-upload.').'<br />';
1.661     raeburn  11229:             next;
                   11230:         } elsif (($fname =~ /\.(\w+)$/) &&
                   11231:                  (!defined(&Apache::loncommon::fileembstyle($1)))) {
1.987     raeburn  11232:             $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
1.661     raeburn  11233:             next;
                   11234:         } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
1.1120    bisitz   11235:             $output .= &mt('Filename not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2).'<br />';
1.661     raeburn  11236:             next;
                   11237:         }
                   11238:         $env{'form.embedded_item_'.$i.'.filename'}=$fname;
1.1123    raeburn  11239:         my $subdir = $path;
                   11240:         $subdir =~ s{/+$}{};
1.661     raeburn  11241:         if ($context eq 'portfolio') {
1.984     raeburn  11242:             my $result;
                   11243:             if ($state eq 'existingfile') {
                   11244:                 $result=
                   11245:                     &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
1.1123    raeburn  11246:                                                     $dirpath.$env{'form.currentpath'}.$subdir);
1.661     raeburn  11247:             } else {
1.984     raeburn  11248:                 $result=
                   11249:                     &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
1.987     raeburn  11250:                                                     $dirpath.
1.1123    raeburn  11251:                                                     $env{'form.currentpath'}.$subdir);
1.984     raeburn  11252:                 if ($result !~ m|^/uploaded/|) {
                   11253:                     $output .= '<span class="LC_error">'
                   11254:                                .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
                   11255:                                ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
                   11256:                                .'</span><br />';
                   11257:                     next;
                   11258:                 } else {
1.987     raeburn  11259:                     $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   11260:                                $path.$fname.'</span>').'<br />';     
1.984     raeburn  11261:                 }
1.661     raeburn  11262:             }
1.1123    raeburn  11263:         } elsif (($context eq 'coursedoc') || ($context eq 'syllabus')) {
1.1126    raeburn  11264:             my $extendedsubdir = $dirpath.'/'.$subdir;
                   11265:             $extendedsubdir =~ s{/+$}{};
1.987     raeburn  11266:             my $result =
1.1126    raeburn  11267:                 &Apache::lonnet::userfileupload('embedded_item_'.$i,$context,$extendedsubdir);
1.987     raeburn  11268:             if ($result !~ m|^/uploaded/|) {
                   11269:                 $output .= '<span class="LC_error">'
                   11270:                            .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
                   11271:                            ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
                   11272:                            .'</span><br />';
                   11273:                     next;
                   11274:             } else {
                   11275:                 $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   11276:                            $path.$fname.'</span>').'<br />';
1.1125    raeburn  11277:                 if ($context eq 'syllabus') {
                   11278:                     &Apache::lonnet::make_public_indefinitely($result);
                   11279:                 }
1.987     raeburn  11280:             }
1.661     raeburn  11281:         } else {
                   11282: # Save the file
                   11283:             my $target = $env{'form.embedded_item_'.$i};
                   11284:             my $fullpath = $dir_root.$dirpath.'/'.$path;
                   11285:             my $dest = $fullpath.$fname;
                   11286:             my $url = $url_root.$dirpath.'/'.$path.$fname;
1.1027    raeburn  11287:             my @parts=split(/\//,"$dirpath/$path");
1.661     raeburn  11288:             my $count;
                   11289:             my $filepath = $dir_root;
1.1027    raeburn  11290:             foreach my $subdir (@parts) {
                   11291:                 $filepath .= "/$subdir";
                   11292:                 if (!-e $filepath) {
1.661     raeburn  11293:                     mkdir($filepath,0770);
                   11294:                 }
                   11295:             }
                   11296:             my $fh;
                   11297:             if (!open($fh,'>'.$dest)) {
                   11298:                 &Apache::lonnet::logthis('Failed to create '.$dest);
                   11299:                 $output .= '<span class="LC_error">'.
1.1071    raeburn  11300:                            &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
                   11301:                                $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661     raeburn  11302:                            '</span><br />';
                   11303:             } else {
                   11304:                 if (!print $fh $env{'form.embedded_item_'.$i}) {
                   11305:                     &Apache::lonnet::logthis('Failed to write to '.$dest);
                   11306:                     $output .= '<span class="LC_error">'.
1.1071    raeburn  11307:                               &mt('An error occurred while writing the file [_1] for embedded element [_2].',
                   11308:                                   $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661     raeburn  11309:                               '</span><br />';
                   11310:                 } else {
1.987     raeburn  11311:                     $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   11312:                                $url.'</span>').'<br />';
                   11313:                     unless ($context eq 'testbank') {
                   11314:                         $footer .= &mt('View embedded file: [_1]',
                   11315:                                        '<a href="'.$url.'">'.$fname.'</a>').'<br />';
                   11316:                     }
                   11317:                 }
                   11318:                 close($fh);
                   11319:             }
                   11320:         }
                   11321:         if ($env{'form.embedded_ref_'.$i}) {
                   11322:             $pathchange{$i} = 1;
                   11323:         }
                   11324:     }
                   11325:     if ($output) {
                   11326:         $output = '<p>'.$output.'</p>';
                   11327:     }
                   11328:     $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
                   11329:     $returnflag = 'ok';
1.1071    raeburn  11330:     my $numpathchgs = scalar(keys(%pathchange));
                   11331:     if ($numpathchgs > 0) {
1.987     raeburn  11332:         if ($context eq 'portfolio') {
                   11333:             $output .= '<p>'.&mt('or').'</p>';
                   11334:         } elsif ($context eq 'testbank') {
1.1071    raeburn  11335:             $output .=  '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
                   11336:                                   '<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
1.987     raeburn  11337:             $returnflag = 'modify_orightml';
                   11338:         }
                   11339:     }
1.1071    raeburn  11340:     return ($output.$footer,$returnflag,$numpathchgs);
1.987     raeburn  11341: }
                   11342: 
                   11343: sub modify_html_form {
                   11344:     my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
                   11345:     my $end = 0;
                   11346:     my $modifyform;
                   11347:     if ($context eq 'upload_embedded') {
                   11348:         return unless (ref($pathchange) eq 'HASH');
                   11349:         if ($env{'form.number_embedded_items'}) {
                   11350:             $end += $env{'form.number_embedded_items'};
                   11351:         }
                   11352:         if ($env{'form.number_pathchange_items'}) {
                   11353:             $end += $env{'form.number_pathchange_items'};
                   11354:         }
                   11355:         if ($end) {
                   11356:             for (my $i=0; $i<$end; $i++) {
                   11357:                 if ($i < $env{'form.number_embedded_items'}) {
                   11358:                     next unless($pathchange->{$i});
                   11359:                 }
                   11360:                 $modifyform .=
                   11361:                     &start_data_table_row().
                   11362:                     '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
                   11363:                     'checked="checked" /></td>'.
                   11364:                     '<td>'.$env{'form.embedded_ref_'.$i}.
                   11365:                     '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
                   11366:                     &escape($env{'form.embedded_ref_'.$i}).'" />'.
                   11367:                     '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
                   11368:                     &escape($env{'form.embedded_codebase_'.$i}).'" />'.
                   11369:                     '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
                   11370:                     &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
                   11371:                     '<td>'.$env{'form.embedded_orig_'.$i}.
                   11372:                     '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
                   11373:                     &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
                   11374:                     &end_data_table_row();
1.1071    raeburn  11375:             }
1.987     raeburn  11376:         }
                   11377:     } else {
                   11378:         $modifyform = $pathchgtable;
                   11379:         if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
                   11380:             $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
                   11381:         } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
                   11382:             $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
                   11383:         }
                   11384:     }
                   11385:     if ($modifyform) {
1.1071    raeburn  11386:         if ($actionurl eq '/adm/dependencies') {
                   11387:             $hiddenstate .= '<input type="hidden" name="action" value="modifyhrefs" />';
                   11388:         }
1.987     raeburn  11389:         return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
                   11390:                '<p>'.&mt('Changes need to be made to the reference(s) used for one or more of the dependencies, if your HTML file is to work correctly:').'<ol>'."\n".
                   11391:                '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
                   11392:                '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
                   11393:                '</ol></p>'."\n".'<p>'.
                   11394:                &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
                   11395:                '<form method="post" name="refchanger" action="'.$actionurl.'">'.
                   11396:                &start_data_table()."\n".
                   11397:                &start_data_table_header_row().
                   11398:                '<th>'.&mt('Change?').'</th>'.
                   11399:                '<th>'.&mt('Current reference').'</th>'.
                   11400:                '<th>'.&mt('Required reference').'</th>'.
                   11401:                &end_data_table_header_row()."\n".
                   11402:                $modifyform.
                   11403:                &end_data_table().'<br />'."\n".$hiddenstate.
                   11404:                '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
                   11405:                '</form>'."\n";
                   11406:     }
                   11407:     return;
                   11408: }
                   11409: 
                   11410: sub modify_html_refs {
1.1123    raeburn  11411:     my ($context,$dirpath,$uname,$udom,$dir_root,$url) = @_;
1.987     raeburn  11412:     my $container;
                   11413:     if ($context eq 'portfolio') {
                   11414:         $container = $env{'form.container'};
                   11415:     } elsif ($context eq 'coursedoc') {
                   11416:         $container = $env{'form.primaryurl'};
1.1071    raeburn  11417:     } elsif ($context eq 'manage_dependencies') {
                   11418:         (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
                   11419:         $container = "/$container";
1.1123    raeburn  11420:     } elsif ($context eq 'syllabus') {
                   11421:         $container = $url;
1.987     raeburn  11422:     } else {
1.1027    raeburn  11423:         $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
1.987     raeburn  11424:     }
                   11425:     my (%allfiles,%codebase,$output,$content);
                   11426:     my @changes = &get_env_multiple('form.namechange');
1.1126    raeburn  11427:     unless ((@changes > 0) || ($context eq 'syllabus')) {
1.1071    raeburn  11428:         if (wantarray) {
                   11429:             return ('',0,0); 
                   11430:         } else {
                   11431:             return;
                   11432:         }
                   11433:     }
                   11434:     if (($context eq 'portfolio') || ($context eq 'coursedoc') || 
1.1123    raeburn  11435:         ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.1071    raeburn  11436:         unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
                   11437:             if (wantarray) {
                   11438:                 return ('',0,0);
                   11439:             } else {
                   11440:                 return;
                   11441:             }
                   11442:         } 
1.987     raeburn  11443:         $content = &Apache::lonnet::getfile($container);
1.1071    raeburn  11444:         if ($content eq '-1') {
                   11445:             if (wantarray) {
                   11446:                 return ('',0,0);
                   11447:             } else {
                   11448:                 return;
                   11449:             }
                   11450:         }
1.987     raeburn  11451:     } else {
1.1071    raeburn  11452:         unless ($container =~ /^\Q$dir_root\E/) {
                   11453:             if (wantarray) {
                   11454:                 return ('',0,0);
                   11455:             } else {
                   11456:                 return;
                   11457:             }
                   11458:         } 
1.987     raeburn  11459:         if (open(my $fh,"<$container")) {
                   11460:             $content = join('', <$fh>);
                   11461:             close($fh);
                   11462:         } else {
1.1071    raeburn  11463:             if (wantarray) {
                   11464:                 return ('',0,0);
                   11465:             } else {
                   11466:                 return;
                   11467:             }
1.987     raeburn  11468:         }
                   11469:     }
                   11470:     my ($count,$codebasecount) = (0,0);
                   11471:     my $mm = new File::MMagic;
                   11472:     my $mime_type = $mm->checktype_contents($content);
                   11473:     if ($mime_type eq 'text/html') {
                   11474:         my $parse_result = 
                   11475:             &Apache::lonnet::extract_embedded_items($container,\%allfiles,
                   11476:                                                     \%codebase,\$content);
                   11477:         if ($parse_result eq 'ok') {
                   11478:             foreach my $i (@changes) {
                   11479:                 my $orig = &unescape($env{'form.embedded_orig_'.$i});
                   11480:                 my $ref = &unescape($env{'form.embedded_ref_'.$i});
                   11481:                 if ($allfiles{$ref}) {
                   11482:                     my $newname =  $orig;
                   11483:                     my ($attrib_regexp,$codebase);
1.1006    raeburn  11484:                     $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
1.987     raeburn  11485:                     if ($attrib_regexp =~ /:/) {
                   11486:                         $attrib_regexp =~ s/\:/|/g;
                   11487:                     }
                   11488:                     if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
                   11489:                         my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
                   11490:                         $count += $numchg;
1.1123    raeburn  11491:                         $allfiles{$newname} = $allfiles{$ref};
1.1148    raeburn  11492:                         delete($allfiles{$ref});
1.987     raeburn  11493:                     }
                   11494:                     if ($env{'form.embedded_codebase_'.$i} ne '') {
1.1006    raeburn  11495:                         $codebase = &unescape($env{'form.embedded_codebase_'.$i});
1.987     raeburn  11496:                         my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
                   11497:                         $codebasecount ++;
                   11498:                     }
                   11499:                 }
                   11500:             }
1.1123    raeburn  11501:             my $skiprewrites;
1.987     raeburn  11502:             if ($count || $codebasecount) {
                   11503:                 my $saveresult;
1.1071    raeburn  11504:                 if (($context eq 'portfolio') || ($context eq 'coursedoc') || 
1.1123    raeburn  11505:                     ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.987     raeburn  11506:                     my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
                   11507:                     if ($url eq $container) {
                   11508:                         my ($fname) = ($container =~ m{/([^/]+)$});
                   11509:                         $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
                   11510:                                             $count,'<span class="LC_filename">'.
1.1071    raeburn  11511:                                             $fname.'</span>').'</p>';
1.987     raeburn  11512:                     } else {
                   11513:                          $output = '<p class="LC_error">'.
                   11514:                                    &mt('Error: update failed for: [_1].',
                   11515:                                    '<span class="LC_filename">'.
                   11516:                                    $container.'</span>').'</p>';
                   11517:                     }
1.1123    raeburn  11518:                     if ($context eq 'syllabus') {
                   11519:                         unless ($saveresult eq 'ok') {
                   11520:                             $skiprewrites = 1;
                   11521:                         }
                   11522:                     }
1.987     raeburn  11523:                 } else {
                   11524:                     if (open(my $fh,">$container")) {
                   11525:                         print $fh $content;
                   11526:                         close($fh);
                   11527:                         $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
                   11528:                                   $count,'<span class="LC_filename">'.
                   11529:                                   $container.'</span>').'</p>';
1.661     raeburn  11530:                     } else {
1.987     raeburn  11531:                          $output = '<p class="LC_error">'.
                   11532:                                    &mt('Error: could not update [_1].',
                   11533:                                    '<span class="LC_filename">'.
                   11534:                                    $container.'</span>').'</p>';
1.661     raeburn  11535:                     }
                   11536:                 }
                   11537:             }
1.1123    raeburn  11538:             if (($context eq 'syllabus') && (!$skiprewrites)) {
                   11539:                 my ($actionurl,$state);
                   11540:                 $actionurl = "/public/$udom/$uname/syllabus";
                   11541:                 my ($ignore,$num,$numpathchanges,$existing,$mapping) =
                   11542:                     &ask_for_embedded_content($actionurl,$state,\%allfiles,
                   11543:                                               \%codebase,
                   11544:                                               {'context' => 'rewrites',
                   11545:                                                'ignore_remote_references' => 1,});
                   11546:                 if (ref($mapping) eq 'HASH') {
                   11547:                     my $rewrites = 0;
                   11548:                     foreach my $key (keys(%{$mapping})) {
                   11549:                         next if ($key =~ m{^https?://});
                   11550:                         my $ref = $mapping->{$key};
                   11551:                         my $newname = "/uploaded/$udom/$uname/portfolio/syllabus/$key";
                   11552:                         my $attrib;
                   11553:                         if (ref($allfiles{$mapping->{$key}}) eq 'ARRAY') {
                   11554:                             $attrib = join('|',@{$allfiles{$mapping->{$key}}});
                   11555:                         }
                   11556:                         if ($content =~ m{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
                   11557:                             my $numchg = ($content =~ s{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
                   11558:                             $rewrites += $numchg;
                   11559:                         }
                   11560:                     }
                   11561:                     if ($rewrites) {
                   11562:                         my $saveresult; 
                   11563:                         my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
                   11564:                         if ($url eq $container) {
                   11565:                             my ($fname) = ($container =~ m{/([^/]+)$});
                   11566:                             $output .= '<p>'.&mt('Rewrote [quant,_1,link] as [quant,_1,absolute link] in [_2].',
                   11567:                                             $count,'<span class="LC_filename">'.
                   11568:                                             $fname.'</span>').'</p>';
                   11569:                         } else {
                   11570:                             $output .= '<p class="LC_error">'.
                   11571:                                        &mt('Error: could not update links in [_1].',
                   11572:                                        '<span class="LC_filename">'.
                   11573:                                        $container.'</span>').'</p>';
                   11574: 
                   11575:                         }
                   11576:                     }
                   11577:                 }
                   11578:             }
1.987     raeburn  11579:         } else {
                   11580:             &logthis('Failed to parse '.$container.
                   11581:                      ' to modify references: '.$parse_result);
1.661     raeburn  11582:         }
                   11583:     }
1.1071    raeburn  11584:     if (wantarray) {
                   11585:         return ($output,$count,$codebasecount);
                   11586:     } else {
                   11587:         return $output;
                   11588:     }
1.661     raeburn  11589: }
                   11590: 
                   11591: sub check_for_existing {
                   11592:     my ($path,$fname,$element) = @_;
                   11593:     my ($state,$msg);
                   11594:     if (-d $path.'/'.$fname) {
                   11595:         $state = 'exists';
                   11596:         $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   11597:     } elsif (-e $path.'/'.$fname) {
                   11598:         $state = 'exists';
                   11599:         $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   11600:     }
                   11601:     if ($state eq 'exists') {
                   11602:         $msg = '<span class="LC_error">'.$msg.'</span><br />';
                   11603:     }
                   11604:     return ($state,$msg);
                   11605: }
                   11606: 
                   11607: sub check_for_upload {
                   11608:     my ($path,$fname,$group,$element,$portfolio_root,$port_path,
                   11609:         $disk_quota,$current_disk_usage,$uname,$udom) = @_;
1.985     raeburn  11610:     my $filesize = length($env{'form.'.$element});
                   11611:     if (!$filesize) {
                   11612:         my $msg = '<span class="LC_error">'.
                   11613:                   &mt('Unable to upload [_1]. (size = [_2] bytes)', 
                   11614:                       '<span class="LC_filename">'.$fname.'</span>',
                   11615:                       $filesize).'<br />'.
1.1007    raeburn  11616:                   &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').'<br />'.
1.985     raeburn  11617:                   '</span>';
                   11618:         return ('zero_bytes',$msg);
                   11619:     }
                   11620:     $filesize =  $filesize/1000; #express in k (1024?)
1.661     raeburn  11621:     my $getpropath = 1;
1.1021    raeburn  11622:     my ($dirlistref,$listerror) =
                   11623:          &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
1.661     raeburn  11624:     my $found_file = 0;
                   11625:     my $locked_file = 0;
1.991     raeburn  11626:     my @lockers;
                   11627:     my $navmap;
                   11628:     if ($env{'request.course.id'}) {
                   11629:         $navmap = Apache::lonnavmaps::navmap->new();
                   11630:     }
1.1021    raeburn  11631:     if (ref($dirlistref) eq 'ARRAY') {
                   11632:         foreach my $line (@{$dirlistref}) {
                   11633:             my ($file_name,$rest)=split(/\&/,$line,2);
                   11634:             if ($file_name eq $fname){
                   11635:                 $file_name = $path.$file_name;
                   11636:                 if ($group ne '') {
                   11637:                     $file_name = $group.$file_name;
                   11638:                 }
                   11639:                 $found_file = 1;
                   11640:                 if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
                   11641:                     foreach my $lock (@lockers) {
                   11642:                         if (ref($lock) eq 'ARRAY') {
                   11643:                             my ($symb,$crsid) = @{$lock};
                   11644:                             if ($crsid eq $env{'request.course.id'}) {
                   11645:                                 if (ref($navmap)) {
                   11646:                                     my $res = $navmap->getBySymb($symb);
                   11647:                                     foreach my $part (@{$res->parts()}) { 
                   11648:                                         my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
                   11649:                                         unless (($slot_status == $res->RESERVED) ||
                   11650:                                                 ($slot_status == $res->RESERVED_LOCATION)) {
                   11651:                                             $locked_file = 1;
                   11652:                                         }
1.991     raeburn  11653:                                     }
1.1021    raeburn  11654:                                 } else {
                   11655:                                     $locked_file = 1;
1.991     raeburn  11656:                                 }
                   11657:                             } else {
                   11658:                                 $locked_file = 1;
                   11659:                             }
                   11660:                         }
1.1021    raeburn  11661:                    }
                   11662:                 } else {
                   11663:                     my @info = split(/\&/,$rest);
                   11664:                     my $currsize = $info[6]/1000;
                   11665:                     if ($currsize < $filesize) {
                   11666:                         my $extra = $filesize - $currsize;
                   11667:                         if (($current_disk_usage + $extra) > $disk_quota) {
1.1179    bisitz   11668:                             my $msg = '<p class="LC_warning">'.
1.1021    raeburn  11669:                                       &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded if existing (smaller) file with same name (size = [_3] kilobytes) is replaced.',
1.1179    bisitz   11670:                                           '<span class="LC_filename">'.$fname.'</span>',$filesize,$currsize).'</p>'.
                   11671:                                       '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
                   11672:                                                    $disk_quota,$current_disk_usage).'</p>';
1.1021    raeburn  11673:                             return ('will_exceed_quota',$msg);
                   11674:                         }
1.984     raeburn  11675:                     }
                   11676:                 }
1.661     raeburn  11677:             }
                   11678:         }
                   11679:     }
                   11680:     if (($current_disk_usage + $filesize) > $disk_quota){
1.1179    bisitz   11681:         my $msg = '<p class="LC_warning">'.
                   11682:                 &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</p>'.
1.1184    raeburn  11683:                   '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage).'</p>';
1.661     raeburn  11684:         return ('will_exceed_quota',$msg);
                   11685:     } elsif ($found_file) {
                   11686:         if ($locked_file) {
1.1179    bisitz   11687:             my $msg = '<p class="LC_warning">';
1.661     raeburn  11688:             $msg .= &mt('Unable to upload [_1]. A locked file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>','<span class="LC_filename">'.$port_path.$env{'form.currentpath'}.'</span>');
1.1179    bisitz   11689:             $msg .= '</p>';
1.661     raeburn  11690:             $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
                   11691:             return ('file_locked',$msg);
                   11692:         } else {
1.1179    bisitz   11693:             my $msg = '<p class="LC_error">';
1.984     raeburn  11694:             $msg .= &mt(' A file by that name: [_1] was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$port_path.$env{'form.currentpath'});
1.1179    bisitz   11695:             $msg .= '</p>';
1.984     raeburn  11696:             return ('existingfile',$msg);
1.661     raeburn  11697:         }
                   11698:     }
                   11699: }
                   11700: 
1.987     raeburn  11701: sub check_for_traversal {
                   11702:     my ($path,$url,$toplevel) = @_;
                   11703:     my @parts=split(/\//,$path);
                   11704:     my $cleanpath;
                   11705:     my $fullpath = $url;
                   11706:     for (my $i=0;$i<@parts;$i++) {
                   11707:         next if ($parts[$i] eq '.');
                   11708:         if ($parts[$i] eq '..') {
                   11709:             $fullpath =~ s{([^/]+/)$}{};
                   11710:         } else {
                   11711:             $fullpath .= $parts[$i].'/';
                   11712:         }
                   11713:     }
                   11714:     if ($fullpath =~ /^\Q$url\E(.*)$/) {
                   11715:         $cleanpath = $1;
                   11716:     } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
                   11717:         my $curr_toprel = $1;
                   11718:         my @parts = split(/\//,$curr_toprel);
                   11719:         my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
                   11720:         my @urlparts = split(/\//,$url_toprel);
                   11721:         my $doubledots;
                   11722:         my $startdiff = -1;
                   11723:         for (my $i=0; $i<@urlparts; $i++) {
                   11724:             if ($startdiff == -1) {
                   11725:                 unless ($urlparts[$i] eq $parts[$i]) {
                   11726:                     $startdiff = $i;
                   11727:                     $doubledots .= '../';
                   11728:                 }
                   11729:             } else {
                   11730:                 $doubledots .= '../';
                   11731:             }
                   11732:         }
                   11733:         if ($startdiff > -1) {
                   11734:             $cleanpath = $doubledots;
                   11735:             for (my $i=$startdiff; $i<@parts; $i++) {
                   11736:                 $cleanpath .= $parts[$i].'/';
                   11737:             }
                   11738:         }
                   11739:     }
                   11740:     $cleanpath =~ s{(/)$}{};
                   11741:     return $cleanpath;
                   11742: }
1.31      albertel 11743: 
1.1053    raeburn  11744: sub is_archive_file {
                   11745:     my ($mimetype) = @_;
                   11746:     if (($mimetype eq 'application/octet-stream') ||
                   11747:         ($mimetype eq 'application/x-stuffit') ||
                   11748:         ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
                   11749:         return 1;
                   11750:     }
                   11751:     return;
                   11752: }
                   11753: 
                   11754: sub decompress_form {
1.1065    raeburn  11755:     my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
1.1053    raeburn  11756:     my %lt = &Apache::lonlocal::texthash (
                   11757:         this => 'This file is an archive file.',
1.1067    raeburn  11758:         camt => 'This file is a Camtasia archive file.',
1.1065    raeburn  11759:         itsc => 'Its contents are as follows:',
1.1053    raeburn  11760:         youm => 'You may wish to extract its contents.',
                   11761:         extr => 'Extract contents',
1.1067    raeburn  11762:         auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
                   11763:         proa => 'Process automatically?',
1.1053    raeburn  11764:         yes  => 'Yes',
                   11765:         no   => 'No',
1.1067    raeburn  11766:         fold => 'Title for folder containing movie',
                   11767:         movi => 'Title for page containing embedded movie', 
1.1053    raeburn  11768:     );
1.1065    raeburn  11769:     my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
1.1067    raeburn  11770:     my ($is_camtasia,$topdir,%toplevel,@paths);
1.1065    raeburn  11771:     my $info = &list_archive_contents($fileloc,\@paths);
                   11772:     if (@paths) {
                   11773:         foreach my $path (@paths) {
                   11774:             $path =~ s{^/}{};
1.1067    raeburn  11775:             if ($path =~ m{^([^/]+)/$}) {
                   11776:                 $topdir = $1;
                   11777:             }
1.1065    raeburn  11778:             if ($path =~ m{^([^/]+)/}) {
                   11779:                 $toplevel{$1} = $path;
                   11780:             } else {
                   11781:                 $toplevel{$path} = $path;
                   11782:             }
                   11783:         }
                   11784:     }
1.1067    raeburn  11785:     if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
1.1164    raeburn  11786:         my @camtasia6 = ("$topdir/","$topdir/index.html",
1.1067    raeburn  11787:                         "$topdir/media/",
                   11788:                         "$topdir/media/$topdir.mp4",
                   11789:                         "$topdir/media/FirstFrame.png",
                   11790:                         "$topdir/media/player.swf",
                   11791:                         "$topdir/media/swfobject.js",
                   11792:                         "$topdir/media/expressInstall.swf");
1.1197    raeburn  11793:         my @camtasia8_1 = ("$topdir/","$topdir/$topdir.html",
1.1164    raeburn  11794:                          "$topdir/$topdir.mp4",
                   11795:                          "$topdir/$topdir\_config.xml",
                   11796:                          "$topdir/$topdir\_controller.swf",
                   11797:                          "$topdir/$topdir\_embed.css",
                   11798:                          "$topdir/$topdir\_First_Frame.png",
                   11799:                          "$topdir/$topdir\_player.html",
                   11800:                          "$topdir/$topdir\_Thumbnails.png",
                   11801:                          "$topdir/playerProductInstall.swf",
                   11802:                          "$topdir/scripts/",
                   11803:                          "$topdir/scripts/config_xml.js",
                   11804:                          "$topdir/scripts/handlebars.js",
                   11805:                          "$topdir/scripts/jquery-1.7.1.min.js",
                   11806:                          "$topdir/scripts/jquery-ui-1.8.15.custom.min.js",
                   11807:                          "$topdir/scripts/modernizr.js",
                   11808:                          "$topdir/scripts/player-min.js",
                   11809:                          "$topdir/scripts/swfobject.js",
                   11810:                          "$topdir/skins/",
                   11811:                          "$topdir/skins/configuration_express.xml",
                   11812:                          "$topdir/skins/express_show/",
                   11813:                          "$topdir/skins/express_show/player-min.css",
                   11814:                          "$topdir/skins/express_show/spritesheet.png");
1.1197    raeburn  11815:         my @camtasia8_4 = ("$topdir/","$topdir/$topdir.html",
                   11816:                          "$topdir/$topdir.mp4",
                   11817:                          "$topdir/$topdir\_config.xml",
                   11818:                          "$topdir/$topdir\_controller.swf",
                   11819:                          "$topdir/$topdir\_embed.css",
                   11820:                          "$topdir/$topdir\_First_Frame.png",
                   11821:                          "$topdir/$topdir\_player.html",
                   11822:                          "$topdir/$topdir\_Thumbnails.png",
                   11823:                          "$topdir/playerProductInstall.swf",
                   11824:                          "$topdir/scripts/",
                   11825:                          "$topdir/scripts/config_xml.js",
                   11826:                          "$topdir/scripts/techsmith-smart-player.min.js",
                   11827:                          "$topdir/skins/",
                   11828:                          "$topdir/skins/configuration_express.xml",
                   11829:                          "$topdir/skins/express_show/",
                   11830:                          "$topdir/skins/express_show/spritesheet.min.css",
                   11831:                          "$topdir/skins/express_show/spritesheet.png",
                   11832:                          "$topdir/skins/express_show/techsmith-smart-player.min.css");
1.1164    raeburn  11833:         my @diffs = &compare_arrays(\@paths,\@camtasia6);
1.1067    raeburn  11834:         if (@diffs == 0) {
1.1164    raeburn  11835:             $is_camtasia = 6;
                   11836:         } else {
1.1197    raeburn  11837:             @diffs = &compare_arrays(\@paths,\@camtasia8_1);
1.1164    raeburn  11838:             if (@diffs == 0) {
                   11839:                 $is_camtasia = 8;
1.1197    raeburn  11840:             } else {
                   11841:                 @diffs = &compare_arrays(\@paths,\@camtasia8_4);
                   11842:                 if (@diffs == 0) {
                   11843:                     $is_camtasia = 8;
                   11844:                 }
1.1164    raeburn  11845:             }
1.1067    raeburn  11846:         }
                   11847:     }
                   11848:     my $output;
                   11849:     if ($is_camtasia) {
                   11850:         $output = <<"ENDCAM";
                   11851: <script type="text/javascript" language="Javascript">
                   11852: // <![CDATA[
                   11853: 
                   11854: function camtasiaToggle() {
                   11855:     for (var i=0; i<document.uploaded_decompress.autoextract_camtasia.length; i++) {
                   11856:         if (document.uploaded_decompress.autoextract_camtasia[i].checked) {
1.1164    raeburn  11857:             if (document.uploaded_decompress.autoextract_camtasia[i].value == $is_camtasia) {
1.1067    raeburn  11858:                 document.getElementById('camtasia_titles').style.display='block';
                   11859:             } else {
                   11860:                 document.getElementById('camtasia_titles').style.display='none';
                   11861:             }
                   11862:         }
                   11863:     }
                   11864:     return;
                   11865: }
                   11866: 
                   11867: // ]]>
                   11868: </script>
                   11869: <p>$lt{'camt'}</p>
                   11870: ENDCAM
1.1065    raeburn  11871:     } else {
1.1067    raeburn  11872:         $output = '<p>'.$lt{'this'};
                   11873:         if ($info eq '') {
                   11874:             $output .= ' '.$lt{'youm'}.'</p>'."\n";
                   11875:         } else {
                   11876:             $output .= ' '.$lt{'itsc'}.'</p>'."\n".
                   11877:                        '<div><pre>'.$info.'</pre></div>';
                   11878:         }
1.1065    raeburn  11879:     }
1.1067    raeburn  11880:     $output .= '<form name="uploaded_decompress" action="'.$action.'" method="post">'."\n";
1.1065    raeburn  11881:     my $duplicates;
                   11882:     my $num = 0;
                   11883:     if (ref($dirlist) eq 'ARRAY') {
                   11884:         foreach my $item (@{$dirlist}) {
                   11885:             if (ref($item) eq 'ARRAY') {
                   11886:                 if (exists($toplevel{$item->[0]})) {
                   11887:                     $duplicates .= 
                   11888:                         &start_data_table_row().
                   11889:                         '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
                   11890:                         'value="0" checked="checked" />'.&mt('No').'</label>'.
                   11891:                         '&nbsp;<label><input type="radio" name="archive_overwrite_'.$num.'" '.
                   11892:                         'value="1" />'.&mt('Yes').'</label>'.
                   11893:                         '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
                   11894:                         '<td>'.$item->[0].'</td>';
                   11895:                     if ($item->[2]) {
                   11896:                         $duplicates .= '<td>'.&mt('Directory').'</td>';
                   11897:                     } else {
                   11898:                         $duplicates .= '<td>'.&mt('File').'</td>';
                   11899:                     }
                   11900:                     $duplicates .= '<td>'.$item->[3].'</td>'.
                   11901:                                    '<td>'.
                   11902:                                    &Apache::lonlocal::locallocaltime($item->[4]).
                   11903:                                    '</td>'.
                   11904:                                    &end_data_table_row();
                   11905:                     $num ++;
                   11906:                 }
                   11907:             }
                   11908:         }
                   11909:     }
                   11910:     my $itemcount;
                   11911:     if (@paths > 0) {
                   11912:         $itemcount = scalar(@paths);
                   11913:     } else {
                   11914:         $itemcount = 1;
                   11915:     }
1.1067    raeburn  11916:     if ($is_camtasia) {
                   11917:         $output .= $lt{'auto'}.'<br />'.
                   11918:                    '<span class="LC_nobreak">'.$lt{'proa'}.'<label>'.
1.1164    raeburn  11919:                    '<input type="radio" name="autoextract_camtasia" value="'.$is_camtasia.'" onclick="javascript:camtasiaToggle();" checked="checked" />'.
1.1067    raeburn  11920:                    $lt{'yes'}.'</label>&nbsp;<label>'.
                   11921:                    '<input type="radio" name="autoextract_camtasia" value="0" onclick="javascript:camtasiaToggle();" />'.
                   11922:                    $lt{'no'}.'</label></span><br />'.
                   11923:                    '<div id="camtasia_titles" style="display:block">'.
                   11924:                    &Apache::lonhtmlcommon::start_pick_box().
                   11925:                    &Apache::lonhtmlcommon::row_title($lt{'fold'}).
                   11926:                    '<input type="textbox" name="camtasia_foldername" value="'.$env{'form.comment'}.'" />'."\n".
                   11927:                    &Apache::lonhtmlcommon::row_closure().
                   11928:                    &Apache::lonhtmlcommon::row_title($lt{'movi'}).
                   11929:                    '<input type="textbox" name="camtasia_moviename" value="" />'."\n".
                   11930:                    &Apache::lonhtmlcommon::row_closure(1).
                   11931:                    &Apache::lonhtmlcommon::end_pick_box().
                   11932:                    '</div>';
                   11933:     }
1.1065    raeburn  11934:     $output .= 
                   11935:         '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
1.1067    raeburn  11936:         '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'.
                   11937:         "\n";
1.1065    raeburn  11938:     if ($duplicates ne '') {
                   11939:         $output .= '<p><span class="LC_warning">'.
                   11940:                    &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.  
                   11941:                    &start_data_table().
                   11942:                    &start_data_table_header_row().
                   11943:                    '<th>'.&mt('Overwrite?').'</th>'.
                   11944:                    '<th>'.&mt('Name').'</th>'.
                   11945:                    '<th>'.&mt('Type').'</th>'.
                   11946:                    '<th>'.&mt('Size').'</th>'.
                   11947:                    '<th>'.&mt('Last modified').'</th>'.
                   11948:                    &end_data_table_header_row().
                   11949:                    $duplicates.
                   11950:                    &end_data_table().
                   11951:                    '</p>';
                   11952:     }
1.1067    raeburn  11953:     $output .= '<input type="hidden" name="archiveurl" value="'.$archiveurl.'" />'."\n";
1.1053    raeburn  11954:     if (ref($hiddenelements) eq 'HASH') {
                   11955:         foreach my $hidden (sort(keys(%{$hiddenelements}))) {
                   11956:             $output .= '<input type="hidden" name="'.$hidden.'" value="'.$hiddenelements->{$hidden}.'" />'."\n";
                   11957:         }
                   11958:     }
                   11959:     $output .= <<"END";
1.1067    raeburn  11960: <br />
1.1053    raeburn  11961: <input type="submit" name="decompress" value="$lt{'extr'}" />
                   11962: </form>
                   11963: $noextract
                   11964: END
                   11965:     return $output;
                   11966: }
                   11967: 
1.1065    raeburn  11968: sub decompression_utility {
                   11969:     my ($program) = @_;
                   11970:     my @utilities = ('tar','gunzip','bunzip2','unzip'); 
                   11971:     my $location;
                   11972:     if (grep(/^\Q$program\E$/,@utilities)) { 
                   11973:         foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
                   11974:                          '/usr/sbin/') {
                   11975:             if (-x $dir.$program) {
                   11976:                 $location = $dir.$program;
                   11977:                 last;
                   11978:             }
                   11979:         }
                   11980:     }
                   11981:     return $location;
                   11982: }
                   11983: 
                   11984: sub list_archive_contents {
                   11985:     my ($file,$pathsref) = @_;
                   11986:     my (@cmd,$output);
                   11987:     my $needsregexp;
                   11988:     if ($file =~ /\.zip$/) {
                   11989:         @cmd = (&decompression_utility('unzip'),"-l");
                   11990:         $needsregexp = 1;
                   11991:     } elsif (($file =~ m/\.tar\.gz$/) ||
                   11992:              ($file =~ /\.tgz$/)) {
                   11993:         @cmd = (&decompression_utility('tar'),"-ztf");
                   11994:     } elsif ($file =~ /\.tar\.bz2$/) {
                   11995:         @cmd = (&decompression_utility('tar'),"-jtf");
                   11996:     } elsif ($file =~ m|\.tar$|) {
                   11997:         @cmd = (&decompression_utility('tar'),"-tf");
                   11998:     }
                   11999:     if (@cmd) {
                   12000:         undef($!);
                   12001:         undef($@);
                   12002:         if (open(my $fh,"-|", @cmd, $file)) {
                   12003:             while (my $line = <$fh>) {
                   12004:                 $output .= $line;
                   12005:                 chomp($line);
                   12006:                 my $item;
                   12007:                 if ($needsregexp) {
                   12008:                     ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/); 
                   12009:                 } else {
                   12010:                     $item = $line;
                   12011:                 }
                   12012:                 if ($item ne '') {
                   12013:                     unless (grep(/^\Q$item\E$/,@{$pathsref})) {
                   12014:                         push(@{$pathsref},$item);
                   12015:                     } 
                   12016:                 }
                   12017:             }
                   12018:             close($fh);
                   12019:         }
                   12020:     }
                   12021:     return $output;
                   12022: }
                   12023: 
1.1053    raeburn  12024: sub decompress_uploaded_file {
                   12025:     my ($file,$dir) = @_;
                   12026:     &Apache::lonnet::appenv({'cgi.file' => $file});
                   12027:     &Apache::lonnet::appenv({'cgi.dir' => $dir});
                   12028:     my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
                   12029:     my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
                   12030:     my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
                   12031:     &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
                   12032:     my $decompressed = $env{'cgi.decompressed'};
                   12033:     &Apache::lonnet::delenv('cgi.file');
                   12034:     &Apache::lonnet::delenv('cgi.dir');
                   12035:     &Apache::lonnet::delenv('cgi.decompressed');
                   12036:     return ($decompressed,$result);
                   12037: }
                   12038: 
1.1055    raeburn  12039: sub process_decompression {
                   12040:     my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
                   12041:     my ($dir,$error,$warning,$output);
1.1180    raeburn  12042:     if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/i) {
1.1120    bisitz   12043:         $error = &mt('Filename not a supported archive file type.').
                   12044:                  '<br />'.&mt('Filename should end with one of: [_1].',
1.1055    raeburn  12045:                               '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
                   12046:     } else {
                   12047:         my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
                   12048:         if ($docuhome eq 'no_host') {
                   12049:             $error = &mt('Could not determine home server for course.');
                   12050:         } else {
                   12051:             my @ids=&Apache::lonnet::current_machine_ids();
                   12052:             my $currdir = "$dir_root/$destination";
                   12053:             if (grep(/^\Q$docuhome\E$/,@ids)) {
                   12054:                 $dir = &LONCAPA::propath($docudom,$docuname).
                   12055:                        "$dir_root/$destination";
                   12056:             } else {
                   12057:                 $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
                   12058:                        "$dir_root/$docudom/$docuname/$destination";
                   12059:                 unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
                   12060:                     $error = &mt('Archive file not found.');
                   12061:                 }
                   12062:             }
1.1065    raeburn  12063:             my (@to_overwrite,@to_skip);
                   12064:             if ($env{'form.archive_overwrite_total'} > 0) {
                   12065:                 my $total = $env{'form.archive_overwrite_total'};
                   12066:                 for (my $i=0; $i<$total; $i++) {
                   12067:                     if ($env{'form.archive_overwrite_'.$i} == 1) {
                   12068:                         push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
                   12069:                     } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
                   12070:                         push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
                   12071:                     }
                   12072:                 }
                   12073:             }
                   12074:             my $numskip = scalar(@to_skip);
                   12075:             if (($numskip > 0) && 
                   12076:                 ($numskip == $env{'form.archive_itemcount'})) {
                   12077:                 $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');         
                   12078:             } elsif ($dir eq '') {
1.1055    raeburn  12079:                 $error = &mt('Directory containing archive file unavailable.');
                   12080:             } elsif (!$error) {
1.1065    raeburn  12081:                 my ($decompressed,$display);
                   12082:                 if ($numskip > 0) {
                   12083:                     my $tempdir = time.'_'.$$.int(rand(10000));
                   12084:                     mkdir("$dir/$tempdir",0755);
                   12085:                     system("mv $dir/$file $dir/$tempdir/$file");
                   12086:                     ($decompressed,$display) = 
                   12087:                         &decompress_uploaded_file($file,"$dir/$tempdir");
                   12088:                     foreach my $item (@to_skip) {
                   12089:                         if (($item ne '') && ($item !~ /\.\./)) {
                   12090:                             if (-f "$dir/$tempdir/$item") { 
                   12091:                                 unlink("$dir/$tempdir/$item");
                   12092:                             } elsif (-d "$dir/$tempdir/$item") {
                   12093:                                 system("rm -rf $dir/$tempdir/$item");
                   12094:                             }
                   12095:                         }
                   12096:                     }
                   12097:                     system("mv $dir/$tempdir/* $dir");
                   12098:                     rmdir("$dir/$tempdir");   
                   12099:                 } else {
                   12100:                     ($decompressed,$display) = 
                   12101:                         &decompress_uploaded_file($file,$dir);
                   12102:                 }
1.1055    raeburn  12103:                 if ($decompressed eq 'ok') {
1.1065    raeburn  12104:                     $output = '<p class="LC_info">'.
                   12105:                               &mt('Files extracted successfully from archive.').
                   12106:                               '</p>'."\n";
1.1055    raeburn  12107:                     my ($warning,$result,@contents);
                   12108:                     my ($newdirlistref,$newlisterror) =
                   12109:                         &Apache::lonnet::dirlist($currdir,$docudom,
                   12110:                                                  $docuname,1);
                   12111:                     my (%is_dir,%changes,@newitems);
                   12112:                     my $dirptr = 16384;
1.1065    raeburn  12113:                     if (ref($newdirlistref) eq 'ARRAY') {
1.1055    raeburn  12114:                         foreach my $dir_line (@{$newdirlistref}) {
                   12115:                             my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
1.1065    raeburn  12116:                             unless (($item =~ /^\.+$/) || ($item eq $file) || 
                   12117:                                     ((@to_skip > 0) && (grep(/^\Q$item\E$/,@to_skip)))) {
1.1055    raeburn  12118:                                 push(@newitems,$item);
                   12119:                                 if ($dirptr&$testdir) {
                   12120:                                     $is_dir{$item} = 1;
                   12121:                                 }
                   12122:                                 $changes{$item} = 1;
                   12123:                             }
                   12124:                         }
                   12125:                     }
                   12126:                     if (keys(%changes) > 0) {
                   12127:                         foreach my $item (sort(@newitems)) {
                   12128:                             if ($changes{$item}) {
                   12129:                                 push(@contents,$item);
                   12130:                             }
                   12131:                         }
                   12132:                     }
                   12133:                     if (@contents > 0) {
1.1067    raeburn  12134:                         my $wantform;
                   12135:                         unless ($env{'form.autoextract_camtasia'}) {
                   12136:                             $wantform = 1;
                   12137:                         }
1.1056    raeburn  12138:                         my (%children,%parent,%dirorder,%titles);
1.1055    raeburn  12139:                         my ($count,$datatable) = &get_extracted($docudom,$docuname,
                   12140:                                                                 $currdir,\%is_dir,
                   12141:                                                                 \%children,\%parent,
1.1056    raeburn  12142:                                                                 \@contents,\%dirorder,
                   12143:                                                                 \%titles,$wantform);
1.1055    raeburn  12144:                         if ($datatable ne '') {
                   12145:                             $output .= &archive_options_form('decompressed',$datatable,
                   12146:                                                              $count,$hiddenelem);
1.1065    raeburn  12147:                             my $startcount = 6;
1.1055    raeburn  12148:                             $output .= &archive_javascript($startcount,$count,
1.1056    raeburn  12149:                                                            \%titles,\%children);
1.1055    raeburn  12150:                         }
1.1067    raeburn  12151:                         if ($env{'form.autoextract_camtasia'}) {
1.1164    raeburn  12152:                             my $version = $env{'form.autoextract_camtasia'};
1.1067    raeburn  12153:                             my %displayed;
                   12154:                             my $total = 1;
                   12155:                             $env{'form.archive_directory'} = [];
                   12156:                             foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
                   12157:                                 my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
                   12158:                                 $path =~ s{/$}{};
                   12159:                                 my $item;
                   12160:                                 if ($path ne '') {
                   12161:                                     $item = "$path/$titles{$i}";
                   12162:                                 } else {
                   12163:                                     $item = $titles{$i};
                   12164:                                 }
                   12165:                                 $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
                   12166:                                 if ($item eq $contents[0]) {
                   12167:                                     push(@{$env{'form.archive_directory'}},$i);
                   12168:                                     $env{'form.archive_'.$i} = 'display';
                   12169:                                     $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
                   12170:                                     $displayed{'folder'} = $i;
1.1164    raeburn  12171:                                 } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) ||
                   12172:                                          (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) { 
1.1067    raeburn  12173:                                     $env{'form.archive_'.$i} = 'display';
                   12174:                                     $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
                   12175:                                     $displayed{'web'} = $i;
                   12176:                                 } else {
1.1164    raeburn  12177:                                     if ((($item eq "$contents[0]/media") && ($version == 6)) ||
                   12178:                                         ((($item eq "$contents[0]/scripts") || ($item eq "$contents[0]/skins") ||
                   12179:                                              ($item eq "$contents[0]/skins/express_show")) && ($version == 8))) {
1.1067    raeburn  12180:                                         push(@{$env{'form.archive_directory'}},$i);
                   12181:                                     }
                   12182:                                     $env{'form.archive_'.$i} = 'dependency';
                   12183:                                 }
                   12184:                                 $total ++;
                   12185:                             }
                   12186:                             for (my $i=1; $i<$total; $i++) {
                   12187:                                 next if ($i == $displayed{'web'});
                   12188:                                 next if ($i == $displayed{'folder'});
                   12189:                                 $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
                   12190:                             }
                   12191:                             $env{'form.phase'} = 'decompress_cleanup';
                   12192:                             $env{'form.archivedelete'} = 1;
                   12193:                             $env{'form.archive_count'} = $total-1;
                   12194:                             $output .=
                   12195:                                 &process_extracted_files('coursedocs',$docudom,
                   12196:                                                          $docuname,$destination,
                   12197:                                                          $dir_root,$hiddenelem);
                   12198:                         }
1.1055    raeburn  12199:                     } else {
                   12200:                         $warning = &mt('No new items extracted from archive file.');
                   12201:                     }
                   12202:                 } else {
                   12203:                     $output = $display;
                   12204:                     $error = &mt('An error occurred during extraction from the archive file.');
                   12205:                 }
                   12206:             }
                   12207:         }
                   12208:     }
                   12209:     if ($error) {
                   12210:         $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   12211:                    $error.'</p>'."\n";
                   12212:     }
                   12213:     if ($warning) {
                   12214:         $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
                   12215:     }
                   12216:     return $output;
                   12217: }
                   12218: 
                   12219: sub get_extracted {
1.1056    raeburn  12220:     my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
                   12221:         $titles,$wantform) = @_;
1.1055    raeburn  12222:     my $count = 0;
                   12223:     my $depth = 0;
                   12224:     my $datatable;
1.1056    raeburn  12225:     my @hierarchy;
1.1055    raeburn  12226:     return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
1.1056    raeburn  12227:                    (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
                   12228:                    (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
1.1055    raeburn  12229:     foreach my $item (@{$contents}) {
                   12230:         $count ++;
1.1056    raeburn  12231:         @{$dirorder->{$count}} = @hierarchy;
                   12232:         $titles->{$count} = $item;
1.1055    raeburn  12233:         &archive_hierarchy($depth,$count,$parent,$children);
                   12234:         if ($wantform) {
                   12235:             $datatable .= &archive_row($is_dir->{$item},$item,
                   12236:                                        $currdir,$depth,$count);
                   12237:         }
                   12238:         if ($is_dir->{$item}) {
                   12239:             $depth ++;
1.1056    raeburn  12240:             push(@hierarchy,$count);
                   12241:             $parent->{$depth} = $count;
1.1055    raeburn  12242:             $datatable .=
                   12243:                 &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
1.1056    raeburn  12244:                                            \$depth,\$count,\@hierarchy,$dirorder,
                   12245:                                            $children,$parent,$titles,$wantform);
1.1055    raeburn  12246:             $depth --;
1.1056    raeburn  12247:             pop(@hierarchy);
1.1055    raeburn  12248:         }
                   12249:     }
                   12250:     return ($count,$datatable);
                   12251: }
                   12252: 
                   12253: sub recurse_extracted_archive {
1.1056    raeburn  12254:     my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
                   12255:         $children,$parent,$titles,$wantform) = @_;
1.1055    raeburn  12256:     my $result='';
1.1056    raeburn  12257:     unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
                   12258:             (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
                   12259:             (ref($dirorder) eq 'HASH')) {
1.1055    raeburn  12260:         return $result;
                   12261:     }
                   12262:     my $dirptr = 16384;
                   12263:     my ($newdirlistref,$newlisterror) =
                   12264:         &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
                   12265:     if (ref($newdirlistref) eq 'ARRAY') {
                   12266:         foreach my $dir_line (@{$newdirlistref}) {
                   12267:             my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
                   12268:             unless ($item =~ /^\.+$/) {
                   12269:                 $$count ++;
1.1056    raeburn  12270:                 @{$dirorder->{$$count}} = @{$hierarchy};
                   12271:                 $titles->{$$count} = $item;
1.1055    raeburn  12272:                 &archive_hierarchy($$depth,$$count,$parent,$children);
1.1056    raeburn  12273: 
1.1055    raeburn  12274:                 my $is_dir;
                   12275:                 if ($dirptr&$testdir) {
                   12276:                     $is_dir = 1;
                   12277:                 }
                   12278:                 if ($wantform) {
                   12279:                     $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
                   12280:                 }
                   12281:                 if ($is_dir) {
                   12282:                     $$depth ++;
1.1056    raeburn  12283:                     push(@{$hierarchy},$$count);
                   12284:                     $parent->{$$depth} = $$count;
1.1055    raeburn  12285:                     $result .=
                   12286:                         &recurse_extracted_archive("$currdir/$item",$docudom,
                   12287:                                                    $docuname,$depth,$count,
1.1056    raeburn  12288:                                                    $hierarchy,$dirorder,$children,
                   12289:                                                    $parent,$titles,$wantform);
1.1055    raeburn  12290:                     $$depth --;
1.1056    raeburn  12291:                     pop(@{$hierarchy});
1.1055    raeburn  12292:                 }
                   12293:             }
                   12294:         }
                   12295:     }
                   12296:     return $result;
                   12297: }
                   12298: 
                   12299: sub archive_hierarchy {
                   12300:     my ($depth,$count,$parent,$children) =@_;
                   12301:     if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
                   12302:         if (exists($parent->{$depth})) {
                   12303:              $children->{$parent->{$depth}} .= $count.':';
                   12304:         }
                   12305:     }
                   12306:     return;
                   12307: }
                   12308: 
                   12309: sub archive_row {
                   12310:     my ($is_dir,$item,$currdir,$depth,$count) = @_;
                   12311:     my ($name) = ($item =~ m{([^/]+)$});
                   12312:     my %choices = &Apache::lonlocal::texthash (
1.1059    raeburn  12313:                                        'display'    => 'Add as file',
1.1055    raeburn  12314:                                        'dependency' => 'Include as dependency',
                   12315:                                        'discard'    => 'Discard',
                   12316:                                       );
                   12317:     if ($is_dir) {
1.1059    raeburn  12318:         $choices{'display'} = &mt('Add as folder'); 
1.1055    raeburn  12319:     }
1.1056    raeburn  12320:     my $output = &start_data_table_row().'<td align="right">'.$count.'</td>'."\n";
                   12321:     my $offset = 0;
1.1055    raeburn  12322:     foreach my $action ('display','dependency','discard') {
1.1056    raeburn  12323:         $offset ++;
1.1065    raeburn  12324:         if ($action ne 'display') {
                   12325:             $offset ++;
                   12326:         }  
1.1055    raeburn  12327:         $output .= '<td><span class="LC_nobreak">'.
                   12328:                    '<label><input type="radio" name="archive_'.$count.
                   12329:                    '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
                   12330:         my $text = $choices{$action};
                   12331:         if ($is_dir) {
                   12332:             $output .= ' onclick="javascript:propagateCheck(this.form,'."'$count'".');"';
                   12333:             if ($action eq 'display') {
1.1059    raeburn  12334:                 $text = &mt('Add as folder');
1.1055    raeburn  12335:             }
1.1056    raeburn  12336:         } else {
                   12337:             $output .= ' onclick="javascript:dependencyCheck(this.form,'."$count,$offset".');"';
                   12338: 
                   12339:         }
                   12340:         $output .= ' />&nbsp;'.$choices{$action}.'</label></span>';
                   12341:         if ($action eq 'dependency') {
                   12342:             $output .= '<div id="arc_depon_'.$count.'" style="display:none;">'."\n".
                   12343:                        &mt('Used by:').'&nbsp;<select name="archive_dependent_on_'.$count.'" '.
                   12344:                        'onchange="propagateSelect(this.form,'."$count,$offset".')">'."\n".
                   12345:                        '<option value=""></option>'."\n".
                   12346:                        '</select>'."\n".
                   12347:                        '</div>';
1.1059    raeburn  12348:         } elsif ($action eq 'display') {
                   12349:             $output .= '<div id="arc_title_'.$count.'" style="display:none;">'."\n".
                   12350:                        &mt('Title:').'&nbsp;<input type="text" name="archive_title_'.$count.'" id="archive_title_'.$count.'" />'."\n".
                   12351:                        '</div>';
1.1055    raeburn  12352:         }
1.1056    raeburn  12353:         $output .= '</td>';
1.1055    raeburn  12354:     }
                   12355:     $output .= '<td><input type="hidden" name="archive_content_'.$count.'" value="'.
                   12356:                &HTML::Entities::encode("$currdir/$item",'"<>&').'" />'.('&nbsp;' x 2);
                   12357:     for (my $i=0; $i<$depth; $i++) {
                   12358:         $output .= ('<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" alt="" />' x2)."\n";
                   12359:     }
                   12360:     if ($is_dir) {
                   12361:         $output .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" />&nbsp;'."\n".
                   12362:                    '<input type="hidden" name="archive_directory" value="'.$count.'" />'."\n";
                   12363:     } else {
                   12364:         $output .= '<input type="hidden" name="archive_file" value="'.$count.'" />'."\n";
                   12365:     }
                   12366:     $output .= '&nbsp;'.$name.'</td>'."\n".
                   12367:                &end_data_table_row();
                   12368:     return $output;
                   12369: }
                   12370: 
                   12371: sub archive_options_form {
1.1065    raeburn  12372:     my ($form,$display,$count,$hiddenelem) = @_;
                   12373:     my %lt = &Apache::lonlocal::texthash(
                   12374:                perm => 'Permanently remove archive file?',
                   12375:                hows => 'How should each extracted item be incorporated in the course?',
                   12376:                cont => 'Content actions for all',
                   12377:                addf => 'Add as folder/file',
                   12378:                incd => 'Include as dependency for a displayed file',
                   12379:                disc => 'Discard',
                   12380:                no   => 'No',
                   12381:                yes  => 'Yes',
                   12382:                save => 'Save',
                   12383:     );
                   12384:     my $output = <<"END";
                   12385: <form name="$form" method="post" action="">
                   12386: <p><span class="LC_nobreak">$lt{'perm'}&nbsp;
                   12387: <label>
                   12388:   <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
                   12389: </label>
                   12390: &nbsp;
                   12391: <label>
                   12392:   <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
                   12393: </span>
                   12394: </p>
                   12395: <input type="hidden" name="phase" value="decompress_cleanup" />
                   12396: <br />$lt{'hows'}
                   12397: <div class="LC_columnSection">
                   12398:   <fieldset>
                   12399:     <legend>$lt{'cont'}</legend>
                   12400:     <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" /> 
                   12401:     &nbsp;&nbsp;<input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
                   12402:     &nbsp;&nbsp;<input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
                   12403:   </fieldset>
                   12404: </div>
                   12405: END
                   12406:     return $output.
1.1055    raeburn  12407:            &start_data_table()."\n".
1.1065    raeburn  12408:            $display."\n".
1.1055    raeburn  12409:            &end_data_table()."\n".
                   12410:            '<input type="hidden" name="archive_count" value="'.$count.'" />'.
                   12411:            $hiddenelem.
1.1065    raeburn  12412:            '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
1.1055    raeburn  12413:            '</form>';
                   12414: }
                   12415: 
                   12416: sub archive_javascript {
1.1056    raeburn  12417:     my ($startcount,$numitems,$titles,$children) = @_;
                   12418:     return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
1.1059    raeburn  12419:     my $maintitle = $env{'form.comment'};
1.1055    raeburn  12420:     my $scripttag = <<START;
                   12421: <script type="text/javascript">
                   12422: // <![CDATA[
                   12423: 
                   12424: function checkAll(form,prefix) {
                   12425:     var idstr =  new RegExp("^archive_"+prefix+"_\\\\d+\$");
                   12426:     for (var i=0; i < form.elements.length; i++) {
                   12427:         var id = form.elements[i].id;
                   12428:         if ((id != '') && (id != undefined)) {
                   12429:             if (idstr.test(id)) {
                   12430:                 if (form.elements[i].type == 'radio') {
                   12431:                     form.elements[i].checked = true;
1.1056    raeburn  12432:                     var nostart = i-$startcount;
1.1059    raeburn  12433:                     var offset = nostart%7;
                   12434:                     var count = (nostart-offset)/7;    
1.1056    raeburn  12435:                     dependencyCheck(form,count,offset);
1.1055    raeburn  12436:                 }
                   12437:             }
                   12438:         }
                   12439:     }
                   12440: }
                   12441: 
                   12442: function propagateCheck(form,count) {
                   12443:     if (count > 0) {
1.1059    raeburn  12444:         var startelement = $startcount + ((count-1) * 7);
                   12445:         for (var j=1; j<6; j++) {
                   12446:             if ((j != 2) && (j != 4)) {
1.1056    raeburn  12447:                 var item = startelement + j; 
                   12448:                 if (form.elements[item].type == 'radio') {
                   12449:                     if (form.elements[item].checked) {
                   12450:                         containerCheck(form,count,j);
                   12451:                         break;
                   12452:                     }
1.1055    raeburn  12453:                 }
                   12454:             }
                   12455:         }
                   12456:     }
                   12457: }
                   12458: 
                   12459: numitems = $numitems
1.1056    raeburn  12460: var titles = new Array(numitems);
                   12461: var parents = new Array(numitems);
1.1055    raeburn  12462: for (var i=0; i<numitems; i++) {
1.1056    raeburn  12463:     parents[i] = new Array;
1.1055    raeburn  12464: }
1.1059    raeburn  12465: var maintitle = '$maintitle';
1.1055    raeburn  12466: 
                   12467: START
                   12468: 
1.1056    raeburn  12469:     foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
                   12470:         my @contents = split(/:/,$children->{$container});
1.1055    raeburn  12471:         for (my $i=0; $i<@contents; $i ++) {
                   12472:             $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
                   12473:         }
                   12474:     }
                   12475: 
1.1056    raeburn  12476:     foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
                   12477:         $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
                   12478:     }
                   12479: 
1.1055    raeburn  12480:     $scripttag .= <<END;
                   12481: 
                   12482: function containerCheck(form,count,offset) {
                   12483:     if (count > 0) {
1.1056    raeburn  12484:         dependencyCheck(form,count,offset);
1.1059    raeburn  12485:         var item = (offset+$startcount)+7*(count-1);
1.1055    raeburn  12486:         form.elements[item].checked = true;
                   12487:         if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   12488:             if (parents[count].length > 0) {
                   12489:                 for (var j=0; j<parents[count].length; j++) {
1.1056    raeburn  12490:                     containerCheck(form,parents[count][j],offset);
                   12491:                 }
                   12492:             }
                   12493:         }
                   12494:     }
                   12495: }
                   12496: 
                   12497: function dependencyCheck(form,count,offset) {
                   12498:     if (count > 0) {
1.1059    raeburn  12499:         var chosen = (offset+$startcount)+7*(count-1);
                   12500:         var depitem = $startcount + ((count-1) * 7) + 4;
1.1056    raeburn  12501:         var currtype = form.elements[depitem].type;
                   12502:         if (form.elements[chosen].value == 'dependency') {
                   12503:             document.getElementById('arc_depon_'+count).style.display='block'; 
                   12504:             form.elements[depitem].options.length = 0;
                   12505:             form.elements[depitem].options[0] = new Option('Select','',true,true);
1.1085    raeburn  12506:             for (var i=1; i<=numitems; i++) {
                   12507:                 if (i == count) {
                   12508:                     continue;
                   12509:                 }
1.1059    raeburn  12510:                 var startelement = $startcount + (i-1) * 7;
                   12511:                 for (var j=1; j<6; j++) {
                   12512:                     if ((j != 2) && (j!= 4)) {
1.1056    raeburn  12513:                         var item = startelement + j;
                   12514:                         if (form.elements[item].type == 'radio') {
                   12515:                             if (form.elements[item].checked) {
                   12516:                                 if (form.elements[item].value == 'display') {
                   12517:                                     var n = form.elements[depitem].options.length;
                   12518:                                     form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
                   12519:                                 }
                   12520:                             }
                   12521:                         }
                   12522:                     }
                   12523:                 }
                   12524:             }
                   12525:         } else {
                   12526:             document.getElementById('arc_depon_'+count).style.display='none';
                   12527:             form.elements[depitem].options.length = 0;
                   12528:             form.elements[depitem].options[0] = new Option('Select','',true,true);
                   12529:         }
1.1059    raeburn  12530:         titleCheck(form,count,offset);
1.1056    raeburn  12531:     }
                   12532: }
                   12533: 
                   12534: function propagateSelect(form,count,offset) {
                   12535:     if (count > 0) {
1.1065    raeburn  12536:         var item = (1+offset+$startcount)+7*(count-1);
1.1056    raeburn  12537:         var picked = form.elements[item].options[form.elements[item].selectedIndex].value; 
                   12538:         if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   12539:             if (parents[count].length > 0) {
                   12540:                 for (var j=0; j<parents[count].length; j++) {
                   12541:                     containerSelect(form,parents[count][j],offset,picked);
1.1055    raeburn  12542:                 }
                   12543:             }
                   12544:         }
                   12545:     }
                   12546: }
1.1056    raeburn  12547: 
                   12548: function containerSelect(form,count,offset,picked) {
                   12549:     if (count > 0) {
1.1065    raeburn  12550:         var item = (offset+$startcount)+7*(count-1);
1.1056    raeburn  12551:         if (form.elements[item].type == 'radio') {
                   12552:             if (form.elements[item].value == 'dependency') {
                   12553:                 if (form.elements[item+1].type == 'select-one') {
                   12554:                     for (var i=0; i<form.elements[item+1].options.length; i++) {
                   12555:                         if (form.elements[item+1].options[i].value == picked) {
                   12556:                             form.elements[item+1].selectedIndex = i;
                   12557:                             break;
                   12558:                         }
                   12559:                     }
                   12560:                 }
                   12561:                 if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   12562:                     if (parents[count].length > 0) {
                   12563:                         for (var j=0; j<parents[count].length; j++) {
                   12564:                             containerSelect(form,parents[count][j],offset,picked);
                   12565:                         }
                   12566:                     }
                   12567:                 }
                   12568:             }
                   12569:         }
                   12570:     }
                   12571: }
                   12572: 
1.1059    raeburn  12573: function titleCheck(form,count,offset) {
                   12574:     if (count > 0) {
                   12575:         var chosen = (offset+$startcount)+7*(count-1);
                   12576:         var depitem = $startcount + ((count-1) * 7) + 2;
                   12577:         var currtype = form.elements[depitem].type;
                   12578:         if (form.elements[chosen].value == 'display') {
                   12579:             document.getElementById('arc_title_'+count).style.display='block';
                   12580:             if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
                   12581:                 document.getElementById('archive_title_'+count).value=maintitle;
                   12582:             }
                   12583:         } else {
                   12584:             document.getElementById('arc_title_'+count).style.display='none';
                   12585:             if (currtype == 'text') { 
                   12586:                 document.getElementById('archive_title_'+count).value='';
                   12587:             }
                   12588:         }
                   12589:     }
                   12590:     return;
                   12591: }
                   12592: 
1.1055    raeburn  12593: // ]]>
                   12594: </script>
                   12595: END
                   12596:     return $scripttag;
                   12597: }
                   12598: 
                   12599: sub process_extracted_files {
1.1067    raeburn  12600:     my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
1.1055    raeburn  12601:     my $numitems = $env{'form.archive_count'};
                   12602:     return unless ($numitems);
                   12603:     my @ids=&Apache::lonnet::current_machine_ids();
                   12604:     my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
1.1067    raeburn  12605:         %folders,%containers,%mapinner,%prompttofetch);
1.1055    raeburn  12606:     my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
                   12607:     if (grep(/^\Q$docuhome\E$/,@ids)) {
                   12608:         $prefix = &LONCAPA::propath($docudom,$docuname);
                   12609:         $pathtocheck = "$dir_root/$destination";
                   12610:         $dir = $dir_root;
                   12611:         $ishome = 1;
                   12612:     } else {
                   12613:         $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
                   12614:         $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
                   12615:         $dir = "$dir_root/$docudom/$docuname";    
                   12616:     }
                   12617:     my $currdir = "$dir_root/$destination";
                   12618:     (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
                   12619:     if ($env{'form.folderpath'}) {
                   12620:         my @items = split('&',$env{'form.folderpath'});
                   12621:         $folders{'0'} = $items[-2];
1.1099    raeburn  12622:         if ($env{'form.folderpath'} =~ /\:1$/) {
                   12623:             $containers{'0'}='page';
                   12624:         } else {  
                   12625:             $containers{'0'}='sequence';
                   12626:         }
1.1055    raeburn  12627:     }
                   12628:     my @archdirs = &get_env_multiple('form.archive_directory');
                   12629:     if ($numitems) {
                   12630:         for (my $i=1; $i<=$numitems; $i++) {
                   12631:             my $path = $env{'form.archive_content_'.$i};
                   12632:             if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
                   12633:                 my $item = $1;
                   12634:                 $toplevelitems{$item} = $i;
                   12635:                 if (grep(/^\Q$i\E$/,@archdirs)) {
                   12636:                     $is_dir{$item} = 1;
                   12637:                 }
                   12638:             }
                   12639:         }
                   12640:     }
1.1067    raeburn  12641:     my ($output,%children,%parent,%titles,%dirorder,$result);
1.1055    raeburn  12642:     if (keys(%toplevelitems) > 0) {
                   12643:         my @contents = sort(keys(%toplevelitems));
1.1056    raeburn  12644:         (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
                   12645:                                            \%parent,\@contents,\%dirorder,\%titles);
1.1055    raeburn  12646:     }
1.1066    raeburn  12647:     my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
1.1055    raeburn  12648:     if ($numitems) {
                   12649:         for (my $i=1; $i<=$numitems; $i++) {
1.1086    raeburn  12650:             next if ($env{'form.archive_'.$i} eq 'dependency');
1.1055    raeburn  12651:             my $path = $env{'form.archive_content_'.$i};
                   12652:             if ($path =~ /^\Q$pathtocheck\E/) {
                   12653:                 if ($env{'form.archive_'.$i} eq 'discard') {
                   12654:                     if ($prefix ne '' && $path ne '') {
                   12655:                         if (-e $prefix.$path) {
1.1066    raeburn  12656:                             if ((@archdirs > 0) && 
                   12657:                                 (grep(/^\Q$i\E$/,@archdirs))) {
                   12658:                                 $todeletedir{$prefix.$path} = 1;
                   12659:                             } else {
                   12660:                                 $todelete{$prefix.$path} = 1;
                   12661:                             }
1.1055    raeburn  12662:                         }
                   12663:                     }
                   12664:                 } elsif ($env{'form.archive_'.$i} eq 'display') {
1.1059    raeburn  12665:                     my ($docstitle,$title,$url,$outer);
1.1055    raeburn  12666:                     ($title) = ($path =~ m{/([^/]+)$});
1.1059    raeburn  12667:                     $docstitle = $env{'form.archive_title_'.$i};
                   12668:                     if ($docstitle eq '') {
                   12669:                         $docstitle = $title;
                   12670:                     }
1.1055    raeburn  12671:                     $outer = 0;
1.1056    raeburn  12672:                     if (ref($dirorder{$i}) eq 'ARRAY') {
                   12673:                         if (@{$dirorder{$i}} > 0) {
                   12674:                             foreach my $item (reverse(@{$dirorder{$i}})) {
1.1055    raeburn  12675:                                 if ($env{'form.archive_'.$item} eq 'display') {
                   12676:                                     $outer = $item;
                   12677:                                     last;
                   12678:                                 }
                   12679:                             }
                   12680:                         }
                   12681:                     }
                   12682:                     my ($errtext,$fatal) = 
                   12683:                         &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
                   12684:                                                '/'.$folders{$outer}.'.'.
                   12685:                                                $containers{$outer});
                   12686:                     next if ($fatal);
                   12687:                     if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
                   12688:                         if ($context eq 'coursedocs') {
1.1056    raeburn  12689:                             $mapinner{$i} = time;
1.1055    raeburn  12690:                             $folders{$i} = 'default_'.$mapinner{$i};
                   12691:                             $containers{$i} = 'sequence';
                   12692:                             my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
                   12693:                                       $folders{$i}.'.'.$containers{$i};
                   12694:                             my $newidx = &LONCAPA::map::getresidx();
                   12695:                             $LONCAPA::map::resources[$newidx]=
1.1059    raeburn  12696:                                 $docstitle.':'.$url.':false:normal:res';
1.1055    raeburn  12697:                             push(@LONCAPA::map::order,$newidx);
                   12698:                             my ($outtext,$errtext) =
                   12699:                                 &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
                   12700:                                                         $docuname.'/'.$folders{$outer}.
1.1087    raeburn  12701:                                                         '.'.$containers{$outer},1,1);
1.1056    raeburn  12702:                             $newseqid{$i} = $newidx;
1.1067    raeburn  12703:                             unless ($errtext) {
                   12704:                                 $result .=  '<li>'.&mt('Folder: [_1] added to course',$docstitle).'</li>'."\n";
                   12705:                             }
1.1055    raeburn  12706:                         }
                   12707:                     } else {
                   12708:                         if ($context eq 'coursedocs') {
                   12709:                             my $newidx=&LONCAPA::map::getresidx();
                   12710:                             my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
                   12711:                                       $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
                   12712:                                       $title;
                   12713:                             if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
                   12714:                                 mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
                   12715:                             }
                   12716:                             if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
                   12717:                                 mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx");
                   12718:                             }
                   12719:                             if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
                   12720:                                 system("mv $prefix$path $prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title");
1.1056    raeburn  12721:                                 $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx";
1.1067    raeburn  12722:                                 unless ($ishome) {
                   12723:                                     my $fetch = "$newdest{$i}/$title";
                   12724:                                     $fetch =~ s/^\Q$prefix$dir\E//;
                   12725:                                     $prompttofetch{$fetch} = 1;
                   12726:                                 }
1.1055    raeburn  12727:                             }
                   12728:                             $LONCAPA::map::resources[$newidx]=
1.1059    raeburn  12729:                                 $docstitle.':'.$url.':false:normal:res';
1.1055    raeburn  12730:                             push(@LONCAPA::map::order, $newidx);
                   12731:                             my ($outtext,$errtext)=
                   12732:                                 &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
                   12733:                                                         $docuname.'/'.$folders{$outer}.
1.1087    raeburn  12734:                                                         '.'.$containers{$outer},1,1);
1.1067    raeburn  12735:                             unless ($errtext) {
                   12736:                                 if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") {
                   12737:                                     $result .= '<li>'.&mt('File: [_1] added to course',$docstitle).'</li>'."\n";
                   12738:                                 }
                   12739:                             }
1.1055    raeburn  12740:                         }
                   12741:                     }
1.1086    raeburn  12742:                 }
                   12743:             } else {
                   12744:                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />'; 
                   12745:             }
                   12746:         }
                   12747:         for (my $i=1; $i<=$numitems; $i++) {
                   12748:             next unless ($env{'form.archive_'.$i} eq 'dependency');
                   12749:             my $path = $env{'form.archive_content_'.$i};
                   12750:             if ($path =~ /^\Q$pathtocheck\E/) {
                   12751:                 my ($title) = ($path =~ m{/([^/]+)$});
                   12752:                 $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
                   12753:                 if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
                   12754:                     if (ref($dirorder{$i}) eq 'ARRAY') {
                   12755:                         my ($itemidx,$fullpath,$relpath);
                   12756:                         if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
                   12757:                             my $container = $dirorder{$referrer{$i}}->[-1];
1.1056    raeburn  12758:                             for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
1.1086    raeburn  12759:                                 if ($dirorder{$i}->[$j] eq $container) {
                   12760:                                     $itemidx = $j;
1.1056    raeburn  12761:                                 }
                   12762:                             }
1.1086    raeburn  12763:                         }
                   12764:                         if ($itemidx eq '') {
                   12765:                             $itemidx =  0;
                   12766:                         } 
                   12767:                         if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
                   12768:                             if ($mapinner{$referrer{$i}}) {
                   12769:                                 $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
                   12770:                                 for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
                   12771:                                     if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
                   12772:                                         unless (defined($newseqid{$dirorder{$i}->[$j]})) {
                   12773:                                             $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   12774:                                             $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   12775:                                             if (!-e $fullpath) {
                   12776:                                                 mkdir($fullpath,0755);
1.1056    raeburn  12777:                                             }
                   12778:                                         }
1.1086    raeburn  12779:                                     } else {
                   12780:                                         last;
1.1056    raeburn  12781:                                     }
1.1086    raeburn  12782:                                 }
                   12783:                             }
                   12784:                         } elsif ($newdest{$referrer{$i}}) {
                   12785:                             $fullpath = $newdest{$referrer{$i}};
                   12786:                             for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
                   12787:                                 if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
                   12788:                                     $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
                   12789:                                     last;
                   12790:                                 } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
                   12791:                                     unless (defined($newseqid{$dirorder{$i}->[$j]})) {
                   12792:                                         $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   12793:                                         $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   12794:                                         if (!-e $fullpath) {
                   12795:                                             mkdir($fullpath,0755);
1.1056    raeburn  12796:                                         }
                   12797:                                     }
1.1086    raeburn  12798:                                 } else {
                   12799:                                     last;
1.1056    raeburn  12800:                                 }
1.1055    raeburn  12801:                             }
                   12802:                         }
1.1086    raeburn  12803:                         if ($fullpath ne '') {
                   12804:                             if (-e "$prefix$path") {
                   12805:                                 system("mv $prefix$path $fullpath/$title");
                   12806:                             }
                   12807:                             if (-e "$fullpath/$title") {
                   12808:                                 my $showpath;
                   12809:                                 if ($relpath ne '') {
                   12810:                                     $showpath = "$relpath/$title";
                   12811:                                 } else {
                   12812:                                     $showpath = "/$title";
                   12813:                                 } 
                   12814:                                 $result .= '<li>'.&mt('[_1] included as a dependency',$showpath).'</li>'."\n";
                   12815:                             } 
                   12816:                             unless ($ishome) {
                   12817:                                 my $fetch = "$fullpath/$title";
                   12818:                                 $fetch =~ s/^\Q$prefix$dir\E//; 
                   12819:                                 $prompttofetch{$fetch} = 1;
                   12820:                             }
                   12821:                         }
1.1055    raeburn  12822:                     }
1.1086    raeburn  12823:                 } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') {
                   12824:                     $warning .= &mt('[_1] is a dependency of [_2], which was discarded.',
                   12825:                                     $path,$env{'form.archive_content_'.$referrer{$i}}).'<br />';
1.1055    raeburn  12826:                 }
                   12827:             } else {
                   12828:                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />'; 
                   12829:             }
                   12830:         }
                   12831:         if (keys(%todelete)) {
                   12832:             foreach my $key (keys(%todelete)) {
                   12833:                 unlink($key);
1.1066    raeburn  12834:             }
                   12835:         }
                   12836:         if (keys(%todeletedir)) {
                   12837:             foreach my $key (keys(%todeletedir)) {
                   12838:                 rmdir($key);
                   12839:             }
                   12840:         }
                   12841:         foreach my $dir (sort(keys(%is_dir))) {
                   12842:             if (($pathtocheck ne '') && ($dir ne ''))  {
                   12843:                 &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
1.1055    raeburn  12844:             }
                   12845:         }
1.1067    raeburn  12846:         if ($result ne '') {
                   12847:             $output .= '<ul>'."\n".
                   12848:                        $result."\n".
                   12849:                        '</ul>';
                   12850:         }
                   12851:         unless ($ishome) {
                   12852:             my $replicationfail;
                   12853:             foreach my $item (keys(%prompttofetch)) {
                   12854:                 my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
                   12855:                 unless ($fetchresult eq 'ok') {
                   12856:                     $replicationfail .= '<li>'.$item.'</li>'."\n";
                   12857:                 }
                   12858:             }
                   12859:             if ($replicationfail) {
                   12860:                 $output .= '<p class="LC_error">'.
                   12861:                            &mt('Course home server failed to retrieve:').'<ul>'.
                   12862:                            $replicationfail.
                   12863:                            '</ul></p>';
                   12864:             }
                   12865:         }
1.1055    raeburn  12866:     } else {
                   12867:         $warning = &mt('No items found in archive.');
                   12868:     }
                   12869:     if ($error) {
                   12870:         $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   12871:                    $error.'</p>'."\n";
                   12872:     }
                   12873:     if ($warning) {
                   12874:         $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
                   12875:     }
                   12876:     return $output;
                   12877: }
                   12878: 
1.1066    raeburn  12879: sub cleanup_empty_dirs {
                   12880:     my ($path) = @_;
                   12881:     if (($path ne '') && (-d $path)) {
                   12882:         if (opendir(my $dirh,$path)) {
                   12883:             my @dircontents = grep(!/^\./,readdir($dirh));
                   12884:             my $numitems = 0;
                   12885:             foreach my $item (@dircontents) {
                   12886:                 if (-d "$path/$item") {
1.1111    raeburn  12887:                     &cleanup_empty_dirs("$path/$item");
1.1066    raeburn  12888:                     if (-e "$path/$item") {
                   12889:                         $numitems ++;
                   12890:                     }
                   12891:                 } else {
                   12892:                     $numitems ++;
                   12893:                 }
                   12894:             }
                   12895:             if ($numitems == 0) {
                   12896:                 rmdir($path);
                   12897:             }
                   12898:             closedir($dirh);
                   12899:         }
                   12900:     }
                   12901:     return;
                   12902: }
                   12903: 
1.41      ng       12904: =pod
1.45      matthew  12905: 
1.1162    raeburn  12906: =item * &get_folder_hierarchy()
1.1068    raeburn  12907: 
                   12908: Provides hierarchy of names of folders/sub-folders containing the current
                   12909: item,
                   12910: 
                   12911: Inputs: 3
                   12912:      - $navmap - navmaps object
                   12913: 
                   12914:      - $map - url for map (either the trigger itself, or map containing
                   12915:                            the resource, which is the trigger).
                   12916: 
                   12917:      - $showitem - 1 => show title for map itself; 0 => do not show.
                   12918: 
                   12919: Outputs: 1 @pathitems - array of folder/subfolder names.
                   12920: 
                   12921: =cut
                   12922: 
                   12923: sub get_folder_hierarchy {
                   12924:     my ($navmap,$map,$showitem) = @_;
                   12925:     my @pathitems;
                   12926:     if (ref($navmap)) {
                   12927:         my $mapres = $navmap->getResourceByUrl($map);
                   12928:         if (ref($mapres)) {
                   12929:             my $pcslist = $mapres->map_hierarchy();
                   12930:             if ($pcslist ne '') {
                   12931:                 my @pcs = split(/,/,$pcslist);
                   12932:                 foreach my $pc (@pcs) {
                   12933:                     if ($pc == 1) {
1.1129    raeburn  12934:                         push(@pathitems,&mt('Main Content'));
1.1068    raeburn  12935:                     } else {
                   12936:                         my $res = $navmap->getByMapPc($pc);
                   12937:                         if (ref($res)) {
                   12938:                             my $title = $res->compTitle();
                   12939:                             $title =~ s/\W+/_/g;
                   12940:                             if ($title ne '') {
                   12941:                                 push(@pathitems,$title);
                   12942:                             }
                   12943:                         }
                   12944:                     }
                   12945:                 }
                   12946:             }
1.1071    raeburn  12947:             if ($showitem) {
                   12948:                 if ($mapres->{ID} eq '0.0') {
1.1129    raeburn  12949:                     push(@pathitems,&mt('Main Content'));
1.1071    raeburn  12950:                 } else {
                   12951:                     my $maptitle = $mapres->compTitle();
                   12952:                     $maptitle =~ s/\W+/_/g;
                   12953:                     if ($maptitle ne '') {
                   12954:                         push(@pathitems,$maptitle);
                   12955:                     }
1.1068    raeburn  12956:                 }
                   12957:             }
                   12958:         }
                   12959:     }
                   12960:     return @pathitems;
                   12961: }
                   12962: 
                   12963: =pod
                   12964: 
1.1015    raeburn  12965: =item * &get_turnedin_filepath()
                   12966: 
                   12967: Determines path in a user's portfolio file for storage of files uploaded
                   12968: to a specific essayresponse or dropbox item.
                   12969: 
                   12970: Inputs: 3 required + 1 optional.
                   12971: $symb is symb for resource, $uname and $udom are for current user (required).
                   12972: $caller is optional (can be "submission", if routine is called when storing
                   12973: an upoaded file when "Submit Answer" button was pressed).
                   12974: 
                   12975: Returns array containing $path and $multiresp. 
                   12976: $path is path in portfolio.  $multiresp is 1 if this resource contains more
                   12977: than one file upload item.  Callers of routine should append partid as a 
                   12978: subdirectory to $path in cases where $multiresp is 1.
                   12979: 
                   12980: Called by: homework/essayresponse.pm and homework/structuretags.pm
                   12981: 
                   12982: =cut
                   12983: 
                   12984: sub get_turnedin_filepath {
                   12985:     my ($symb,$uname,$udom,$caller) = @_;
                   12986:     my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
                   12987:     my $turnindir;
                   12988:     my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
                   12989:     $turnindir = $userhash{'turnindir'};
                   12990:     my ($path,$multiresp);
                   12991:     if ($turnindir eq '') {
                   12992:         if ($caller eq 'submission') {
                   12993:             $turnindir = &mt('turned in');
                   12994:             $turnindir =~ s/\W+/_/g;
                   12995:             my %newhash = (
                   12996:                             'turnindir' => $turnindir,
                   12997:                           );
                   12998:             &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
                   12999:         }
                   13000:     }
                   13001:     if ($turnindir ne '') {
                   13002:         $path = '/'.$turnindir.'/';
                   13003:         my ($multipart,$turnin,@pathitems);
                   13004:         my $navmap = Apache::lonnavmaps::navmap->new();
                   13005:         if (defined($navmap)) {
                   13006:             my $mapres = $navmap->getResourceByUrl($map);
                   13007:             if (ref($mapres)) {
                   13008:                 my $pcslist = $mapres->map_hierarchy();
                   13009:                 if ($pcslist ne '') {
                   13010:                     foreach my $pc (split(/,/,$pcslist)) {
                   13011:                         my $res = $navmap->getByMapPc($pc);
                   13012:                         if (ref($res)) {
                   13013:                             my $title = $res->compTitle();
                   13014:                             $title =~ s/\W+/_/g;
                   13015:                             if ($title ne '') {
1.1149    raeburn  13016:                                 if (($pc > 1) && (length($title) > 12)) {
                   13017:                                     $title = substr($title,0,12);
                   13018:                                 }
1.1015    raeburn  13019:                                 push(@pathitems,$title);
                   13020:                             }
                   13021:                         }
                   13022:                     }
                   13023:                 }
                   13024:                 my $maptitle = $mapres->compTitle();
                   13025:                 $maptitle =~ s/\W+/_/g;
                   13026:                 if ($maptitle ne '') {
1.1149    raeburn  13027:                     if (length($maptitle) > 12) {
                   13028:                         $maptitle = substr($maptitle,0,12);
                   13029:                     }
1.1015    raeburn  13030:                     push(@pathitems,$maptitle);
                   13031:                 }
                   13032:                 unless ($env{'request.state'} eq 'construct') {
                   13033:                     my $res = $navmap->getBySymb($symb);
                   13034:                     if (ref($res)) {
                   13035:                         my $partlist = $res->parts();
                   13036:                         my $totaluploads = 0;
                   13037:                         if (ref($partlist) eq 'ARRAY') {
                   13038:                             foreach my $part (@{$partlist}) {
                   13039:                                 my @types = $res->responseType($part);
                   13040:                                 my @ids = $res->responseIds($part);
                   13041:                                 for (my $i=0; $i < scalar(@ids); $i++) {
                   13042:                                     if ($types[$i] eq 'essay') {
                   13043:                                         my $partid = $part.'_'.$ids[$i];
                   13044:                                         if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
                   13045:                                             $totaluploads ++;
                   13046:                                         }
                   13047:                                     }
                   13048:                                 }
                   13049:                             }
                   13050:                             if ($totaluploads > 1) {
                   13051:                                 $multiresp = 1;
                   13052:                             }
                   13053:                         }
                   13054:                     }
                   13055:                 }
                   13056:             } else {
                   13057:                 return;
                   13058:             }
                   13059:         } else {
                   13060:             return;
                   13061:         }
                   13062:         my $restitle=&Apache::lonnet::gettitle($symb);
                   13063:         $restitle =~ s/\W+/_/g;
                   13064:         if ($restitle eq '') {
                   13065:             $restitle = ($resurl =~ m{/[^/]+$});
                   13066:             if ($restitle eq '') {
                   13067:                 $restitle = time;
                   13068:             }
                   13069:         }
1.1149    raeburn  13070:         if (length($restitle) > 12) {
                   13071:             $restitle = substr($restitle,0,12);
                   13072:         }
1.1015    raeburn  13073:         push(@pathitems,$restitle);
                   13074:         $path .= join('/',@pathitems);
                   13075:     }
                   13076:     return ($path,$multiresp);
                   13077: }
                   13078: 
                   13079: =pod
                   13080: 
1.464     albertel 13081: =back
1.41      ng       13082: 
1.112     bowersj2 13083: =head1 CSV Upload/Handling functions
1.38      albertel 13084: 
1.41      ng       13085: =over 4
                   13086: 
1.648     raeburn  13087: =item * &upfile_store($r)
1.41      ng       13088: 
                   13089: Store uploaded file, $r should be the HTTP Request object,
1.258     albertel 13090: needs $env{'form.upfile'}
1.41      ng       13091: returns $datatoken to be put into hidden field
                   13092: 
                   13093: =cut
1.31      albertel 13094: 
                   13095: sub upfile_store {
                   13096:     my $r=shift;
1.258     albertel 13097:     $env{'form.upfile'}=~s/\r/\n/gs;
                   13098:     $env{'form.upfile'}=~s/\f/\n/gs;
                   13099:     $env{'form.upfile'}=~s/\n+/\n/gs;
                   13100:     $env{'form.upfile'}=~s/\n+$//gs;
1.31      albertel 13101: 
1.258     albertel 13102:     my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
                   13103: 	'_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31      albertel 13104:     {
1.158     raeburn  13105:         my $datafile = $r->dir_config('lonDaemons').
                   13106:                            '/tmp/'.$datatoken.'.tmp';
                   13107:         if ( open(my $fh,">$datafile") ) {
1.258     albertel 13108:             print $fh $env{'form.upfile'};
1.158     raeburn  13109:             close($fh);
                   13110:         }
1.31      albertel 13111:     }
                   13112:     return $datatoken;
                   13113: }
                   13114: 
1.56      matthew  13115: =pod
                   13116: 
1.648     raeburn  13117: =item * &load_tmp_file($r)
1.41      ng       13118: 
                   13119: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258     albertel 13120: needs $env{'form.datatoken'},
                   13121: sets $env{'form.upfile'} to the contents of the file
1.41      ng       13122: 
                   13123: =cut
1.31      albertel 13124: 
                   13125: sub load_tmp_file {
                   13126:     my $r=shift;
                   13127:     my @studentdata=();
                   13128:     {
1.158     raeburn  13129:         my $studentfile = $r->dir_config('lonDaemons').
1.258     albertel 13130:                               '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158     raeburn  13131:         if ( open(my $fh,"<$studentfile") ) {
                   13132:             @studentdata=<$fh>;
                   13133:             close($fh);
                   13134:         }
1.31      albertel 13135:     }
1.258     albertel 13136:     $env{'form.upfile'}=join('',@studentdata);
1.31      albertel 13137: }
                   13138: 
1.56      matthew  13139: =pod
                   13140: 
1.648     raeburn  13141: =item * &upfile_record_sep()
1.41      ng       13142: 
                   13143: Separate uploaded file into records
                   13144: returns array of records,
1.258     albertel 13145: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41      ng       13146: 
                   13147: =cut
1.31      albertel 13148: 
                   13149: sub upfile_record_sep {
1.258     albertel 13150:     if ($env{'form.upfiletype'} eq 'xml') {
1.31      albertel 13151:     } else {
1.248     albertel 13152: 	my @records;
1.258     albertel 13153: 	foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248     albertel 13154: 	    if ($line=~/^\s*$/) { next; }
                   13155: 	    push(@records,$line);
                   13156: 	}
                   13157: 	return @records;
1.31      albertel 13158:     }
                   13159: }
                   13160: 
1.56      matthew  13161: =pod
                   13162: 
1.648     raeburn  13163: =item * &record_sep($record)
1.41      ng       13164: 
1.258     albertel 13165: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41      ng       13166: 
                   13167: =cut
                   13168: 
1.263     www      13169: sub takeleft {
                   13170:     my $index=shift;
                   13171:     return substr('0000'.$index,-4,4);
                   13172: }
                   13173: 
1.31      albertel 13174: sub record_sep {
                   13175:     my $record=shift;
                   13176:     my %components=();
1.258     albertel 13177:     if ($env{'form.upfiletype'} eq 'xml') {
                   13178:     } elsif ($env{'form.upfiletype'} eq 'space') {
1.31      albertel 13179:         my $i=0;
1.356     albertel 13180:         foreach my $field (split(/\s+/,$record)) {
1.31      albertel 13181:             $field=~s/^(\"|\')//;
                   13182:             $field=~s/(\"|\')$//;
1.263     www      13183:             $components{&takeleft($i)}=$field;
1.31      albertel 13184:             $i++;
                   13185:         }
1.258     albertel 13186:     } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31      albertel 13187:         my $i=0;
1.356     albertel 13188:         foreach my $field (split(/\t/,$record)) {
1.31      albertel 13189:             $field=~s/^(\"|\')//;
                   13190:             $field=~s/(\"|\')$//;
1.263     www      13191:             $components{&takeleft($i)}=$field;
1.31      albertel 13192:             $i++;
                   13193:         }
                   13194:     } else {
1.561     www      13195:         my $separator=',';
1.480     banghart 13196:         if ($env{'form.upfiletype'} eq 'semisv') {
1.561     www      13197:             $separator=';';
1.480     banghart 13198:         }
1.31      albertel 13199:         my $i=0;
1.561     www      13200: # the character we are looking for to indicate the end of a quote or a record 
                   13201:         my $looking_for=$separator;
                   13202: # do not add the characters to the fields
                   13203:         my $ignore=0;
                   13204: # we just encountered a separator (or the beginning of the record)
                   13205:         my $just_found_separator=1;
                   13206: # store the field we are working on here
                   13207:         my $field='';
                   13208: # work our way through all characters in record
                   13209:         foreach my $character ($record=~/(.)/g) {
                   13210:             if ($character eq $looking_for) {
                   13211:                if ($character ne $separator) {
                   13212: # Found the end of a quote, again looking for separator
                   13213:                   $looking_for=$separator;
                   13214:                   $ignore=1;
                   13215:                } else {
                   13216: # Found a separator, store away what we got
                   13217:                   $components{&takeleft($i)}=$field;
                   13218: 	          $i++;
                   13219:                   $just_found_separator=1;
                   13220:                   $ignore=0;
                   13221:                   $field='';
                   13222:                }
                   13223:                next;
                   13224:             }
                   13225: # single or double quotation marks after a separator indicate beginning of a quote
                   13226: # we are now looking for the end of the quote and need to ignore separators
                   13227:             if ((($character eq '"') || ($character eq "'")) && ($just_found_separator))  {
                   13228:                $looking_for=$character;
                   13229:                next;
                   13230:             }
                   13231: # ignore would be true after we reached the end of a quote
                   13232:             if ($ignore) { next; }
                   13233:             if (($just_found_separator) && ($character=~/\s/)) { next; }
                   13234:             $field.=$character;
                   13235:             $just_found_separator=0; 
1.31      albertel 13236:         }
1.561     www      13237: # catch the very last entry, since we never encountered the separator
                   13238:         $components{&takeleft($i)}=$field;
1.31      albertel 13239:     }
                   13240:     return %components;
                   13241: }
                   13242: 
1.144     matthew  13243: ######################################################
                   13244: ######################################################
                   13245: 
1.56      matthew  13246: =pod
                   13247: 
1.648     raeburn  13248: =item * &upfile_select_html()
1.41      ng       13249: 
1.144     matthew  13250: Return HTML code to select a file from the users machine and specify 
                   13251: the file type.
1.41      ng       13252: 
                   13253: =cut
                   13254: 
1.144     matthew  13255: ######################################################
                   13256: ######################################################
1.31      albertel 13257: sub upfile_select_html {
1.144     matthew  13258:     my %Types = (
                   13259:                  csv   => &mt('CSV (comma separated values, spreadsheet)'),
1.480     banghart 13260:                  semisv => &mt('Semicolon separated values'),
1.144     matthew  13261:                  space => &mt('Space separated'),
                   13262:                  tab   => &mt('Tabulator separated'),
                   13263: #                 xml   => &mt('HTML/XML'),
                   13264:                  );
                   13265:     my $Str = '<input type="file" name="upfile" size="50" />'.
1.727     riegler  13266:         '<br />'.&mt('Type').': <select name="upfiletype">';
1.144     matthew  13267:     foreach my $type (sort(keys(%Types))) {
                   13268:         $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
                   13269:     }
                   13270:     $Str .= "</select>\n";
                   13271:     return $Str;
1.31      albertel 13272: }
                   13273: 
1.301     albertel 13274: sub get_samples {
                   13275:     my ($records,$toget) = @_;
                   13276:     my @samples=({});
                   13277:     my $got=0;
                   13278:     foreach my $rec (@$records) {
                   13279: 	my %temp = &record_sep($rec);
                   13280: 	if (! grep(/\S/, values(%temp))) { next; }
                   13281: 	if (%temp) {
                   13282: 	    $samples[$got]=\%temp;
                   13283: 	    $got++;
                   13284: 	    if ($got == $toget) { last; }
                   13285: 	}
                   13286:     }
                   13287:     return \@samples;
                   13288: }
                   13289: 
1.144     matthew  13290: ######################################################
                   13291: ######################################################
                   13292: 
1.56      matthew  13293: =pod
                   13294: 
1.648     raeburn  13295: =item * &csv_print_samples($r,$records)
1.41      ng       13296: 
                   13297: Prints a table of sample values from each column uploaded $r is an
                   13298: Apache Request ref, $records is an arrayref from
                   13299: &Apache::loncommon::upfile_record_sep
                   13300: 
                   13301: =cut
                   13302: 
1.144     matthew  13303: ######################################################
                   13304: ######################################################
1.31      albertel 13305: sub csv_print_samples {
                   13306:     my ($r,$records) = @_;
1.662     bisitz   13307:     my $samples = &get_samples($records,5);
1.301     albertel 13308: 
1.594     raeburn  13309:     $r->print(&mt('Samples').'<br />'.&start_data_table().
                   13310:               &start_data_table_header_row());
1.356     albertel 13311:     foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { 
1.845     bisitz   13312:         $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.594     raeburn  13313:     $r->print(&end_data_table_header_row());
1.301     albertel 13314:     foreach my $hash (@$samples) {
1.594     raeburn  13315: 	$r->print(&start_data_table_row());
1.356     albertel 13316: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31      albertel 13317: 	    $r->print('<td>');
1.356     albertel 13318: 	    if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31      albertel 13319: 	    $r->print('</td>');
                   13320: 	}
1.594     raeburn  13321: 	$r->print(&end_data_table_row());
1.31      albertel 13322:     }
1.594     raeburn  13323:     $r->print(&end_data_table().'<br />'."\n");
1.31      albertel 13324: }
                   13325: 
1.144     matthew  13326: ######################################################
                   13327: ######################################################
                   13328: 
1.56      matthew  13329: =pod
                   13330: 
1.648     raeburn  13331: =item * &csv_print_select_table($r,$records,$d)
1.41      ng       13332: 
                   13333: Prints a table to create associations between values and table columns.
1.144     matthew  13334: 
1.41      ng       13335: $r is an Apache Request ref,
                   13336: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174     matthew  13337: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41      ng       13338: 
                   13339: =cut
                   13340: 
1.144     matthew  13341: ######################################################
                   13342: ######################################################
1.31      albertel 13343: sub csv_print_select_table {
                   13344:     my ($r,$records,$d) = @_;
1.301     albertel 13345:     my $i=0;
                   13346:     my $samples = &get_samples($records,1);
1.144     matthew  13347:     $r->print(&mt('Associate columns with student attributes.')."\n".
1.594     raeburn  13348: 	      &start_data_table().&start_data_table_header_row().
1.144     matthew  13349:               '<th>'.&mt('Attribute').'</th>'.
1.594     raeburn  13350:               '<th>'.&mt('Column').'</th>'.
                   13351:               &end_data_table_header_row()."\n");
1.356     albertel 13352:     foreach my $array_ref (@$d) {
                   13353: 	my ($value,$display,$defaultcol)=@{ $array_ref };
1.729     raeburn  13354: 	$r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31      albertel 13355: 
1.875     bisitz   13356: 	$r->print('<td><select name="f'.$i.'"'.
1.32      matthew  13357: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 13358: 	$r->print('<option value="none"></option>');
1.356     albertel 13359: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
                   13360: 	    $r->print('<option value="'.$sample.'"'.
                   13361:                       ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662     bisitz   13362:                       '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31      albertel 13363: 	}
1.594     raeburn  13364: 	$r->print('</select></td>'.&end_data_table_row()."\n");
1.31      albertel 13365: 	$i++;
                   13366:     }
1.594     raeburn  13367:     $r->print(&end_data_table());
1.31      albertel 13368:     $i--;
                   13369:     return $i;
                   13370: }
1.56      matthew  13371: 
1.144     matthew  13372: ######################################################
                   13373: ######################################################
                   13374: 
1.56      matthew  13375: =pod
1.31      albertel 13376: 
1.648     raeburn  13377: =item * &csv_samples_select_table($r,$records,$d)
1.41      ng       13378: 
                   13379: Prints a table of sample values from the upload and can make associate samples to internal names.
                   13380: 
                   13381: $r is an Apache Request ref,
                   13382: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
                   13383: $d is an array of 2 element arrays (internal name, displayed name)
                   13384: 
                   13385: =cut
                   13386: 
1.144     matthew  13387: ######################################################
                   13388: ######################################################
1.31      albertel 13389: sub csv_samples_select_table {
                   13390:     my ($r,$records,$d) = @_;
                   13391:     my $i=0;
1.144     matthew  13392:     #
1.662     bisitz   13393:     my $max_samples = 5;
                   13394:     my $samples = &get_samples($records,$max_samples);
1.594     raeburn  13395:     $r->print(&start_data_table().
                   13396:               &start_data_table_header_row().'<th>'.
                   13397:               &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
                   13398:               &end_data_table_header_row());
1.301     albertel 13399: 
                   13400:     foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594     raeburn  13401: 	$r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32      matthew  13402: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.301     albertel 13403: 	foreach my $option (@$d) {
                   13404: 	    my ($value,$display,$defaultcol)=@{ $option };
1.174     matthew  13405: 	    $r->print('<option value="'.$value.'"'.
1.253     albertel 13406:                       ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174     matthew  13407:                       $display.'</option>');
1.31      albertel 13408: 	}
                   13409: 	$r->print('</select></td><td>');
1.662     bisitz   13410: 	foreach my $line (0..($max_samples-1)) {
1.301     albertel 13411: 	    if (defined($samples->[$line]{$key})) { 
                   13412: 		$r->print($samples->[$line]{$key}."<br />\n"); 
                   13413: 	    }
                   13414: 	}
1.594     raeburn  13415: 	$r->print('</td>'.&end_data_table_row());
1.31      albertel 13416: 	$i++;
                   13417:     }
1.594     raeburn  13418:     $r->print(&end_data_table());
1.31      albertel 13419:     $i--;
                   13420:     return($i);
1.115     matthew  13421: }
                   13422: 
1.144     matthew  13423: ######################################################
                   13424: ######################################################
                   13425: 
1.115     matthew  13426: =pod
                   13427: 
1.648     raeburn  13428: =item * &clean_excel_name($name)
1.115     matthew  13429: 
                   13430: Returns a replacement for $name which does not contain any illegal characters.
                   13431: 
                   13432: =cut
                   13433: 
1.144     matthew  13434: ######################################################
                   13435: ######################################################
1.115     matthew  13436: sub clean_excel_name {
                   13437:     my ($name) = @_;
                   13438:     $name =~ s/[:\*\?\/\\]//g;
                   13439:     if (length($name) > 31) {
                   13440:         $name = substr($name,0,31);
                   13441:     }
                   13442:     return $name;
1.25      albertel 13443: }
1.84      albertel 13444: 
1.85      albertel 13445: =pod
                   13446: 
1.648     raeburn  13447: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85      albertel 13448: 
                   13449: Returns either 1 or undef
                   13450: 
                   13451: 1 if the part is to be hidden, undef if it is to be shown
                   13452: 
                   13453: Arguments are:
                   13454: 
                   13455: $id the id of the part to be checked
                   13456: $symb, optional the symb of the resource to check
                   13457: $udom, optional the domain of the user to check for
                   13458: $uname, optional the username of the user to check for
                   13459: 
                   13460: =cut
1.84      albertel 13461: 
                   13462: sub check_if_partid_hidden {
                   13463:     my ($id,$symb,$udom,$uname) = @_;
1.133     albertel 13464:     my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84      albertel 13465: 					 $symb,$udom,$uname);
1.141     albertel 13466:     my $truth=1;
                   13467:     #if the string starts with !, then the list is the list to show not hide
                   13468:     if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84      albertel 13469:     my @hiddenlist=split(/,/,$hiddenparts);
                   13470:     foreach my $checkid (@hiddenlist) {
1.141     albertel 13471: 	if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84      albertel 13472:     }
1.141     albertel 13473:     return !$truth;
1.84      albertel 13474: }
1.127     matthew  13475: 
1.138     matthew  13476: 
                   13477: ############################################################
                   13478: ############################################################
                   13479: 
                   13480: =pod
                   13481: 
1.157     matthew  13482: =back 
                   13483: 
1.138     matthew  13484: =head1 cgi-bin script and graphing routines
                   13485: 
1.157     matthew  13486: =over 4
                   13487: 
1.648     raeburn  13488: =item * &get_cgi_id()
1.138     matthew  13489: 
                   13490: Inputs: none
                   13491: 
                   13492: Returns an id which can be used to pass environment variables
                   13493: to various cgi-bin scripts.  These environment variables will
                   13494: be removed from the users environment after a given time by
                   13495: the routine &Apache::lonnet::transfer_profile_to_env.
                   13496: 
                   13497: =cut
                   13498: 
                   13499: ############################################################
                   13500: ############################################################
1.152     albertel 13501: my $uniq=0;
1.136     matthew  13502: sub get_cgi_id {
1.154     albertel 13503:     $uniq=($uniq+1)%100000;
1.280     albertel 13504:     return (time.'_'.$$.'_'.$uniq);
1.136     matthew  13505: }
                   13506: 
1.127     matthew  13507: ############################################################
                   13508: ############################################################
                   13509: 
                   13510: =pod
                   13511: 
1.648     raeburn  13512: =item * &DrawBarGraph()
1.127     matthew  13513: 
1.138     matthew  13514: Facilitates the plotting of data in a (stacked) bar graph.
                   13515: Puts plot definition data into the users environment in order for 
                   13516: graph.png to plot it.  Returns an <img> tag for the plot.
                   13517: The bars on the plot are labeled '1','2',...,'n'.
                   13518: 
                   13519: Inputs:
                   13520: 
                   13521: =over 4
                   13522: 
                   13523: =item $Title: string, the title of the plot
                   13524: 
                   13525: =item $xlabel: string, text describing the X-axis of the plot
                   13526: 
                   13527: =item $ylabel: string, text describing the Y-axis of the plot
                   13528: 
                   13529: =item $Max: scalar, the maximum Y value to use in the plot
                   13530: If $Max is < any data point, the graph will not be rendered.
                   13531: 
1.140     matthew  13532: =item $colors: array ref holding the colors to be used for the data sets when
1.138     matthew  13533: they are plotted.  If undefined, default values will be used.
                   13534: 
1.178     matthew  13535: =item $labels: array ref holding the labels to use on the x-axis for the bars.
                   13536: 
1.138     matthew  13537: =item @Values: An array of array references.  Each array reference holds data
                   13538: to be plotted in a stacked bar chart.
                   13539: 
1.239     matthew  13540: =item If the final element of @Values is a hash reference the key/value
                   13541: pairs will be added to the graph definition.
                   13542: 
1.138     matthew  13543: =back
                   13544: 
                   13545: Returns:
                   13546: 
                   13547: An <img> tag which references graph.png and the appropriate identifying
                   13548: information for the plot.
                   13549: 
1.127     matthew  13550: =cut
                   13551: 
                   13552: ############################################################
                   13553: ############################################################
1.134     matthew  13554: sub DrawBarGraph {
1.178     matthew  13555:     my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134     matthew  13556:     #
                   13557:     if (! defined($colors)) {
                   13558:         $colors = ['#33ff00', 
                   13559:                   '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
                   13560:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                   13561:                   ]; 
                   13562:     }
1.228     matthew  13563:     my $extra_settings = {};
                   13564:     if (ref($Values[-1]) eq 'HASH') {
                   13565:         $extra_settings = pop(@Values);
                   13566:     }
1.127     matthew  13567:     #
1.136     matthew  13568:     my $identifier = &get_cgi_id();
                   13569:     my $id = 'cgi.'.$identifier;        
1.129     matthew  13570:     if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127     matthew  13571:         return '';
                   13572:     }
1.225     matthew  13573:     #
                   13574:     my @Labels;
                   13575:     if (defined($labels)) {
                   13576:         @Labels = @$labels;
                   13577:     } else {
                   13578:         for (my $i=0;$i<@{$Values[0]};$i++) {
                   13579:             push (@Labels,$i+1);
                   13580:         }
                   13581:     }
                   13582:     #
1.129     matthew  13583:     my $NumBars = scalar(@{$Values[0]});
1.225     matthew  13584:     if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129     matthew  13585:     my %ValuesHash;
                   13586:     my $NumSets=1;
                   13587:     foreach my $array (@Values) {
                   13588:         next if (! ref($array));
1.136     matthew  13589:         $ValuesHash{$id.'.data.'.$NumSets++} = 
1.132     matthew  13590:             join(',',@$array);
1.129     matthew  13591:     }
1.127     matthew  13592:     #
1.136     matthew  13593:     my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225     matthew  13594:     if ($NumBars < 3) {
                   13595:         $width = 120+$NumBars*32;
1.220     matthew  13596:         $xskip = 1;
1.225     matthew  13597:         $bar_width = 30;
                   13598:     } elsif ($NumBars < 5) {
                   13599:         $width = 120+$NumBars*20;
                   13600:         $xskip = 1;
                   13601:         $bar_width = 20;
1.220     matthew  13602:     } elsif ($NumBars < 10) {
1.136     matthew  13603:         $width = 120+$NumBars*15;
                   13604:         $xskip = 1;
                   13605:         $bar_width = 15;
                   13606:     } elsif ($NumBars <= 25) {
                   13607:         $width = 120+$NumBars*11;
                   13608:         $xskip = 5;
                   13609:         $bar_width = 8;
                   13610:     } elsif ($NumBars <= 50) {
                   13611:         $width = 120+$NumBars*8;
                   13612:         $xskip = 5;
                   13613:         $bar_width = 4;
                   13614:     } else {
                   13615:         $width = 120+$NumBars*8;
                   13616:         $xskip = 5;
                   13617:         $bar_width = 4;
                   13618:     }
                   13619:     #
1.137     matthew  13620:     $Max = 1 if ($Max < 1);
                   13621:     if ( int($Max) < $Max ) {
                   13622:         $Max++;
                   13623:         $Max = int($Max);
                   13624:     }
1.127     matthew  13625:     $Title  = '' if (! defined($Title));
                   13626:     $xlabel = '' if (! defined($xlabel));
                   13627:     $ylabel = '' if (! defined($ylabel));
1.369     www      13628:     $ValuesHash{$id.'.title'}    = &escape($Title);
                   13629:     $ValuesHash{$id.'.xlabel'}   = &escape($xlabel);
                   13630:     $ValuesHash{$id.'.ylabel'}   = &escape($ylabel);
1.137     matthew  13631:     $ValuesHash{$id.'.y_max_value'} = $Max;
1.136     matthew  13632:     $ValuesHash{$id.'.NumBars'}  = $NumBars;
                   13633:     $ValuesHash{$id.'.NumSets'}  = $NumSets;
                   13634:     $ValuesHash{$id.'.PlotType'} = 'bar';
                   13635:     $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   13636:     $ValuesHash{$id.'.height'}   = $height;
                   13637:     $ValuesHash{$id.'.width'}    = $width;
                   13638:     $ValuesHash{$id.'.xskip'}    = $xskip;
                   13639:     $ValuesHash{$id.'.bar_width'} = $bar_width;
                   13640:     $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127     matthew  13641:     #
1.228     matthew  13642:     # Deal with other parameters
                   13643:     while (my ($key,$value) = each(%$extra_settings)) {
                   13644:         $ValuesHash{$id.'.'.$key} = $value;
                   13645:     }
                   13646:     #
1.646     raeburn  13647:     &Apache::lonnet::appenv(\%ValuesHash);
1.137     matthew  13648:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   13649: }
                   13650: 
                   13651: ############################################################
                   13652: ############################################################
                   13653: 
                   13654: =pod
                   13655: 
1.648     raeburn  13656: =item * &DrawXYGraph()
1.137     matthew  13657: 
1.138     matthew  13658: Facilitates the plotting of data in an XY graph.
                   13659: Puts plot definition data into the users environment in order for 
                   13660: graph.png to plot it.  Returns an <img> tag for the plot.
                   13661: 
                   13662: Inputs:
                   13663: 
                   13664: =over 4
                   13665: 
                   13666: =item $Title: string, the title of the plot
                   13667: 
                   13668: =item $xlabel: string, text describing the X-axis of the plot
                   13669: 
                   13670: =item $ylabel: string, text describing the Y-axis of the plot
                   13671: 
                   13672: =item $Max: scalar, the maximum Y value to use in the plot
                   13673: If $Max is < any data point, the graph will not be rendered.
                   13674: 
                   13675: =item $colors: Array ref containing the hex color codes for the data to be 
                   13676: plotted in.  If undefined, default values will be used.
                   13677: 
                   13678: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   13679: 
                   13680: =item $Ydata: Array ref containing Array refs.  
1.185     www      13681: Each of the contained arrays will be plotted as a separate curve.
1.138     matthew  13682: 
                   13683: =item %Values: hash indicating or overriding any default values which are 
                   13684: passed to graph.png.  
                   13685: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   13686: 
                   13687: =back
                   13688: 
                   13689: Returns:
                   13690: 
                   13691: An <img> tag which references graph.png and the appropriate identifying
                   13692: information for the plot.
                   13693: 
1.137     matthew  13694: =cut
                   13695: 
                   13696: ############################################################
                   13697: ############################################################
                   13698: sub DrawXYGraph {
                   13699:     my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
                   13700:     #
                   13701:     # Create the identifier for the graph
                   13702:     my $identifier = &get_cgi_id();
                   13703:     my $id = 'cgi.'.$identifier;
                   13704:     #
                   13705:     $Title  = '' if (! defined($Title));
                   13706:     $xlabel = '' if (! defined($xlabel));
                   13707:     $ylabel = '' if (! defined($ylabel));
                   13708:     my %ValuesHash = 
                   13709:         (
1.369     www      13710:          $id.'.title'  => &escape($Title),
                   13711:          $id.'.xlabel' => &escape($xlabel),
                   13712:          $id.'.ylabel' => &escape($ylabel),
1.137     matthew  13713:          $id.'.y_max_value'=> $Max,
                   13714:          $id.'.labels'     => join(',',@$Xlabels),
                   13715:          $id.'.PlotType'   => 'XY',
                   13716:          );
                   13717:     #
                   13718:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   13719:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   13720:     }
                   13721:     #
                   13722:     if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
                   13723:         return '';
                   13724:     }
                   13725:     my $NumSets=1;
1.138     matthew  13726:     foreach my $array (@{$Ydata}){
1.137     matthew  13727:         next if (! ref($array));
                   13728:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
                   13729:     }
1.138     matthew  13730:     $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137     matthew  13731:     #
                   13732:     # Deal with other parameters
                   13733:     while (my ($key,$value) = each(%Values)) {
                   13734:         $ValuesHash{$id.'.'.$key} = $value;
1.127     matthew  13735:     }
                   13736:     #
1.646     raeburn  13737:     &Apache::lonnet::appenv(\%ValuesHash);
1.136     matthew  13738:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   13739: }
                   13740: 
                   13741: ############################################################
                   13742: ############################################################
                   13743: 
                   13744: =pod
                   13745: 
1.648     raeburn  13746: =item * &DrawXYYGraph()
1.138     matthew  13747: 
                   13748: Facilitates the plotting of data in an XY graph with two Y axes.
                   13749: Puts plot definition data into the users environment in order for 
                   13750: graph.png to plot it.  Returns an <img> tag for the plot.
                   13751: 
                   13752: Inputs:
                   13753: 
                   13754: =over 4
                   13755: 
                   13756: =item $Title: string, the title of the plot
                   13757: 
                   13758: =item $xlabel: string, text describing the X-axis of the plot
                   13759: 
                   13760: =item $ylabel: string, text describing the Y-axis of the plot
                   13761: 
                   13762: =item $colors: Array ref containing the hex color codes for the data to be 
                   13763: plotted in.  If undefined, default values will be used.
                   13764: 
                   13765: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   13766: 
                   13767: =item $Ydata1: The first data set
                   13768: 
                   13769: =item $Min1: The minimum value of the left Y-axis
                   13770: 
                   13771: =item $Max1: The maximum value of the left Y-axis
                   13772: 
                   13773: =item $Ydata2: The second data set
                   13774: 
                   13775: =item $Min2: The minimum value of the right Y-axis
                   13776: 
                   13777: =item $Max2: The maximum value of the left Y-axis
                   13778: 
                   13779: =item %Values: hash indicating or overriding any default values which are 
                   13780: passed to graph.png.  
                   13781: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   13782: 
                   13783: =back
                   13784: 
                   13785: Returns:
                   13786: 
                   13787: An <img> tag which references graph.png and the appropriate identifying
                   13788: information for the plot.
1.136     matthew  13789: 
                   13790: =cut
                   13791: 
                   13792: ############################################################
                   13793: ############################################################
1.137     matthew  13794: sub DrawXYYGraph {
                   13795:     my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
                   13796:                                         $Ydata2,$Min2,$Max2,%Values)=@_;
1.136     matthew  13797:     #
                   13798:     # Create the identifier for the graph
                   13799:     my $identifier = &get_cgi_id();
                   13800:     my $id = 'cgi.'.$identifier;
                   13801:     #
                   13802:     $Title  = '' if (! defined($Title));
                   13803:     $xlabel = '' if (! defined($xlabel));
                   13804:     $ylabel = '' if (! defined($ylabel));
                   13805:     my %ValuesHash = 
                   13806:         (
1.369     www      13807:          $id.'.title'  => &escape($Title),
                   13808:          $id.'.xlabel' => &escape($xlabel),
                   13809:          $id.'.ylabel' => &escape($ylabel),
1.136     matthew  13810:          $id.'.labels' => join(',',@$Xlabels),
                   13811:          $id.'.PlotType' => 'XY',
                   13812:          $id.'.NumSets' => 2,
1.137     matthew  13813:          $id.'.two_axes' => 1,
                   13814:          $id.'.y1_max_value' => $Max1,
                   13815:          $id.'.y1_min_value' => $Min1,
                   13816:          $id.'.y2_max_value' => $Max2,
                   13817:          $id.'.y2_min_value' => $Min2,
1.136     matthew  13818:          );
                   13819:     #
1.137     matthew  13820:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   13821:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   13822:     }
                   13823:     #
                   13824:     if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
                   13825:         ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136     matthew  13826:         return '';
                   13827:     }
                   13828:     my $NumSets=1;
1.137     matthew  13829:     foreach my $array ($Ydata1,$Ydata2){
1.136     matthew  13830:         next if (! ref($array));
                   13831:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137     matthew  13832:     }
                   13833:     #
                   13834:     # Deal with other parameters
                   13835:     while (my ($key,$value) = each(%Values)) {
                   13836:         $ValuesHash{$id.'.'.$key} = $value;
1.136     matthew  13837:     }
                   13838:     #
1.646     raeburn  13839:     &Apache::lonnet::appenv(\%ValuesHash);
1.130     albertel 13840:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139     matthew  13841: }
                   13842: 
                   13843: ############################################################
                   13844: ############################################################
                   13845: 
                   13846: =pod
                   13847: 
1.157     matthew  13848: =back 
                   13849: 
1.139     matthew  13850: =head1 Statistics helper routines?  
                   13851: 
                   13852: Bad place for them but what the hell.
                   13853: 
1.157     matthew  13854: =over 4
                   13855: 
1.648     raeburn  13856: =item * &chartlink()
1.139     matthew  13857: 
                   13858: Returns a link to the chart for a specific student.  
                   13859: 
                   13860: Inputs:
                   13861: 
                   13862: =over 4
                   13863: 
                   13864: =item $linktext: The text of the link
                   13865: 
                   13866: =item $sname: The students username
                   13867: 
                   13868: =item $sdomain: The students domain
                   13869: 
                   13870: =back
                   13871: 
1.157     matthew  13872: =back
                   13873: 
1.139     matthew  13874: =cut
                   13875: 
                   13876: ############################################################
                   13877: ############################################################
                   13878: sub chartlink {
                   13879:     my ($linktext, $sname, $sdomain) = @_;
                   13880:     my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369     www      13881:         '&amp;SelectedStudent='.&escape($sname.':'.$sdomain).
1.219     albertel 13882:         '&amp;chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139     matthew  13883:        '">'.$linktext.'</a>';
1.153     matthew  13884: }
                   13885: 
                   13886: #######################################################
                   13887: #######################################################
                   13888: 
                   13889: =pod
                   13890: 
                   13891: =head1 Course Environment Routines
1.157     matthew  13892: 
                   13893: =over 4
1.153     matthew  13894: 
1.648     raeburn  13895: =item * &restore_course_settings()
1.153     matthew  13896: 
1.648     raeburn  13897: =item * &store_course_settings()
1.153     matthew  13898: 
                   13899: Restores/Store indicated form parameters from the course environment.
                   13900: Will not overwrite existing values of the form parameters.
                   13901: 
                   13902: Inputs: 
                   13903: a scalar describing the data (e.g. 'chart', 'problem_analysis')
                   13904: 
                   13905: a hash ref describing the data to be stored.  For example:
                   13906:    
                   13907: %Save_Parameters = ('Status' => 'scalar',
                   13908:     'chartoutputmode' => 'scalar',
                   13909:     'chartoutputdata' => 'scalar',
                   13910:     'Section' => 'array',
1.373     raeburn  13911:     'Group' => 'array',
1.153     matthew  13912:     'StudentData' => 'array',
                   13913:     'Maps' => 'array');
                   13914: 
                   13915: Returns: both routines return nothing
                   13916: 
1.631     raeburn  13917: =back
                   13918: 
1.153     matthew  13919: =cut
                   13920: 
                   13921: #######################################################
                   13922: #######################################################
                   13923: sub store_course_settings {
1.496     albertel 13924:     return &store_settings($env{'request.course.id'},@_);
                   13925: }
                   13926: 
                   13927: sub store_settings {
1.153     matthew  13928:     # save to the environment
                   13929:     # appenv the same items, just to be safe
1.300     albertel 13930:     my $udom  = $env{'user.domain'};
                   13931:     my $uname = $env{'user.name'};
1.496     albertel 13932:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  13933:     my %SaveHash;
                   13934:     my %AppHash;
                   13935:     while (my ($setting,$type) = each(%$Settings)) {
1.496     albertel 13936:         my $basename = join('.','internal',$context,$prefix,$setting);
1.300     albertel 13937:         my $envname = 'environment.'.$basename;
1.258     albertel 13938:         if (exists($env{'form.'.$setting})) {
1.153     matthew  13939:             # Save this value away
                   13940:             if ($type eq 'scalar' &&
1.258     albertel 13941:                 (! exists($env{$envname}) || 
                   13942:                  $env{$envname} ne $env{'form.'.$setting})) {
                   13943:                 $SaveHash{$basename} = $env{'form.'.$setting};
                   13944:                 $AppHash{$envname}   = $env{'form.'.$setting};
1.153     matthew  13945:             } elsif ($type eq 'array') {
                   13946:                 my $stored_form;
1.258     albertel 13947:                 if (ref($env{'form.'.$setting})) {
1.153     matthew  13948:                     $stored_form = join(',',
                   13949:                                         map {
1.369     www      13950:                                             &escape($_);
1.258     albertel 13951:                                         } sort(@{$env{'form.'.$setting}}));
1.153     matthew  13952:                 } else {
                   13953:                     $stored_form = 
1.369     www      13954:                         &escape($env{'form.'.$setting});
1.153     matthew  13955:                 }
                   13956:                 # Determine if the array contents are the same.
1.258     albertel 13957:                 if ($stored_form ne $env{$envname}) {
1.153     matthew  13958:                     $SaveHash{$basename} = $stored_form;
                   13959:                     $AppHash{$envname}   = $stored_form;
                   13960:                 }
                   13961:             }
                   13962:         }
                   13963:     }
                   13964:     my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300     albertel 13965:                                           $udom,$uname);
1.153     matthew  13966:     if ($put_result !~ /^(ok|delayed)/) {
                   13967:         &Apache::lonnet::logthis('unable to save form parameters, '.
                   13968:                                  'got error:'.$put_result);
                   13969:     }
                   13970:     # Make sure these settings stick around in this session, too
1.646     raeburn  13971:     &Apache::lonnet::appenv(\%AppHash);
1.153     matthew  13972:     return;
                   13973: }
                   13974: 
                   13975: sub restore_course_settings {
1.499     albertel 13976:     return &restore_settings($env{'request.course.id'},@_);
1.496     albertel 13977: }
                   13978: 
                   13979: sub restore_settings {
                   13980:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  13981:     while (my ($setting,$type) = each(%$Settings)) {
1.258     albertel 13982:         next if (exists($env{'form.'.$setting}));
1.496     albertel 13983:         my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153     matthew  13984:             '.'.$setting;
1.258     albertel 13985:         if (exists($env{$envname})) {
1.153     matthew  13986:             if ($type eq 'scalar') {
1.258     albertel 13987:                 $env{'form.'.$setting} = $env{$envname};
1.153     matthew  13988:             } elsif ($type eq 'array') {
1.258     albertel 13989:                 $env{'form.'.$setting} = [ 
1.153     matthew  13990:                                            map { 
1.369     www      13991:                                                &unescape($_); 
1.258     albertel 13992:                                            } split(',',$env{$envname})
1.153     matthew  13993:                                            ];
                   13994:             }
                   13995:         }
                   13996:     }
1.127     matthew  13997: }
                   13998: 
1.618     raeburn  13999: #######################################################
                   14000: #######################################################
                   14001: 
                   14002: =pod
                   14003: 
                   14004: =head1 Domain E-mail Routines  
                   14005: 
                   14006: =over 4
                   14007: 
1.648     raeburn  14008: =item * &build_recipient_list()
1.618     raeburn  14009: 
1.1144    raeburn  14010: Build recipient lists for following types of e-mail:
1.766     raeburn  14011: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.1144    raeburn  14012: (d) Help requests, (e) Course requests needing approval, (f) loncapa
                   14013: module change checking, student/employee ID conflict checks, as
                   14014: generated by lonerrorhandler.pm, CHECKRPMS, loncron,
                   14015: lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
1.618     raeburn  14016: 
                   14017: Inputs:
1.619     raeburn  14018: defmail (scalar - email address of default recipient), 
1.1144    raeburn  14019: mailing type (scalar: errormail, packagesmail, helpdeskmail,
                   14020: requestsmail, updatesmail, or idconflictsmail).
                   14021: 
1.619     raeburn  14022: defdom (domain for which to retrieve configuration settings),
1.1144    raeburn  14023: 
1.619     raeburn  14024: origmail (scalar - email address of recipient from loncapa.conf, 
                   14025: i.e., predates configuration by DC via domainprefs.pm 
1.618     raeburn  14026: 
1.655     raeburn  14027: Returns: comma separated list of addresses to which to send e-mail.
                   14028: 
                   14029: =back
1.618     raeburn  14030: 
                   14031: =cut
                   14032: 
                   14033: ############################################################
                   14034: ############################################################
                   14035: sub build_recipient_list {
1.619     raeburn  14036:     my ($defmail,$mailing,$defdom,$origmail) = @_;
1.618     raeburn  14037:     my @recipients;
                   14038:     my $otheremails;
                   14039:     my %domconfig =
                   14040:          &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
                   14041:     if (ref($domconfig{'contacts'}) eq 'HASH') {
1.766     raeburn  14042:         if (exists($domconfig{'contacts'}{$mailing})) {
                   14043:             if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
                   14044:                 my @contacts = ('adminemail','supportemail');
                   14045:                 foreach my $item (@contacts) {
                   14046:                     if ($domconfig{'contacts'}{$mailing}{$item}) {
                   14047:                         my $addr = $domconfig{'contacts'}{$item}; 
                   14048:                         if (!grep(/^\Q$addr\E$/,@recipients)) {
                   14049:                             push(@recipients,$addr);
                   14050:                         }
1.619     raeburn  14051:                     }
1.766     raeburn  14052:                     $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
1.618     raeburn  14053:                 }
                   14054:             }
1.766     raeburn  14055:         } elsif ($origmail ne '') {
                   14056:             push(@recipients,$origmail);
1.618     raeburn  14057:         }
1.619     raeburn  14058:     } elsif ($origmail ne '') {
                   14059:         push(@recipients,$origmail);
1.618     raeburn  14060:     }
1.688     raeburn  14061:     if (defined($defmail)) {
                   14062:         if ($defmail ne '') {
                   14063:             push(@recipients,$defmail);
                   14064:         }
1.618     raeburn  14065:     }
                   14066:     if ($otheremails) {
1.619     raeburn  14067:         my @others;
                   14068:         if ($otheremails =~ /,/) {
                   14069:             @others = split(/,/,$otheremails);
1.618     raeburn  14070:         } else {
1.619     raeburn  14071:             push(@others,$otheremails);
                   14072:         }
                   14073:         foreach my $addr (@others) {
                   14074:             if (!grep(/^\Q$addr\E$/,@recipients)) {
                   14075:                 push(@recipients,$addr);
                   14076:             }
1.618     raeburn  14077:         }
                   14078:     }
1.619     raeburn  14079:     my $recipientlist = join(',',@recipients); 
1.618     raeburn  14080:     return $recipientlist;
                   14081: }
                   14082: 
1.127     matthew  14083: ############################################################
                   14084: ############################################################
1.154     albertel 14085: 
1.655     raeburn  14086: =pod
                   14087: 
1.1224    musolffc 14088: =over 4
                   14089: 
1.1223    musolffc 14090: =item * &mime_email()
                   14091: 
                   14092: Sends an email with a possible attachment
                   14093: 
                   14094: Inputs:
                   14095: 
                   14096: =over 4
                   14097: 
                   14098: from -              Sender's email address
                   14099: 
                   14100: to -                Email address of recipient
                   14101: 
                   14102: subject -           Subject of email
                   14103: 
                   14104: body -              Body of email
                   14105: 
                   14106: cc_string -         Carbon copy email address
                   14107: 
                   14108: bcc -               Blind carbon copy email address
                   14109: 
                   14110: type -              File type of attachment
                   14111: 
                   14112: attachment_path -   Path of file to be attached
                   14113: 
                   14114: file_name -         Name of file to be attached
                   14115: 
                   14116: attachment_text -   The body of an attachment of type "TEXT"
                   14117: 
                   14118: =back
                   14119: 
                   14120: =back
                   14121: 
                   14122: =cut
                   14123: 
                   14124: ############################################################
                   14125: ############################################################
                   14126: 
                   14127: sub mime_email {
                   14128:     my ($from, $to, $subject, $body, $cc_string, $bcc, $attachment_path, 
                   14129:         $file_name, $attachment_text) = @_;
                   14130:     my $msg = MIME::Lite->new(
                   14131:              From    => $from,
                   14132:              To      => $to,
                   14133:              Subject => $subject,
                   14134:              Type    =>'TEXT',
                   14135:              Data    => $body,
                   14136:              );
                   14137:     if ($cc_string ne '') {
                   14138:         $msg->add("Cc" => $cc_string);
                   14139:     }
                   14140:     if ($bcc ne '') {
                   14141:         $msg->add("Bcc" => $bcc);
                   14142:     }
                   14143:     $msg->attr("content-type"         => "text/plain");
                   14144:     $msg->attr("content-type.charset" => "UTF-8");
                   14145:     # Attach file if given
                   14146:     if ($attachment_path) {
                   14147:         unless ($file_name) {
                   14148:             if ($attachment_path =~ m-/([^/]+)$-) { $file_name = $1; }
                   14149:         }
                   14150:         my ($type, $encoding) = MIME::Types::by_suffix($attachment_path);
                   14151:         $msg->attach(Type     => $type,
                   14152:                      Path     => $attachment_path,
                   14153:                      Filename => $file_name
                   14154:                      );
                   14155:     # Otherwise attach text if given
                   14156:     } elsif ($attachment_text) {
                   14157:         $msg->attach(Type => 'TEXT',
                   14158:                      Data => $attachment_text);
                   14159:     }
                   14160:     # Send it
                   14161:     $msg->send('sendmail');
                   14162: }
                   14163: 
                   14164: ############################################################
                   14165: ############################################################
                   14166: 
                   14167: =pod
                   14168: 
1.655     raeburn  14169: =head1 Course Catalog Routines
                   14170: 
                   14171: =over 4
                   14172: 
                   14173: =item * &gather_categories()
                   14174: 
                   14175: Converts category definitions - keys of categories hash stored in  
                   14176: coursecategories in configuration.db on the primary library server in a 
                   14177: domain - to an array.  Also generates javascript and idx hash used to 
                   14178: generate Domain Coordinator interface for editing Course Categories.
                   14179: 
                   14180: Inputs:
1.663     raeburn  14181: 
1.655     raeburn  14182: categories (reference to hash of category definitions).
1.663     raeburn  14183: 
1.655     raeburn  14184: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   14185:       categories and subcategories).
1.663     raeburn  14186: 
1.655     raeburn  14187: idx (reference to hash of counters used in Domain Coordinator interface for 
                   14188:       editing Course Categories).
1.663     raeburn  14189: 
1.655     raeburn  14190: jsarray (reference to array of categories used to create Javascript arrays for
                   14191:          Domain Coordinator interface for editing Course Categories).
                   14192: 
                   14193: Returns: nothing
                   14194: 
                   14195: Side effects: populates cats, idx and jsarray. 
                   14196: 
                   14197: =cut
                   14198: 
                   14199: sub gather_categories {
                   14200:     my ($categories,$cats,$idx,$jsarray) = @_;
                   14201:     my %counters;
                   14202:     my $num = 0;
                   14203:     foreach my $item (keys(%{$categories})) {
                   14204:         my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
                   14205:         if ($container eq '' && $depth == 0) {
                   14206:             $cats->[$depth][$categories->{$item}] = $cat;
                   14207:         } else {
                   14208:             $cats->[$depth]{$container}[$categories->{$item}] = $cat;
                   14209:         }
                   14210:         my ($escitem,$tail) = split(/:/,$item,2);
                   14211:         if ($counters{$tail} eq '') {
                   14212:             $counters{$tail} = $num;
                   14213:             $num ++;
                   14214:         }
                   14215:         if (ref($idx) eq 'HASH') {
                   14216:             $idx->{$item} = $counters{$tail};
                   14217:         }
                   14218:         if (ref($jsarray) eq 'ARRAY') {
                   14219:             push(@{$jsarray->[$counters{$tail}]},$item);
                   14220:         }
                   14221:     }
                   14222:     return;
                   14223: }
                   14224: 
                   14225: =pod
                   14226: 
                   14227: =item * &extract_categories()
                   14228: 
                   14229: Used to generate breadcrumb trails for course categories.
                   14230: 
                   14231: Inputs:
1.663     raeburn  14232: 
1.655     raeburn  14233: categories (reference to hash of category definitions).
1.663     raeburn  14234: 
1.655     raeburn  14235: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   14236:       categories and subcategories).
1.663     raeburn  14237: 
1.655     raeburn  14238: trails (reference to array of breacrumb trails for each category).
1.663     raeburn  14239: 
1.655     raeburn  14240: allitems (reference to hash - key is category key 
                   14241:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  14242: 
1.655     raeburn  14243: idx (reference to hash of counters used in Domain Coordinator interface for
                   14244:       editing Course Categories).
1.663     raeburn  14245: 
1.655     raeburn  14246: jsarray (reference to array of categories used to create Javascript arrays for
                   14247:          Domain Coordinator interface for editing Course Categories).
                   14248: 
1.665     raeburn  14249: subcats (reference to hash of arrays containing all subcategories within each 
                   14250:          category, -recursive)
                   14251: 
1.655     raeburn  14252: Returns: nothing
                   14253: 
                   14254: Side effects: populates trails and allitems hash references.
                   14255: 
                   14256: =cut
                   14257: 
                   14258: sub extract_categories {
1.665     raeburn  14259:     my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_;
1.655     raeburn  14260:     if (ref($categories) eq 'HASH') {
                   14261:         &gather_categories($categories,$cats,$idx,$jsarray);
                   14262:         if (ref($cats->[0]) eq 'ARRAY') {
                   14263:             for (my $i=0; $i<@{$cats->[0]}; $i++) {
                   14264:                 my $name = $cats->[0][$i];
                   14265:                 my $item = &escape($name).'::0';
                   14266:                 my $trailstr;
                   14267:                 if ($name eq 'instcode') {
                   14268:                     $trailstr = &mt('Official courses (with institutional codes)');
1.919     raeburn  14269:                 } elsif ($name eq 'communities') {
                   14270:                     $trailstr = &mt('Communities');
1.655     raeburn  14271:                 } else {
                   14272:                     $trailstr = $name;
                   14273:                 }
                   14274:                 if ($allitems->{$item} eq '') {
                   14275:                     push(@{$trails},$trailstr);
                   14276:                     $allitems->{$item} = scalar(@{$trails})-1;
                   14277:                 }
                   14278:                 my @parents = ($name);
                   14279:                 if (ref($cats->[1]{$name}) eq 'ARRAY') {
                   14280:                     for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
                   14281:                         my $category = $cats->[1]{$name}[$j];
1.665     raeburn  14282:                         if (ref($subcats) eq 'HASH') {
                   14283:                             push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
                   14284:                         }
                   14285:                         &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats);
                   14286:                     }
                   14287:                 } else {
                   14288:                     if (ref($subcats) eq 'HASH') {
                   14289:                         $subcats->{$item} = [];
1.655     raeburn  14290:                     }
                   14291:                 }
                   14292:             }
                   14293:         }
                   14294:     }
                   14295:     return;
                   14296: }
                   14297: 
                   14298: =pod
                   14299: 
1.1162    raeburn  14300: =item * &recurse_categories()
1.655     raeburn  14301: 
                   14302: Recursively used to generate breadcrumb trails for course categories.
                   14303: 
                   14304: Inputs:
1.663     raeburn  14305: 
1.655     raeburn  14306: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   14307:       categories and subcategories).
1.663     raeburn  14308: 
1.655     raeburn  14309: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663     raeburn  14310: 
                   14311: category (current course category, for which breadcrumb trail is being generated).
                   14312: 
                   14313: trails (reference to array of breadcrumb trails for each category).
                   14314: 
1.655     raeburn  14315: allitems (reference to hash - key is category key
                   14316:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  14317: 
1.655     raeburn  14318: parents (array containing containers directories for current category, 
                   14319:          back to top level). 
                   14320: 
                   14321: Returns: nothing
                   14322: 
                   14323: Side effects: populates trails and allitems hash references
                   14324: 
                   14325: =cut
                   14326: 
                   14327: sub recurse_categories {
1.665     raeburn  14328:     my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_;
1.655     raeburn  14329:     my $shallower = $depth - 1;
                   14330:     if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
                   14331:         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
                   14332:             my $name = $cats->[$depth]{$category}[$k];
                   14333:             my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   14334:             my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   14335:             if ($allitems->{$item} eq '') {
                   14336:                 push(@{$trails},$trailstr);
                   14337:                 $allitems->{$item} = scalar(@{$trails})-1;
                   14338:             }
                   14339:             my $deeper = $depth+1;
                   14340:             push(@{$parents},$category);
1.665     raeburn  14341:             if (ref($subcats) eq 'HASH') {
                   14342:                 my $subcat = &escape($name).':'.$category.':'.$depth;
                   14343:                 for (my $j=@{$parents}; $j>=0; $j--) {
                   14344:                     my $higher;
                   14345:                     if ($j > 0) {
                   14346:                         $higher = &escape($parents->[$j]).':'.
                   14347:                                   &escape($parents->[$j-1]).':'.$j;
                   14348:                     } else {
                   14349:                         $higher = &escape($parents->[$j]).'::'.$j;
                   14350:                     }
                   14351:                     push(@{$subcats->{$higher}},$subcat);
                   14352:                 }
                   14353:             }
                   14354:             &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
                   14355:                                 $subcats);
1.655     raeburn  14356:             pop(@{$parents});
                   14357:         }
                   14358:     } else {
                   14359:         my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
                   14360:         my $trailstr = join(' -&gt; ',(@{$parents},$category));
                   14361:         if ($allitems->{$item} eq '') {
                   14362:             push(@{$trails},$trailstr);
                   14363:             $allitems->{$item} = scalar(@{$trails})-1;
                   14364:         }
                   14365:     }
                   14366:     return;
                   14367: }
                   14368: 
1.663     raeburn  14369: =pod
                   14370: 
1.1162    raeburn  14371: =item * &assign_categories_table()
1.663     raeburn  14372: 
                   14373: Create a datatable for display of hierarchical categories in a domain,
                   14374: with checkboxes to allow a course to be categorized. 
                   14375: 
                   14376: Inputs:
                   14377: 
                   14378: cathash - reference to hash of categories defined for the domain (from
                   14379:           configuration.db)
                   14380: 
                   14381: currcat - scalar with an & separated list of categories assigned to a course. 
                   14382: 
1.919     raeburn  14383: type    - scalar contains course type (Course or Community).
                   14384: 
1.663     raeburn  14385: Returns: $output (markup to be displayed) 
                   14386: 
                   14387: =cut
                   14388: 
                   14389: sub assign_categories_table {
1.919     raeburn  14390:     my ($cathash,$currcat,$type) = @_;
1.663     raeburn  14391:     my $output;
                   14392:     if (ref($cathash) eq 'HASH') {
                   14393:         my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth);
                   14394:         &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray);
                   14395:         $maxdepth = scalar(@cats);
                   14396:         if (@cats > 0) {
                   14397:             my $itemcount = 0;
                   14398:             if (ref($cats[0]) eq 'ARRAY') {
                   14399:                 my @currcategories;
                   14400:                 if ($currcat ne '') {
                   14401:                     @currcategories = split('&',$currcat);
                   14402:                 }
1.919     raeburn  14403:                 my $table;
1.663     raeburn  14404:                 for (my $i=0; $i<@{$cats[0]}; $i++) {
                   14405:                     my $parent = $cats[0][$i];
1.919     raeburn  14406:                     next if ($parent eq 'instcode');
                   14407:                     if ($type eq 'Community') {
                   14408:                         next unless ($parent eq 'communities');
                   14409:                     } else {
                   14410:                         next if ($parent eq 'communities');
                   14411:                     }
1.663     raeburn  14412:                     my $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   14413:                     my $item = &escape($parent).'::0';
                   14414:                     my $checked = '';
                   14415:                     if (@currcategories > 0) {
                   14416:                         if (grep(/^\Q$item\E$/,@currcategories)) {
1.772     bisitz   14417:                             $checked = ' checked="checked"';
1.663     raeburn  14418:                         }
                   14419:                     }
1.919     raeburn  14420:                     my $parent_title = $parent;
                   14421:                     if ($parent eq 'communities') {
                   14422:                         $parent_title = &mt('Communities');
                   14423:                     }
                   14424:                     $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
                   14425:                               '<input type="checkbox" name="usecategory" value="'.
                   14426:                               $item.'"'.$checked.' />'.$parent_title.'</span>'.
                   14427:                               '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663     raeburn  14428:                     my $depth = 1;
                   14429:                     push(@path,$parent);
1.919     raeburn  14430:                     $table .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories);
1.663     raeburn  14431:                     pop(@path);
1.919     raeburn  14432:                     $table .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
1.663     raeburn  14433:                     $itemcount ++;
                   14434:                 }
1.919     raeburn  14435:                 if ($itemcount) {
                   14436:                     $output = &Apache::loncommon::start_data_table().
                   14437:                               $table.
                   14438:                               &Apache::loncommon::end_data_table();
                   14439:                 }
1.663     raeburn  14440:             }
                   14441:         }
                   14442:     }
                   14443:     return $output;
                   14444: }
                   14445: 
                   14446: =pod
                   14447: 
1.1162    raeburn  14448: =item * &assign_category_rows()
1.663     raeburn  14449: 
                   14450: Create a datatable row for display of nested categories in a domain,
                   14451: with checkboxes to allow a course to be categorized,called recursively.
                   14452: 
                   14453: Inputs:
                   14454: 
                   14455: itemcount - track row number for alternating colors
                   14456: 
                   14457: cats - reference to array of arrays/hashes which encapsulates hierarchy of
                   14458:       categories and subcategories.
                   14459: 
                   14460: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
                   14461: 
                   14462: parent - parent of current category item
                   14463: 
                   14464: path - Array containing all categories back up through the hierarchy from the
                   14465:        current category to the top level.
                   14466: 
                   14467: currcategories - reference to array of current categories assigned to the course
                   14468: 
                   14469: Returns: $output (markup to be displayed).
                   14470: 
                   14471: =cut
                   14472: 
                   14473: sub assign_category_rows {
                   14474:     my ($itemcount,$cats,$depth,$parent,$path,$currcategories) = @_;
                   14475:     my ($text,$name,$item,$chgstr);
                   14476:     if (ref($cats) eq 'ARRAY') {
                   14477:         my $maxdepth = scalar(@{$cats});
                   14478:         if (ref($cats->[$depth]) eq 'HASH') {
                   14479:             if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
                   14480:                 my $numchildren = @{$cats->[$depth]{$parent}};
                   14481:                 my $css_class = $itemcount%2?' class="LC_odd_row"':'';
1.1145    raeburn  14482:                 $text .= '<td><table class="LC_data_table">';
1.663     raeburn  14483:                 for (my $j=0; $j<$numchildren; $j++) {
                   14484:                     $name = $cats->[$depth]{$parent}[$j];
                   14485:                     $item = &escape($name).':'.&escape($parent).':'.$depth;
                   14486:                     my $deeper = $depth+1;
                   14487:                     my $checked = '';
                   14488:                     if (ref($currcategories) eq 'ARRAY') {
                   14489:                         if (@{$currcategories} > 0) {
                   14490:                             if (grep(/^\Q$item\E$/,@{$currcategories})) {
1.772     bisitz   14491:                                 $checked = ' checked="checked"';
1.663     raeburn  14492:                             }
                   14493:                         }
                   14494:                     }
1.664     raeburn  14495:                     $text .= '<tr><td><span class="LC_nobreak"><label>'.
                   14496:                              '<input type="checkbox" name="usecategory" value="'.
1.675     raeburn  14497:                              $item.'"'.$checked.' />'.$name.'</label></span>'.
                   14498:                              '<input type="hidden" name="catname" value="'.$name.'" />'.
                   14499:                              '</td><td>';
1.663     raeburn  14500:                     if (ref($path) eq 'ARRAY') {
                   14501:                         push(@{$path},$name);
                   14502:                         $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories);
                   14503:                         pop(@{$path});
                   14504:                     }
                   14505:                     $text .= '</td></tr>';
                   14506:                 }
                   14507:                 $text .= '</table></td>';
                   14508:             }
                   14509:         }
                   14510:     }
                   14511:     return $text;
                   14512: }
                   14513: 
1.1181    raeburn  14514: =pod
                   14515: 
                   14516: =back
                   14517: 
                   14518: =cut
                   14519: 
1.655     raeburn  14520: ############################################################
                   14521: ############################################################
                   14522: 
                   14523: 
1.443     albertel 14524: sub commit_customrole {
1.664     raeburn  14525:     my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
1.630     raeburn  14526:     my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443     albertel 14527:                          ($start?', '.&mt('starting').' '.localtime($start):'').
                   14528:                          ($end?', ending '.localtime($end):'').': <b>'.
                   14529:               &Apache::lonnet::assigncustomrole(
1.664     raeburn  14530:                  $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
1.443     albertel 14531:                  '</b><br />';
                   14532:     return $output;
                   14533: }
                   14534: 
                   14535: sub commit_standardrole {
1.1116    raeburn  14536:     my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;
1.541     raeburn  14537:     my ($output,$logmsg,$linefeed);
                   14538:     if ($context eq 'auto') {
                   14539:         $linefeed = "\n";
                   14540:     } else {
                   14541:         $linefeed = "<br />\n";
                   14542:     }  
1.443     albertel 14543:     if ($three eq 'st') {
1.541     raeburn  14544:         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
1.1116    raeburn  14545:                                          $one,$two,$sec,$context,$credits);
1.541     raeburn  14546:         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
1.626     raeburn  14547:             ($result eq 'unknown_course') || ($result eq 'refused')) {
                   14548:             $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
1.443     albertel 14549:         } else {
1.541     raeburn  14550:             $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443     albertel 14551:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  14552:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
                   14553:             if ($context eq 'auto') {
                   14554:                 $output .= $result.$linefeed.&mt('Add to classlist').': ok';
                   14555:             } else {
                   14556:                $output .= '<b>'.$result.'</b>'.$linefeed.
                   14557:                &mt('Add to classlist').': <b>ok</b>';
                   14558:             }
                   14559:             $output .= $linefeed;
1.443     albertel 14560:         }
                   14561:     } else {
                   14562:         $output = &mt('Assigning').' '.$three.' in '.$url.
                   14563:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  14564:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652     raeburn  14565:         my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541     raeburn  14566:         if ($context eq 'auto') {
                   14567:             $output .= $result.$linefeed;
                   14568:         } else {
                   14569:             $output .= '<b>'.$result.'</b>'.$linefeed;
                   14570:         }
1.443     albertel 14571:     }
                   14572:     return $output;
                   14573: }
                   14574: 
                   14575: sub commit_studentrole {
1.1116    raeburn  14576:     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
                   14577:         $credits) = @_;
1.626     raeburn  14578:     my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541     raeburn  14579:     if ($context eq 'auto') {
                   14580:         $linefeed = "\n";
                   14581:     } else {
                   14582:         $linefeed = '<br />'."\n";
                   14583:     }
1.443     albertel 14584:     if (defined($one) && defined($two)) {
                   14585:         my $cid=$one.'_'.$two;
                   14586:         my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
                   14587:         my $secchange = 0;
                   14588:         my $expire_role_result;
                   14589:         my $modify_section_result;
1.628     raeburn  14590:         if ($oldsec ne '-1') { 
                   14591:             if ($oldsec ne $sec) {
1.443     albertel 14592:                 $secchange = 1;
1.628     raeburn  14593:                 my $now = time;
1.443     albertel 14594:                 my $uurl='/'.$cid;
                   14595:                 $uurl=~s/\_/\//g;
                   14596:                 if ($oldsec) {
                   14597:                     $uurl.='/'.$oldsec;
                   14598:                 }
1.626     raeburn  14599:                 $oldsecurl = $uurl;
1.628     raeburn  14600:                 $expire_role_result = 
1.652     raeburn  14601:                     &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628     raeburn  14602:                 if ($env{'request.course.sec'} ne '') { 
                   14603:                     if ($expire_role_result eq 'refused') {
                   14604:                         my @roles = ('st');
                   14605:                         my @statuses = ('previous');
                   14606:                         my @roledoms = ($one);
                   14607:                         my $withsec = 1;
                   14608:                         my %roleshash = 
                   14609:                             &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
                   14610:                                               \@statuses,\@roles,\@roledoms,$withsec);
                   14611:                         if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
                   14612:                             my ($oldstart,$oldend) = 
                   14613:                                 split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
                   14614:                             if ($oldend > 0 && $oldend <= $now) {
                   14615:                                 $expire_role_result = 'ok';
                   14616:                             }
                   14617:                         }
                   14618:                     }
                   14619:                 }
1.443     albertel 14620:                 $result = $expire_role_result;
                   14621:             }
                   14622:         }
                   14623:         if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.1116    raeburn  14624:             $modify_section_result = 
                   14625:                 &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
                   14626:                                                            undef,undef,undef,$sec,
                   14627:                                                            $end,$start,'','',$cid,
                   14628:                                                            '',$context,$credits);
1.443     albertel 14629:             if ($modify_section_result =~ /^ok/) {
                   14630:                 if ($secchange == 1) {
1.628     raeburn  14631:                     if ($sec eq '') {
                   14632:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
                   14633:                     } else {
                   14634:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
                   14635:                     }
1.443     albertel 14636:                 } elsif ($oldsec eq '-1') {
1.628     raeburn  14637:                     if ($sec eq '') {
                   14638:                         $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
                   14639:                     } else {
                   14640:                         $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   14641:                     }
1.443     albertel 14642:                 } else {
1.628     raeburn  14643:                     if ($sec eq '') {
                   14644:                         $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
                   14645:                     } else {
                   14646:                         $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   14647:                     }
1.443     albertel 14648:                 }
                   14649:             } else {
1.1115    raeburn  14650:                 if ($secchange) { 
1.628     raeburn  14651:                     $$logmsg .= &mt('Error when attempting section change for [_1] from old section "[_2]" to new section: "[_3]" in course [_4] -error:',$uname,$oldsec,$sec,$cid).' '.$modify_section_result.$linefeed;
                   14652:                 } else {
                   14653:                     $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
                   14654:                 }
1.443     albertel 14655:             }
                   14656:             $result = $modify_section_result;
                   14657:         } elsif ($secchange == 1) {
1.628     raeburn  14658:             if ($oldsec eq '') {
1.1103    raeburn  14659:                 $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_2] -error: ',$uname,$cid).' '.$expire_role_result.$linefeed;
1.628     raeburn  14660:             } else {
                   14661:                 $$logmsg .= &mt('Error when attempting to expire existing role for [_1] in section [_2] in course [_3] -error: ',$uname,$oldsec,$cid).' '.$expire_role_result.$linefeed;
                   14662:             }
1.626     raeburn  14663:             if ($expire_role_result eq 'refused') {
                   14664:                 my $newsecurl = '/'.$cid;
                   14665:                 $newsecurl =~ s/\_/\//g;
                   14666:                 if ($sec ne '') {
                   14667:                     $newsecurl.='/'.$sec;
                   14668:                 }
                   14669:                 if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
                   14670:                     if ($sec eq '') {
                   14671:                         $$logmsg .= &mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments unaffiliated with any section.',$sec).$linefeed;
                   14672:                     } else {
                   14673:                         $$logmsg .= &mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments in other sections.',$sec).$linefeed;
                   14674:                     }
                   14675:                 }
                   14676:             }
1.443     albertel 14677:         }
                   14678:     } else {
1.626     raeburn  14679:         $$logmsg .= &mt('Incomplete course id defined.').$linefeed.&mt('Addition of user [_1] from domain [_2] to course [_3], section [_4] not completed.',$uname,$udom,$one.'_'.$two,$sec).$linefeed;
1.443     albertel 14680:         $result = "error: incomplete course id\n";
                   14681:     }
                   14682:     return $result;
                   14683: }
                   14684: 
1.1108    raeburn  14685: sub show_role_extent {
                   14686:     my ($scope,$context,$role) = @_;
                   14687:     $scope =~ s{^/}{};
                   14688:     my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
                   14689:     push(@courseroles,'co');
                   14690:     my @authorroles = &Apache::lonuserutils::roles_by_context('author');
                   14691:     if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
                   14692:         $scope =~ s{/}{_};
                   14693:         return '<span class="LC_cusr_emph">'.$env{'course.'.$scope.'.description'}.'</span>';
                   14694:     } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
                   14695:         my ($audom,$auname) = split(/\//,$scope);
                   14696:         return &mt('[_1] Author Space','<span class="LC_cusr_emph">'.
                   14697:                    &Apache::loncommon::plainname($auname,$audom).'</span>');
                   14698:     } else {
                   14699:         $scope =~ s{/$}{};
                   14700:         return &mt('Domain: [_1]','<span class="LC_cusr_emph">'.
                   14701:                    &Apache::lonnet::domain($scope,'description').'</span>');
                   14702:     }
                   14703: }
                   14704: 
1.443     albertel 14705: ############################################################
                   14706: ############################################################
                   14707: 
1.566     albertel 14708: sub check_clone {
1.578     raeburn  14709:     my ($args,$linefeed) = @_;
1.566     albertel 14710:     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
                   14711:     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
                   14712:     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
                   14713:     my $clonemsg;
                   14714:     my $can_clone = 0;
1.944     raeburn  14715:     my $lctype = lc($args->{'crstype'});
1.908     raeburn  14716:     if ($lctype ne 'community') {
                   14717:         $lctype = 'course';
                   14718:     }
1.566     albertel 14719:     if ($clonehome eq 'no_host') {
1.944     raeburn  14720:         if ($args->{'crstype'} eq 'Community') {
1.908     raeburn  14721:             $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
                   14722:         } else {
                   14723:             $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'});
                   14724:         }     
1.566     albertel 14725:     } else {
                   14726: 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.944     raeburn  14727:         if ($args->{'crstype'} eq 'Community') {
1.908     raeburn  14728:             if ($clonedesc{'type'} ne 'Community') {
                   14729:                  $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
                   14730:                 return ($can_clone, $clonemsg, $cloneid, $clonehome);
                   14731:             }
                   14732:         }
1.882     raeburn  14733: 	if (($env{'request.role.domain'} eq $args->{'clonedomain'}) && 
                   14734:             (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.566     albertel 14735: 	    $can_clone = 1;
                   14736: 	} else {
1.1221    raeburn  14737: 	    my %clonehash = &Apache::lonnet::get('environment',['cloners','internal.coursecode'],
1.566     albertel 14738: 						 $args->{'clonedomain'},$args->{'clonecourse'});
1.1221    raeburn  14739:             if ($clonehash{'cloners'} eq '') {
                   14740:                 my %domdefs = &Apache::lonnet::get_domain_defaults($args->{'course_domain'});
                   14741:                 if ($domdefs{'canclone'}) {
                   14742:                     unless ($domdefs{'canclone'} eq 'none') {
                   14743:                         if ($domdefs{'canclone'} eq 'domain') {
                   14744:                             if ($args->{'ccdomain'} eq $args->{'clonedomain'}) {
                   14745:                                 $can_clone = 1;
                   14746:                             }
                   14747:                         } elsif (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && 
                   14748:                                  ($args->{'clonedomain'} eq  $args->{'course_domain'})) {
                   14749:                             if (&Apache::lonnet::default_instcode_cloning($args->{'clonedomain'},$domdefs{'canclone'},
                   14750:                                                                           $clonehash{'internal.coursecode'},$args->{'crscode'})) {
                   14751:                                 $can_clone = 1;
                   14752:                             }
                   14753:                         }
                   14754:                     }
                   14755:                 }
1.578     raeburn  14756:             } else {
1.1221    raeburn  14757: 	        my @cloners = split(/,/,$clonehash{'cloners'});
                   14758:                 if (grep(/^\*$/,@cloners)) {
1.942     raeburn  14759:                     $can_clone = 1;
1.1221    raeburn  14760:                 } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
1.942     raeburn  14761:                     $can_clone = 1;
1.1225    raeburn  14762:                 } elsif (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners)) {
                   14763:                     $can_clone = 1;
1.1221    raeburn  14764:                 }
                   14765:                 unless ($can_clone) {
1.1225    raeburn  14766:                     if (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && 
                   14767:                         ($args->{'clonedomain'} eq  $args->{'course_domain'})) {
1.1221    raeburn  14768:                         my (%gotdomdefaults,%gotcodedefaults);
                   14769:                         foreach my $cloner (@cloners) {
                   14770:                             if (($cloner ne '*') && ($cloner !~ /^\*\:$match_domain$/) &&
                   14771:                                 ($cloner !~ /^$match_username\:$match_domain$/) && ($cloner ne '')) {
                   14772:                                 my (%codedefaults,@code_order);
                   14773:                                 if (ref($gotcodedefaults{$args->{'clonedomain'}}) eq 'HASH') {
                   14774:                                     if (ref($gotcodedefaults{$args->{'clonedomain'}}{'defaults'}) eq 'HASH') {
                   14775:                                         %codedefaults = %{$gotcodedefaults{$args->{'clonedomain'}}{'defaults'}};
                   14776:                                     }
                   14777:                                     if (ref($gotcodedefaults{$args->{'clonedomain'}}{'order'}) eq 'ARRAY') {
                   14778:                                         @code_order = @{$gotcodedefaults{$args->{'clonedomain'}}{'order'}};
                   14779:                                     }
                   14780:                                 } else {
                   14781:                                     &Apache::lonnet::auto_instcode_defaults($args->{'clonedomain'},
                   14782:                                                                             \%codedefaults,
                   14783:                                                                             \@code_order);
                   14784:                                     $gotcodedefaults{$args->{'clonedomain'}}{'defaults'} = \%codedefaults;
                   14785:                                     $gotcodedefaults{$args->{'clonedomain'}}{'order'} = \@code_order;
                   14786:                                 }
                   14787:                                 if (@code_order > 0) {
                   14788:                                     if (&Apache::lonnet::check_instcode_cloning(\%codedefaults,\@code_order,
                   14789:                                                                                 $cloner,$clonehash{'internal.coursecode'},
                   14790:                                                                                 $args->{'crscode'})) {
                   14791:                                         $can_clone = 1;
                   14792:                                         last;
                   14793:                                     }
                   14794:                                 }
                   14795:                             }
                   14796:                         }
                   14797:                     }
1.1225    raeburn  14798:                 }
                   14799:             }
                   14800:             unless ($can_clone) {
                   14801:                 my $ccrole = 'cc';
                   14802:                 if ($args->{'crstype'} eq 'Community') {
                   14803:                     $ccrole = 'co';
                   14804:                 }
                   14805: 	        my %roleshash =
                   14806: 		    &Apache::lonnet::get_my_roles($args->{'ccuname'},
                   14807: 					          $args->{'ccdomain'},
                   14808:                                                   'userroles',['active'],[$ccrole],
                   14809: 					          [$args->{'clonedomain'}]);
                   14810: 	        if ($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) {
                   14811:                     $can_clone = 1;
                   14812:                 } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},
                   14813:                                                           $args->{'ccuname'},$args->{'ccdomain'})) {
                   14814:                     $can_clone = 1;
1.1221    raeburn  14815:                 }
                   14816:             }
                   14817:             unless ($can_clone) {
                   14818:                 if ($args->{'crstype'} eq 'Community') {
                   14819:                     $clonemsg = &mt('No new community created.').$linefeed.&mt('The new community could not be cloned from the existing community because the new community owner ([_1]) does not have cloning rights in the existing community ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
1.942     raeburn  14820:                 } else {
1.1221    raeburn  14821:                     $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'});
                   14822:                 }
1.566     albertel 14823: 	    }
1.578     raeburn  14824:         }
1.566     albertel 14825:     }
                   14826:     return ($can_clone, $clonemsg, $cloneid, $clonehome);
                   14827: }
                   14828: 
1.444     albertel 14829: sub construct_course {
1.1166    raeburn  14830:     my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,$cnum,$category,$coderef) = @_;
1.444     albertel 14831:     my $outcome;
1.541     raeburn  14832:     my $linefeed =  '<br />'."\n";
                   14833:     if ($context eq 'auto') {
                   14834:         $linefeed = "\n";
                   14835:     }
1.566     albertel 14836: 
                   14837: #
                   14838: # Are we cloning?
                   14839: #
                   14840:     my ($can_clone, $clonemsg, $cloneid, $clonehome);
                   14841:     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578     raeburn  14842: 	($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566     albertel 14843: 	if ($context ne 'auto') {
1.578     raeburn  14844:             if ($clonemsg ne '') {
                   14845: 	        $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
                   14846:             }
1.566     albertel 14847: 	}
                   14848: 	$outcome .= $clonemsg.$linefeed;
                   14849: 
                   14850:         if (!$can_clone) {
                   14851: 	    return (0,$outcome);
                   14852: 	}
                   14853:     }
                   14854: 
1.444     albertel 14855: #
                   14856: # Open course
                   14857: #
                   14858:     my $crstype = lc($args->{'crstype'});
                   14859:     my %cenv=();
                   14860:     $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
                   14861:                                              $args->{'cdescr'},
                   14862:                                              $args->{'curl'},
                   14863:                                              $args->{'course_home'},
                   14864:                                              $args->{'nonstandard'},
                   14865:                                              $args->{'crscode'},
                   14866:                                              $args->{'ccuname'}.':'.
                   14867:                                              $args->{'ccdomain'},
1.882     raeburn  14868:                                              $args->{'crstype'},
1.885     raeburn  14869:                                              $cnum,$context,$category);
1.444     albertel 14870: 
                   14871:     # Note: The testing routines depend on this being output; see 
                   14872:     # Utils::Course. This needs to at least be output as a comment
                   14873:     # if anyone ever decides to not show this, and Utils::Course::new
                   14874:     # will need to be suitably modified.
1.541     raeburn  14875:     $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
1.943     raeburn  14876:     if ($$courseid =~ /^error:/) {
                   14877:         return (0,$outcome);
                   14878:     }
                   14879: 
1.444     albertel 14880: #
                   14881: # Check if created correctly
                   14882: #
1.479     albertel 14883:     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444     albertel 14884:     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.943     raeburn  14885:     if ($crsuhome eq 'no_host') {
                   14886:         $outcome .= &mt('Course creation failed, unrecognized course home server.').$linefeed;
                   14887:         return (0,$outcome);
                   14888:     }
1.541     raeburn  14889:     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566     albertel 14890: 
1.444     albertel 14891: #
1.566     albertel 14892: # Do the cloning
                   14893: #   
                   14894:     if ($can_clone && $cloneid) {
                   14895: 	$clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
                   14896: 	if ($context ne 'auto') {
                   14897: 	    $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
                   14898: 	}
                   14899: 	$outcome .= $clonemsg.$linefeed;
                   14900: 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444     albertel 14901: # Copy all files
1.637     www      14902: 	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
1.444     albertel 14903: # Restore URL
1.566     albertel 14904: 	$cenv{'url'}=$oldcenv{'url'};
1.444     albertel 14905: # Restore title
1.566     albertel 14906: 	$cenv{'description'}=$oldcenv{'description'};
1.955     raeburn  14907: # Restore creation date, creator and creation context.
                   14908:         $cenv{'internal.created'}=$oldcenv{'internal.created'};
                   14909:         $cenv{'internal.creator'}=$oldcenv{'internal.creator'};
                   14910:         $cenv{'internal.creationcontext'}=$oldcenv{'internal.creationcontext'};
1.444     albertel 14911: # Mark as cloned
1.566     albertel 14912: 	$cenv{'clonedfrom'}=$cloneid;
1.638     www      14913: # Need to clone grading mode
                   14914:         my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
                   14915:         $cenv{'grading'}=$newenv{'grading'};
                   14916: # Do not clone these environment entries
                   14917:         &Apache::lonnet::del('environment',
                   14918:                   ['default_enrollment_start_date',
                   14919:                    'default_enrollment_end_date',
                   14920:                    'question.email',
                   14921:                    'policy.email',
                   14922:                    'comment.email',
                   14923:                    'pch.users.denied',
1.725     raeburn  14924:                    'plc.users.denied',
                   14925:                    'hidefromcat',
1.1121    raeburn  14926:                    'checkforpriv',
1.1166    raeburn  14927:                    'categories',
                   14928:                    'internal.uniquecode'],
1.638     www      14929:                    $$crsudom,$$crsunum);
1.1170    raeburn  14930:         if ($args->{'textbook'}) {
                   14931:             $cenv{'internal.textbook'} = $args->{'textbook'};
                   14932:         }
1.444     albertel 14933:     }
1.566     albertel 14934: 
1.444     albertel 14935: #
                   14936: # Set environment (will override cloned, if existing)
                   14937: #
                   14938:     my @sections = ();
                   14939:     my @xlists = ();
                   14940:     if ($args->{'crstype'}) {
                   14941:         $cenv{'type'}=$args->{'crstype'};
                   14942:     }
                   14943:     if ($args->{'crsid'}) {
                   14944:         $cenv{'courseid'}=$args->{'crsid'};
                   14945:     }
                   14946:     if ($args->{'crscode'}) {
                   14947:         $cenv{'internal.coursecode'}=$args->{'crscode'};
                   14948:     }
                   14949:     if ($args->{'crsquota'} ne '') {
                   14950:         $cenv{'internal.coursequota'}=$args->{'crsquota'};
                   14951:     } else {
                   14952:         $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
                   14953:     }
                   14954:     if ($args->{'ccuname'}) {
                   14955:         $cenv{'internal.courseowner'} = $args->{'ccuname'}.
                   14956:                                         ':'.$args->{'ccdomain'};
                   14957:     } else {
                   14958:         $cenv{'internal.courseowner'} = $args->{'curruser'};
                   14959:     }
1.1116    raeburn  14960:     if ($args->{'defaultcredits'}) {
                   14961:         $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
                   14962:     }
1.444     albertel 14963:     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
                   14964:     if ($args->{'crssections'}) {
                   14965:         $cenv{'internal.sectionnums'} = '';
                   14966:         if ($args->{'crssections'} =~ m/,/) {
                   14967:             @sections = split/,/,$args->{'crssections'};
                   14968:         } else {
                   14969:             $sections[0] = $args->{'crssections'};
                   14970:         }
                   14971:         if (@sections > 0) {
                   14972:             foreach my $item (@sections) {
                   14973:                 my ($sec,$gp) = split/:/,$item;
                   14974:                 my $class = $args->{'crscode'}.$sec;
                   14975:                 my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
                   14976:                 $cenv{'internal.sectionnums'} .= $item.',';
                   14977:                 unless ($addcheck eq 'ok') {
                   14978:                     push @badclasses, $class;
                   14979:                 }
                   14980:             }
                   14981:             $cenv{'internal.sectionnums'} =~ s/,$//;
                   14982:         }
                   14983:     }
                   14984: # do not hide course coordinator from staff listing, 
                   14985: # even if privileged
                   14986:     $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
1.1121    raeburn  14987: # add course coordinator's domain to domains to check for privileged users
                   14988: # if different to course domain
                   14989:     if ($$crsudom ne $args->{'ccdomain'}) {
                   14990:         $cenv{'checkforpriv'} = $args->{'ccdomain'};
                   14991:     }
1.444     albertel 14992: # add crosslistings
                   14993:     if ($args->{'crsxlist'}) {
                   14994:         $cenv{'internal.crosslistings'}='';
                   14995:         if ($args->{'crsxlist'} =~ m/,/) {
                   14996:             @xlists = split/,/,$args->{'crsxlist'};
                   14997:         } else {
                   14998:             $xlists[0] = $args->{'crsxlist'};
                   14999:         }
                   15000:         if (@xlists > 0) {
                   15001:             foreach my $item (@xlists) {
                   15002:                 my ($xl,$gp) = split/:/,$item;
                   15003:                 my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
                   15004:                 $cenv{'internal.crosslistings'} .= $item.',';
                   15005:                 unless ($addcheck eq 'ok') {
                   15006:                     push @badclasses, $xl;
                   15007:                 }
                   15008:             }
                   15009:             $cenv{'internal.crosslistings'} =~ s/,$//;
                   15010:         }
                   15011:     }
                   15012:     if ($args->{'autoadds'}) {
                   15013:         $cenv{'internal.autoadds'}=$args->{'autoadds'};
                   15014:     }
                   15015:     if ($args->{'autodrops'}) {
                   15016:         $cenv{'internal.autodrops'}=$args->{'autodrops'};
                   15017:     }
                   15018: # check for notification of enrollment changes
                   15019:     my @notified = ();
                   15020:     if ($args->{'notify_owner'}) {
                   15021:         if ($args->{'ccuname'} ne '') {
                   15022:             push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
                   15023:         }
                   15024:     }
                   15025:     if ($args->{'notify_dc'}) {
                   15026:         if ($uname ne '') { 
1.630     raeburn  15027:             push(@notified,$uname.':'.$udom);
1.444     albertel 15028:         }
                   15029:     }
                   15030:     if (@notified > 0) {
                   15031:         my $notifylist;
                   15032:         if (@notified > 1) {
                   15033:             $notifylist = join(',',@notified);
                   15034:         } else {
                   15035:             $notifylist = $notified[0];
                   15036:         }
                   15037:         $cenv{'internal.notifylist'} = $notifylist;
                   15038:     }
                   15039:     if (@badclasses > 0) {
                   15040:         my %lt=&Apache::lonlocal::texthash(
                   15041:                 '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',
                   15042:                 'dnhr' => 'does not have rights to access enrollment in these classes',
                   15043:                 'adby' => 'as determined by the policies of your institution on access to official classlists'
                   15044:         );
1.541     raeburn  15045:         my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
                   15046:                            ' ('.$lt{'adby'}.')';
                   15047:         if ($context eq 'auto') {
                   15048:             $outcome .= $badclass_msg.$linefeed;
1.566     albertel 15049:             $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.541     raeburn  15050:             foreach my $item (@badclasses) {
                   15051:                 if ($context eq 'auto') {
                   15052:                     $outcome .= " - $item\n";
                   15053:                 } else {
                   15054:                     $outcome .= "<li>$item</li>\n";
                   15055:                 }
                   15056:             }
                   15057:             if ($context eq 'auto') {
                   15058:                 $outcome .= $linefeed;
                   15059:             } else {
1.566     albertel 15060:                 $outcome .= "</ul><br /><br /></div>\n";
1.541     raeburn  15061:             }
                   15062:         } 
1.444     albertel 15063:     }
                   15064:     if ($args->{'no_end_date'}) {
                   15065:         $args->{'endaccess'} = 0;
                   15066:     }
                   15067:     $cenv{'internal.autostart'}=$args->{'enrollstart'};
                   15068:     $cenv{'internal.autoend'}=$args->{'enrollend'};
                   15069:     $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
                   15070:     $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
                   15071:     if ($args->{'showphotos'}) {
                   15072:       $cenv{'internal.showphotos'}=$args->{'showphotos'};
                   15073:     }
                   15074:     $cenv{'internal.authtype'} = $args->{'authtype'};
                   15075:     $cenv{'internal.autharg'} = $args->{'autharg'}; 
                   15076:     if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
                   15077:         if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'}  eq '') {
1.541     raeburn  15078:             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'); 
                   15079:             if ($context eq 'auto') {
                   15080:                 $outcome .= $krb_msg;
                   15081:             } else {
1.566     albertel 15082:                 $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541     raeburn  15083:             }
                   15084:             $outcome .= $linefeed;
1.444     albertel 15085:         }
                   15086:     }
                   15087:     if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
                   15088:        if ($args->{'setpolicy'}) {
                   15089:            $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   15090:        }
                   15091:        if ($args->{'setcontent'}) {
                   15092:            $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   15093:        }
                   15094:     }
                   15095:     if ($args->{'reshome'}) {
                   15096: 	$cenv{'reshome'}=$args->{'reshome'}.'/';
                   15097: 	$cenv{'reshome'}=~s/\/+$/\//;
                   15098:     }
                   15099: #
                   15100: # course has keyed access
                   15101: #
                   15102:     if ($args->{'setkeys'}) {
                   15103:        $cenv{'keyaccess'}='yes';
                   15104:     }
                   15105: # if specified, key authority is not course, but user
                   15106: # only active if keyaccess is yes
                   15107:     if ($args->{'keyauth'}) {
1.487     albertel 15108: 	my ($user,$domain) = split(':',$args->{'keyauth'});
                   15109: 	$user = &LONCAPA::clean_username($user);
                   15110: 	$domain = &LONCAPA::clean_username($domain);
1.488     foxr     15111: 	if ($user ne '' && $domain ne '') {
1.487     albertel 15112: 	    $cenv{'keyauth'}=$user.':'.$domain;
1.444     albertel 15113: 	}
                   15114:     }
                   15115: 
1.1166    raeburn  15116: #
1.1167    raeburn  15117: #  generate and store uniquecode (available to course requester), if course should have one.
1.1166    raeburn  15118: #
                   15119:     if ($args->{'uniquecode'}) {
                   15120:         my ($code,$error) = &make_unique_code($$crsudom,$$crsunum);
                   15121:         if ($code) {
                   15122:             $cenv{'internal.uniquecode'} = $code;
1.1167    raeburn  15123:             my %crsinfo =
                   15124:                 &Apache::lonnet::courseiddump($$crsudom,'.',1,'.','.',$$crsunum,undef,undef,'.');
                   15125:             if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') {
                   15126:                 $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code;
                   15127:                 my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime');
                   15128:             } 
1.1166    raeburn  15129:             if (ref($coderef)) {
                   15130:                 $$coderef = $code;
                   15131:             }
                   15132:         }
                   15133:     }
                   15134: 
1.444     albertel 15135:     if ($args->{'disresdis'}) {
                   15136:         $cenv{'pch.roles.denied'}='st';
                   15137:     }
                   15138:     if ($args->{'disablechat'}) {
                   15139:         $cenv{'plc.roles.denied'}='st';
                   15140:     }
                   15141: 
                   15142:     # Record we've not yet viewed the Course Initialization Helper for this 
                   15143:     # course
                   15144:     $cenv{'course.helper.not.run'} = 1;
                   15145:     #
                   15146:     # Use new Randomseed
                   15147:     #
                   15148:     $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
                   15149:     $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
                   15150:     #
                   15151:     # The encryption code and receipt prefix for this course
                   15152:     #
                   15153:     $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
                   15154:     $cenv{'internal.encpref'}=100+int(9*rand(99));
                   15155:     #
                   15156:     # By default, use standard grading
                   15157:     if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
                   15158: 
1.541     raeburn  15159:     $outcome .= $linefeed.&mt('Setting environment').': '.                 
                   15160:           &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 15161: #
                   15162: # Open all assignments
                   15163: #
                   15164:     if ($args->{'openall'}) {
                   15165:        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
                   15166:        my %storecontent = ($storeunder         => time,
                   15167:                            $storeunder.'.type' => 'date_start');
                   15168:        
                   15169:        $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541     raeburn  15170:                  ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 15171:    }
                   15172: #
                   15173: # Set first page
                   15174: #
                   15175:     unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
                   15176: 	    || ($cloneid)) {
1.445     albertel 15177: 	use LONCAPA::map;
1.444     albertel 15178: 	$outcome .= &mt('Setting first resource').': ';
1.445     albertel 15179: 
                   15180: 	my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
                   15181:         my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
                   15182: 
1.444     albertel 15183:         $outcome .= ($fatal?$errtext:'read ok').' - ';
                   15184:         my $title; my $url;
                   15185:         if ($args->{'firstres'} eq 'syl') {
1.690     bisitz   15186: 	    $title=&mt('Syllabus');
1.444     albertel 15187:             $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
                   15188:         } else {
1.963     raeburn  15189:             $title=&mt('Table of Contents');
1.444     albertel 15190:             $url='/adm/navmaps';
                   15191:         }
1.445     albertel 15192: 
                   15193:         $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
                   15194: 	(my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
                   15195: 
                   15196: 	if ($errtext) { $fatal=2; }
1.541     raeburn  15197:         $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444     albertel 15198:     }
1.566     albertel 15199: 
1.1237  ! raeburn  15200: # 
        !          15201: # Set params for Placement Tests
        !          15202: #
        !          15203:     if ($crstype eq 'Placement') {
        !          15204:        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.buttonshide';
        !          15205:        my %storecontent = ($storeunder         => 'yes',
        !          15206:                            $storeunder.'.type' => 'string_yesno');
        !          15207:        &Apache::lonnet::cput
        !          15208:                  ('resourcedata',\%storecontent,$$crsudom,$$crsunum); 
        !          15209:     }
        !          15210: 
1.566     albertel 15211:     return (1,$outcome);
1.444     albertel 15212: }
                   15213: 
1.1166    raeburn  15214: sub make_unique_code {
                   15215:     my ($cdom,$cnum) = @_;
                   15216:     # get lock on uniquecodes db
                   15217:     my $lockhash = {
                   15218:                       $cnum."\0".'uniquecodes' => $env{'user.name'}.
                   15219:                                                   ':'.$env{'user.domain'},
                   15220:                    };
                   15221:     my $tries = 0;
                   15222:     my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
                   15223:     my ($code,$error);
                   15224:   
                   15225:     while (($gotlock ne 'ok') && ($tries<3)) {
                   15226:         $tries ++;
                   15227:         sleep 1;
                   15228:         $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
                   15229:     }
                   15230:     if ($gotlock eq 'ok') {
                   15231:         my %currcodes = &Apache::lonnet::dump_dom('uniquecodes',$cdom);
                   15232:         my $gotcode;
                   15233:         my $attempts = 0;
                   15234:         while ((!$gotcode) && ($attempts < 100)) {
                   15235:             $code = &generate_code();
                   15236:             if (!exists($currcodes{$code})) {
                   15237:                 $gotcode = 1;
                   15238:                 unless (&Apache::lonnet::newput_dom('uniquecodes',{ $code => $cnum },$cdom) eq 'ok') {
                   15239:                     $error = 'nostore';
                   15240:                 }
                   15241:             }
                   15242:             $attempts ++;
                   15243:         }
                   15244:         my @del_lock = ($cnum."\0".'uniquecodes');
                   15245:         my $dellockoutcome = &Apache::lonnet::del_dom('uniquecodes',\@del_lock,$cdom);
                   15246:     } else {
                   15247:         $error = 'nolock';
                   15248:     }
                   15249:     return ($code,$error);
                   15250: }
                   15251: 
                   15252: sub generate_code {
                   15253:     my $code;
                   15254:     my @letts = qw(B C D G H J K M N P Q R S T V W X Z);
                   15255:     for (my $i=0; $i<6; $i++) {
                   15256:         my $lettnum = int (rand 2);
                   15257:         my $item = '';
                   15258:         if ($lettnum) {
                   15259:             $item = $letts[int( rand(18) )];
                   15260:         } else {
                   15261:             $item = 1+int( rand(8) );
                   15262:         }
                   15263:         $code .= $item;
                   15264:     }
                   15265:     return $code;
                   15266: }
                   15267: 
1.444     albertel 15268: ############################################################
                   15269: ############################################################
                   15270: 
1.1237  ! raeburn  15271: # Community, Course and Placement Test
1.378     raeburn  15272: sub course_type {
                   15273:     my ($cid) = @_;
                   15274:     if (!defined($cid)) {
                   15275:         $cid = $env{'request.course.id'};
                   15276:     }
1.404     albertel 15277:     if (defined($env{'course.'.$cid.'.type'})) {
                   15278:         return $env{'course.'.$cid.'.type'};
1.378     raeburn  15279:     } else {
                   15280:         return 'Course';
1.377     raeburn  15281:     }
                   15282: }
1.156     albertel 15283: 
1.406     raeburn  15284: sub group_term {
                   15285:     my $crstype = &course_type();
                   15286:     my %names = (
                   15287:                   'Course' => 'group',
1.865     raeburn  15288:                   'Community' => 'group',
1.1237  ! raeburn  15289:                   'Placement' => 'group',
1.406     raeburn  15290:                 );
                   15291:     return $names{$crstype};
                   15292: }
                   15293: 
1.902     raeburn  15294: sub course_types {
1.1237  ! raeburn  15295:     my @types = ('official','unofficial','community','textbook','placement');
1.902     raeburn  15296:     my %typename = (
                   15297:                          official   => 'Official course',
                   15298:                          unofficial => 'Unofficial course',
                   15299:                          community  => 'Community',
1.1165    raeburn  15300:                          textbook   => 'Textbook course',
1.1237  ! raeburn  15301:                          placement  => 'Placement test',
1.902     raeburn  15302:                    );
                   15303:     return (\@types,\%typename);
                   15304: }
                   15305: 
1.156     albertel 15306: sub icon {
                   15307:     my ($file)=@_;
1.505     albertel 15308:     my $curfext = lc((split(/\./,$file))[-1]);
1.168     albertel 15309:     my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156     albertel 15310:     my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168     albertel 15311:     if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
                   15312: 	if (-e  $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                   15313: 	          $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   15314: 	            $curfext.".gif") {
                   15315: 	    $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   15316: 		$curfext.".gif";
                   15317: 	}
                   15318:     }
1.249     albertel 15319:     return &lonhttpdurl($iconname);
1.154     albertel 15320: } 
1.84      albertel 15321: 
1.575     albertel 15322: sub lonhttpdurl {
1.692     www      15323: #
                   15324: # Had been used for "small fry" static images on separate port 8080.
                   15325: # Modify here if lightweight http functionality desired again.
                   15326: # Currently eliminated due to increasing firewall issues.
                   15327: #
1.575     albertel 15328:     my ($url)=@_;
1.692     www      15329:     return $url;
1.215     albertel 15330: }
                   15331: 
1.213     albertel 15332: sub connection_aborted {
                   15333:     my ($r)=@_;
                   15334:     $r->print(" ");$r->rflush();
                   15335:     my $c = $r->connection;
                   15336:     return $c->aborted();
                   15337: }
                   15338: 
1.221     foxr     15339: #    Escapes strings that may have embedded 's that will be put into
1.222     foxr     15340: #    strings as 'strings'.
                   15341: sub escape_single {
1.221     foxr     15342:     my ($input) = @_;
1.223     albertel 15343:     $input =~ s/\\/\\\\/g;	# Escape the \'s..(must be first)>
1.221     foxr     15344:     $input =~ s/\'/\\\'/g;	# Esacpe the 's....
                   15345:     return $input;
                   15346: }
1.223     albertel 15347: 
1.222     foxr     15348: #  Same as escape_single, but escape's "'s  This 
                   15349: #  can be used for  "strings"
                   15350: sub escape_double {
                   15351:     my ($input) = @_;
                   15352:     $input =~ s/\\/\\\\/g;	# Escape the /'s..(must be first)>
                   15353:     $input =~ s/\"/\\\"/g;	# Esacpe the "s....
                   15354:     return $input;
                   15355: }
1.223     albertel 15356:  
1.222     foxr     15357: #   Escapes the last element of a full URL.
                   15358: sub escape_url {
                   15359:     my ($url)   = @_;
1.238     raeburn  15360:     my @urlslices = split(/\//, $url,-1);
1.369     www      15361:     my $lastitem = &escape(pop(@urlslices));
1.1203    raeburn  15362:     return &HTML::Entities::encode(join('/',@urlslices),"'").'/'.$lastitem;
1.222     foxr     15363: }
1.462     albertel 15364: 
1.820     raeburn  15365: sub compare_arrays {
                   15366:     my ($arrayref1,$arrayref2) = @_;
                   15367:     my (@difference,%count);
                   15368:     @difference = ();
                   15369:     %count = ();
                   15370:     if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
                   15371:         foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
                   15372:         foreach my $element (keys(%count)) {
                   15373:             if ($count{$element} == 1) {
                   15374:                 push(@difference,$element);
                   15375:             }
                   15376:         }
                   15377:     }
                   15378:     return @difference;
                   15379: }
                   15380: 
1.817     bisitz   15381: # -------------------------------------------------------- Initialize user login
1.462     albertel 15382: sub init_user_environment {
1.463     albertel 15383:     my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462     albertel 15384:     my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
                   15385: 
                   15386:     my $public=($username eq 'public' && $domain eq 'public');
                   15387: 
                   15388: # See if old ID present, if so, remove
                   15389: 
1.1062    raeburn  15390:     my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
1.462     albertel 15391:     my $now=time;
                   15392: 
                   15393:     if ($public) {
                   15394: 	my $max_public=100;
                   15395: 	my $oldest;
                   15396: 	my $oldest_time=0;
                   15397: 	for(my $next=1;$next<=$max_public;$next++) {
                   15398: 	    if (-e $lonids."/publicuser_$next.id") {
                   15399: 		my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
                   15400: 		if ($mtime<$oldest_time || !$oldest_time) {
                   15401: 		    $oldest_time=$mtime;
                   15402: 		    $oldest=$next;
                   15403: 		}
                   15404: 	    } else {
                   15405: 		$cookie="publicuser_$next";
                   15406: 		last;
                   15407: 	    }
                   15408: 	}
                   15409: 	if (!$cookie) { $cookie="publicuser_$oldest"; }
                   15410:     } else {
1.463     albertel 15411: 	# if this isn't a robot, kill any existing non-robot sessions
                   15412: 	if (!$args->{'robot'}) {
                   15413: 	    opendir(DIR,$lonids);
                   15414: 	    while ($filename=readdir(DIR)) {
                   15415: 		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
                   15416: 		    unlink($lonids.'/'.$filename);
                   15417: 		}
1.462     albertel 15418: 	    }
1.463     albertel 15419: 	    closedir(DIR);
1.1204    raeburn  15420: # If there is a undeleted lockfile for the user's paste buffer remove it.
                   15421:             my $namespace = 'nohist_courseeditor';
                   15422:             my $lockingkey = 'paste'."\0".'locked_num';
                   15423:             my %lockhash = &Apache::lonnet::get($namespace,[$lockingkey],
                   15424:                                                 $domain,$username);
                   15425:             if (exists($lockhash{$lockingkey})) {
                   15426:                 my $delresult = &Apache::lonnet::del($namespace,[$lockingkey],$domain,$username);
                   15427:                 unless ($delresult eq 'ok') {
                   15428:                     &Apache::lonnet::logthis("Failed to delete paste buffer locking key in $namespace for ".$username.":".$domain." Result was: $delresult");
                   15429:                 }
                   15430:             }
1.462     albertel 15431: 	}
                   15432: # Give them a new cookie
1.463     albertel 15433: 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684     www      15434: 		                   : $now.$$.int(rand(10000)));
1.463     albertel 15435: 	$cookie="$username\_$id\_$domain\_$authhost";
1.462     albertel 15436:     
                   15437: # Initialize roles
                   15438: 
1.1062    raeburn  15439: 	($userroles,$firstaccenv,$timerintenv) = 
                   15440:             &Apache::lonnet::rolesinit($domain,$username,$authhost);
1.462     albertel 15441:     }
                   15442: # ------------------------------------ Check browser type and MathML capability
                   15443: 
1.1194    raeburn  15444:     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,$clientunicode,
                   15445:         $clientos,$clientmobile,$clientinfo,$clientosversion) = &decode_user_agent($r);
1.462     albertel 15446: 
                   15447: # ------------------------------------------------------------- Get environment
                   15448: 
                   15449:     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
                   15450:     my ($tmp) = keys(%userenv);
                   15451:     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   15452:     } else {
                   15453: 	undef(%userenv);
                   15454:     }
                   15455:     if (($userenv{'interface'}) && (!$form->{'interface'})) {
                   15456: 	$form->{'interface'}=$userenv{'interface'};
                   15457:     }
                   15458:     if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
                   15459: 
                   15460: # --------------- Do not trust query string to be put directly into environment
1.817     bisitz   15461:     foreach my $option ('interface','localpath','localres') {
                   15462:         $form->{$option}=~s/[\n\r\=]//gs;
1.462     albertel 15463:     }
                   15464: # --------------------------------------------------------- Write first profile
                   15465: 
                   15466:     {
                   15467: 	my %initial_env = 
                   15468: 	    ("user.name"          => $username,
                   15469: 	     "user.domain"        => $domain,
                   15470: 	     "user.home"          => $authhost,
                   15471: 	     "browser.type"       => $clientbrowser,
                   15472: 	     "browser.version"    => $clientversion,
                   15473: 	     "browser.mathml"     => $clientmathml,
                   15474: 	     "browser.unicode"    => $clientunicode,
                   15475: 	     "browser.os"         => $clientos,
1.1137    raeburn  15476:              "browser.mobile"     => $clientmobile,
1.1141    raeburn  15477:              "browser.info"       => $clientinfo,
1.1194    raeburn  15478:              "browser.osversion"  => $clientosversion,
1.462     albertel 15479: 	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
                   15480: 	     "request.course.fn"  => '',
                   15481: 	     "request.course.uri" => '',
                   15482: 	     "request.course.sec" => '',
                   15483: 	     "request.role"       => 'cm',
                   15484: 	     "request.role.adv"   => $env{'user.adv'},
                   15485: 	     "request.host"       => $ENV{'REMOTE_ADDR'},);
                   15486: 
                   15487:         if ($form->{'localpath'}) {
                   15488: 	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
                   15489: 	    $initial_env{"browser.localres"}   = $form->{'localres'};
                   15490:         }
                   15491: 	
                   15492: 	if ($form->{'interface'}) {
                   15493: 	    $form->{'interface'}=~s/\W//gs;
                   15494: 	    $initial_env{"browser.interface"} = $form->{'interface'};
                   15495: 	    $env{'browser.interface'}=$form->{'interface'};
                   15496: 	}
                   15497: 
1.1157    raeburn  15498:         if ($form->{'iptoken'}) {
                   15499:             my $lonhost = $r->dir_config('lonHostID');
                   15500:             $initial_env{"user.noloadbalance"} = $lonhost;
                   15501:             $env{'user.noloadbalance'} = $lonhost;
                   15502:         }
                   15503: 
1.981     raeburn  15504:         my %is_adv = ( is_adv => $env{'user.adv'} );
1.1016    raeburn  15505:         my %domdef;
                   15506:         unless ($domain eq 'public') {
                   15507:             %domdef = &Apache::lonnet::get_domain_defaults($domain);
                   15508:         }
1.980     raeburn  15509: 
1.1081    raeburn  15510:         foreach my $tool ('aboutme','blog','webdav','portfolio') {
1.724     raeburn  15511:             $userenv{'availabletools.'.$tool} = 
1.980     raeburn  15512:                 &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
                   15513:                                                   undef,\%userenv,\%domdef,\%is_adv);
1.724     raeburn  15514:         }
                   15515: 
1.1237  ! raeburn  15516:         foreach my $crstype ('official','unofficial','community','textbook','placement') {
1.765     raeburn  15517:             $userenv{'canrequest.'.$crstype} =
                   15518:                 &Apache::lonnet::usertools_access($username,$domain,$crstype,
1.980     raeburn  15519:                                                   'reload','requestcourses',
                   15520:                                                   \%userenv,\%domdef,\%is_adv);
1.765     raeburn  15521:         }
                   15522: 
1.1092    raeburn  15523:         $userenv{'canrequest.author'} =
                   15524:             &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
                   15525:                                         'reload','requestauthor',
                   15526:                                         \%userenv,\%domdef,\%is_adv);
                   15527:         my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
                   15528:                                              $domain,$username);
                   15529:         my $reqstatus = $reqauthor{'author_status'};
                   15530:         if ($reqstatus eq 'approval' || $reqstatus eq 'approved') { 
                   15531:             if (ref($reqauthor{'author'}) eq 'HASH') {
                   15532:                 $userenv{'requestauthorqueued'} = $reqstatus.':'.
                   15533:                                                   $reqauthor{'author'}{'timestamp'};
                   15534:             }
                   15535:         }
                   15536: 
1.462     albertel 15537: 	$env{'user.environment'} = "$lonids/$cookie.id";
1.1062    raeburn  15538: 
1.462     albertel 15539: 	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
                   15540: 		 &GDBM_WRCREAT(),0640)) {
                   15541: 	    &_add_to_env(\%disk_env,\%initial_env);
                   15542: 	    &_add_to_env(\%disk_env,\%userenv,'environment.');
                   15543: 	    &_add_to_env(\%disk_env,$userroles);
1.1062    raeburn  15544:             if (ref($firstaccenv) eq 'HASH') {
                   15545:                 &_add_to_env(\%disk_env,$firstaccenv);
                   15546:             }
                   15547:             if (ref($timerintenv) eq 'HASH') {
                   15548:                 &_add_to_env(\%disk_env,$timerintenv);
                   15549:             }
1.463     albertel 15550: 	    if (ref($args->{'extra_env'})) {
                   15551: 		&_add_to_env(\%disk_env,$args->{'extra_env'});
                   15552: 	    }
1.462     albertel 15553: 	    untie(%disk_env);
                   15554: 	} else {
1.705     tempelho 15555: 	    &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
                   15556: 			   'Could not create environment storage in lonauth: '.$!.'</span>');
1.462     albertel 15557: 	    return 'error: '.$!;
                   15558: 	}
                   15559:     }
                   15560:     $env{'request.role'}='cm';
                   15561:     $env{'request.role.adv'}=$env{'user.adv'};
                   15562:     $env{'browser.type'}=$clientbrowser;
                   15563: 
                   15564:     return $cookie;
                   15565: 
                   15566: }
                   15567: 
                   15568: sub _add_to_env {
                   15569:     my ($idf,$env_data,$prefix) = @_;
1.676     raeburn  15570:     if (ref($env_data) eq 'HASH') {
                   15571:         while (my ($key,$value) = each(%$env_data)) {
                   15572: 	    $idf->{$prefix.$key} = $value;
                   15573: 	    $env{$prefix.$key}   = $value;
                   15574:         }
1.462     albertel 15575:     }
                   15576: }
                   15577: 
1.685     tempelho 15578: # --- Get the symbolic name of a problem and the url
                   15579: sub get_symb {
                   15580:     my ($request,$silent) = @_;
1.726     raeburn  15581:     (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685     tempelho 15582:     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
                   15583:     if ($symb eq '') {
                   15584:         if (!$silent) {
1.1071    raeburn  15585:             if (ref($request)) { 
                   15586:                 $request->print("Unable to handle ambiguous references:$url:.");
                   15587:             }
1.685     tempelho 15588:             return ();
                   15589:         }
                   15590:     }
                   15591:     &Apache::lonenc::check_decrypt(\$symb);
                   15592:     return ($symb);
                   15593: }
                   15594: 
                   15595: # --------------------------------------------------------------Get annotation
                   15596: 
                   15597: sub get_annotation {
                   15598:     my ($symb,$enc) = @_;
                   15599: 
                   15600:     my $key = $symb;
                   15601:     if (!$enc) {
                   15602:         $key =
                   15603:             &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
                   15604:     }
                   15605:     my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
                   15606:     return $annotation{$key};
                   15607: }
                   15608: 
                   15609: sub clean_symb {
1.731     raeburn  15610:     my ($symb,$delete_enc) = @_;
1.685     tempelho 15611: 
                   15612:     &Apache::lonenc::check_decrypt(\$symb);
                   15613:     my $enc = $env{'request.enc'};
1.731     raeburn  15614:     if ($delete_enc) {
1.730     raeburn  15615:         delete($env{'request.enc'});
                   15616:     }
1.685     tempelho 15617: 
                   15618:     return ($symb,$enc);
                   15619: }
1.462     albertel 15620: 
1.1181    raeburn  15621: ############################################################
                   15622: ############################################################
                   15623: 
                   15624: =pod
                   15625: 
                   15626: =head1 Routines for building display used to search for courses
                   15627: 
                   15628: 
                   15629: =over 4
                   15630: 
                   15631: =item * &build_filters()
                   15632: 
                   15633: Create markup for a table used to set filters to use when selecting
1.1182    raeburn  15634: courses in a domain.  Used by lonpickcourse.pm, lonmodifycourse.pm
                   15635: and quotacheck.pl
                   15636: 
1.1181    raeburn  15637: 
                   15638: Inputs:
                   15639: 
                   15640: filterlist - anonymous array of fields to include as potential filters 
                   15641: 
                   15642: crstype - course type
                   15643: 
                   15644: roleelement - fifth arg in selectcourse_link() populates fifth arg in javascript: opencrsbrowser() function, used
                   15645:               to pop-open a course selector (will contain "extra element"). 
                   15646: 
                   15647: multelement - if multiple course selections will be allowed, this will be a hidden form element: name: multiple; value: 1
                   15648: 
                   15649: filter - anonymous hash of criteria and their values
                   15650: 
                   15651: action - form action
                   15652: 
                   15653: numfiltersref - ref to scalar (count of number of elements in institutional codes -- e.g., 4 for year, semester, department, and number)
                   15654: 
1.1182    raeburn  15655: caller - caller context (e.g., set to 'modifycourse' when routine is called from lonmodifycourse.pm)
1.1181    raeburn  15656: 
                   15657: cloneruname - username of owner of new course who wants to clone
                   15658: 
                   15659: clonerudom - domain of owner of new course who wants to clone
                   15660: 
                   15661: typeelem - text to use for left column in row containing course type (i.e., Course, Community or Course/Community) 
                   15662: 
                   15663: codetitlesref - reference to array of titles of components in institutional codes (official courses)
                   15664: 
                   15665: codedom - domain
                   15666: 
                   15667: formname - value of form element named "form". 
                   15668: 
                   15669: fixeddom - domain, if fixed.
                   15670: 
                   15671: prevphase - value to assign to form element named "phase" when going back to the previous screen  
                   15672: 
                   15673: cnameelement - name of form element in form on opener page which will receive title of selected course 
                   15674: 
                   15675: cnumelement - name of form element in form on opener page which will receive courseID  of selected course
                   15676: 
                   15677: cdomelement - name of form element in form on opener page which will receive domain of selected course
                   15678: 
                   15679: setroles - includes access constraint identifier when setting a roles-based condition for acces to a portfolio file
                   15680: 
                   15681: clonetext - hidden form elements containing list of courses cloneable by intended course owner when DC creates a course
                   15682: 
                   15683: clonewarning - warning message about missing information for intended course owner when DC creates a course
                   15684: 
1.1182    raeburn  15685: 
1.1181    raeburn  15686: Returns: $output - HTML for display of search criteria, and hidden form elements.
                   15687: 
1.1182    raeburn  15688: 
1.1181    raeburn  15689: Side Effects: None
                   15690: 
                   15691: =cut
                   15692: 
                   15693: # ---------------------------------------------- search for courses based on last activity etc.
                   15694: 
                   15695: sub build_filters {
                   15696:     my ($filterlist,$crstype,$roleelement,$multelement,$filter,$action,
                   15697:         $numtitlesref,$caller,$cloneruname,$clonerudom,$typeelement,
                   15698:         $codetitlesref,$codedom,$formname,$fixeddom,$prevphase,
                   15699:         $cnameelement,$cnumelement,$cdomelement,$setroles,
                   15700:         $clonetext,$clonewarning) = @_;
1.1182    raeburn  15701:     my ($list,$jscript);
1.1181    raeburn  15702:     my $onchange = 'javascript:updateFilters(this)';
                   15703:     my ($domainselectform,$sincefilterform,$createdfilterform,
                   15704:         $ownerdomselectform,$persondomselectform,$instcodeform,
                   15705:         $typeselectform,$instcodetitle);
                   15706:     if ($formname eq '') {
                   15707:         $formname = $caller;
                   15708:     }
                   15709:     foreach my $item (@{$filterlist}) {
                   15710:         unless (($item eq 'descriptfilter') || ($item eq 'instcodefilter') ||
                   15711:                 ($item eq 'sincefilter') || ($item eq 'createdfilter')) {
                   15712:             if ($item eq 'domainfilter') {
                   15713:                 $filter->{$item} = &LONCAPA::clean_domain($filter->{$item});
                   15714:             } elsif ($item eq 'coursefilter') {
                   15715:                 $filter->{$item} = &LONCAPA::clean_courseid($filter->{$item});
                   15716:             } elsif ($item eq 'ownerfilter') {
                   15717:                 $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
                   15718:             } elsif ($item eq 'ownerdomfilter') {
                   15719:                 $filter->{'ownerdomfilter'} =
                   15720:                     &LONCAPA::clean_domain($filter->{$item});
                   15721:                 $ownerdomselectform = &select_dom_form($filter->{'ownerdomfilter'},
                   15722:                                                        'ownerdomfilter',1);
                   15723:             } elsif ($item eq 'personfilter') {
                   15724:                 $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
                   15725:             } elsif ($item eq 'persondomfilter') {
                   15726:                 $persondomselectform = &select_dom_form($filter->{'persondomfilter'},
                   15727:                                                         'persondomfilter',1);
                   15728:             } else {
                   15729:                 $filter->{$item} =~ s/\W//g;
                   15730:             }
                   15731:             if (!$filter->{$item}) {
                   15732:                 $filter->{$item} = '';
                   15733:             }
                   15734:         }
                   15735:         if ($item eq 'domainfilter') {
                   15736:             my $allow_blank = 1;
                   15737:             if ($formname eq 'portform') {
                   15738:                 $allow_blank=0;
                   15739:             } elsif ($formname eq 'studentform') {
                   15740:                 $allow_blank=0;
                   15741:             }
                   15742:             if ($fixeddom) {
                   15743:                 $domainselectform = '<input type="hidden" name="domainfilter"'.
                   15744:                                     ' value="'.$codedom.'" />'.
                   15745:                                     &Apache::lonnet::domain($codedom,'description');
                   15746:             } else {
                   15747:                 $domainselectform = &select_dom_form($filter->{$item},
                   15748:                                                      'domainfilter',
                   15749:                                                       $allow_blank,'',$onchange);
                   15750:             }
                   15751:         } else {
                   15752:             $list->{$item} = &HTML::Entities::encode($filter->{$item},'<>&"');
                   15753:         }
                   15754:     }
                   15755: 
                   15756:     # last course activity filter and selection
                   15757:     $sincefilterform = &timebased_select_form('sincefilter',$filter);
                   15758: 
                   15759:     # course created filter and selection
                   15760:     if (exists($filter->{'createdfilter'})) {
                   15761:         $createdfilterform = &timebased_select_form('createdfilter',$filter);
                   15762:     }
                   15763: 
                   15764:     my %lt = &Apache::lonlocal::texthash(
                   15765:                 'cac' => "$crstype Activity",
                   15766:                 'ccr' => "$crstype Created",
                   15767:                 'cde' => "$crstype Title",
                   15768:                 'cdo' => "$crstype Domain",
                   15769:                 'ins' => 'Institutional Code',
                   15770:                 'inc' => 'Institutional Categorization',
                   15771:                 'cow' => "$crstype Owner/Co-owner",
                   15772:                 'cop' => "$crstype Personnel Includes",
                   15773:                 'cog' => 'Type',
                   15774:              );
                   15775: 
                   15776:     if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
                   15777:         my $typeval = 'Course';
                   15778:         if ($crstype eq 'Community') {
                   15779:             $typeval = 'Community';
                   15780:         }
                   15781:         $typeselectform = '<input type="hidden" name="type" value="'.$typeval.'" />';
                   15782:     } else {
                   15783:         $typeselectform =  '<select name="type" size="1"';
                   15784:         if ($onchange) {
                   15785:             $typeselectform .= ' onchange="'.$onchange.'"';
                   15786:         }
                   15787:         $typeselectform .= '>'."\n";
1.1237  ! raeburn  15788:         foreach my $posstype ('Course','Community','Placement') {
1.1181    raeburn  15789:             $typeselectform.='<option value="'.$posstype.'"'.
                   15790:                 ($posstype eq $crstype ? ' selected="selected" ' : ''). ">".&mt($posstype)."</option>\n";
                   15791:         }
                   15792:         $typeselectform.="</select>";
                   15793:     }
                   15794: 
                   15795:     my ($cloneableonlyform,$cloneabletitle);
                   15796:     if (exists($filter->{'cloneableonly'})) {
                   15797:         my $cloneableon = '';
                   15798:         my $cloneableoff = ' checked="checked"';
                   15799:         if ($filter->{'cloneableonly'}) {
                   15800:             $cloneableon = $cloneableoff;
                   15801:             $cloneableoff = '';
                   15802:         }
                   15803:         $cloneableonlyform = '<span class="LC_nobreak"><label><input type="radio" name="cloneableonly" value="1" '.$cloneableon.'/>&nbsp;'.&mt('Required').'</label>'.('&nbsp;'x3).'<label><input type="radio" name="cloneableonly" value="" '.$cloneableoff.' />&nbsp;'.&mt('No restriction').'</label></span>';
                   15804:         if ($formname eq 'ccrs') {
1.1187    bisitz   15805:             $cloneabletitle = &mt('Cloneable for [_1]',$cloneruname.':'.$clonerudom);
1.1181    raeburn  15806:         } else {
                   15807:             $cloneabletitle = &mt('Cloneable by you');
                   15808:         }
                   15809:     }
                   15810:     my $officialjs;
                   15811:     if ($crstype eq 'Course') {
                   15812:         if (exists($filter->{'instcodefilter'})) {
1.1182    raeburn  15813: #            if (($fixeddom) || ($formname eq 'requestcrs') ||
                   15814: #                ($formname eq 'modifycourse') || ($formname eq 'filterpicker')) {
                   15815:             if ($codedom) { 
1.1181    raeburn  15816:                 $officialjs = 1;
                   15817:                 ($instcodeform,$jscript,$$numtitlesref) =
                   15818:                     &Apache::courseclassifier::instcode_selectors($codedom,'filterpicker',
                   15819:                                                                   $officialjs,$codetitlesref);
                   15820:                 if ($jscript) {
1.1182    raeburn  15821:                     $jscript = '<script type="text/javascript">'."\n".
                   15822:                                '// <![CDATA['."\n".
                   15823:                                $jscript."\n".
                   15824:                                '// ]]>'."\n".
                   15825:                                '</script>'."\n";
1.1181    raeburn  15826:                 }
                   15827:             }
                   15828:             if ($instcodeform eq '') {
                   15829:                 $instcodeform =
                   15830:                     '<input type="text" name="instcodefilter" size="10" value="'.
                   15831:                     $list->{'instcodefilter'}.'" />';
                   15832:                 $instcodetitle = $lt{'ins'};
                   15833:             } else {
                   15834:                 $instcodetitle = $lt{'inc'};
                   15835:             }
                   15836:             if ($fixeddom) {
                   15837:                 $instcodetitle .= '<br />('.$codedom.')';
                   15838:             }
                   15839:         }
                   15840:     }
                   15841:     my $output = qq|
                   15842: <form method="post" name="filterpicker" action="$action">
                   15843: <input type="hidden" name="form" value="$formname" />
                   15844: |;
                   15845:     if ($formname eq 'modifycourse') {
                   15846:         $output .= '<input type="hidden" name="phase" value="courselist" />'."\n".
                   15847:                    '<input type="hidden" name="prevphase" value="'.
                   15848:                    $prevphase.'" />'."\n";
1.1198    musolffc 15849:     } elsif ($formname eq 'quotacheck') {
                   15850:         $output .= qq|
                   15851: <input type="hidden" name="sortby" value="" />
                   15852: <input type="hidden" name="sortorder" value="" />
                   15853: |;
                   15854:     } else {
1.1181    raeburn  15855:         my $name_input;
                   15856:         if ($cnameelement ne '') {
                   15857:             $name_input = '<input type="hidden" name="cnameelement" value="'.
                   15858:                           $cnameelement.'" />';
                   15859:         }
                   15860:         $output .= qq|
1.1182    raeburn  15861: <input type="hidden" name="cnumelement" value="$cnumelement" />
                   15862: <input type="hidden" name="cdomelement" value="$cdomelement" />
1.1181    raeburn  15863: $name_input
                   15864: $roleelement
                   15865: $multelement
                   15866: $typeelement
                   15867: |;
                   15868:         if ($formname eq 'portform') {
                   15869:             $output .= '<input type="hidden" name="setroles" value="'.$setroles.'" />'."\n";
                   15870:         }
                   15871:     }
                   15872:     if ($fixeddom) {
                   15873:         $output .= '<input type="hidden" name="fixeddom" value="'.$fixeddom.'" />'."\n";
                   15874:     }
                   15875:     $output .= "<br />\n".&Apache::lonhtmlcommon::start_pick_box();
                   15876:     if ($sincefilterform) {
                   15877:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cac'})
                   15878:                   .$sincefilterform
                   15879:                   .&Apache::lonhtmlcommon::row_closure();
                   15880:     }
                   15881:     if ($createdfilterform) {
                   15882:         $output .= &Apache::lonhtmlcommon::row_title($lt{'ccr'})
                   15883:                   .$createdfilterform
                   15884:                   .&Apache::lonhtmlcommon::row_closure();
                   15885:     }
                   15886:     if ($domainselectform) {
                   15887:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cdo'})
                   15888:                   .$domainselectform
                   15889:                   .&Apache::lonhtmlcommon::row_closure();
                   15890:     }
                   15891:     if ($typeselectform) {
                   15892:         if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
                   15893:             $output .= $typeselectform;
                   15894:         } else {
                   15895:             $output .= &Apache::lonhtmlcommon::row_title($lt{'cog'})
                   15896:                       .$typeselectform
                   15897:                       .&Apache::lonhtmlcommon::row_closure();
                   15898:         }
                   15899:     }
                   15900:     if ($instcodeform) {
                   15901:         $output .= &Apache::lonhtmlcommon::row_title($instcodetitle)
                   15902:                   .$instcodeform
                   15903:                   .&Apache::lonhtmlcommon::row_closure();
                   15904:     }
                   15905:     if (exists($filter->{'ownerfilter'})) {
                   15906:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cow'}).
                   15907:                    '<table><tr><td>'.&mt('Username').'<br />'.
                   15908:                    '<input type="text" name="ownerfilter" size="20" value="'.
                   15909:                    $list->{'ownerfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
                   15910:                    $ownerdomselectform.'</td></tr></table>'.
                   15911:                    &Apache::lonhtmlcommon::row_closure();
                   15912:     }
                   15913:     if (exists($filter->{'personfilter'})) {
                   15914:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cop'}).
                   15915:                    '<table><tr><td>'.&mt('Username').'<br />'.
                   15916:                    '<input type="text" name="personfilter" size="20" value="'.
                   15917:                    $list->{'personfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
                   15918:                    $persondomselectform.'</td></tr></table>'.
                   15919:                    &Apache::lonhtmlcommon::row_closure();
                   15920:     }
                   15921:     if (exists($filter->{'coursefilter'})) {
                   15922:         $output .= &Apache::lonhtmlcommon::row_title(&mt('LON-CAPA course ID'))
                   15923:                   .'<input type="text" name="coursefilter" size="25" value="'
                   15924:                   .$list->{'coursefilter'}.'" />'
                   15925:                   .&Apache::lonhtmlcommon::row_closure();
                   15926:     }
                   15927:     if ($cloneableonlyform) {
                   15928:         $output .= &Apache::lonhtmlcommon::row_title($cloneabletitle).
                   15929:                    $cloneableonlyform.&Apache::lonhtmlcommon::row_closure();
                   15930:     }
                   15931:     if (exists($filter->{'descriptfilter'})) {
                   15932:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cde'})
                   15933:                   .'<input type="text" name="descriptfilter" size="40" value="'
                   15934:                   .$list->{'descriptfilter'}.'" />'
                   15935:                   .&Apache::lonhtmlcommon::row_closure(1);
                   15936:     }
                   15937:     $output .= &Apache::lonhtmlcommon::end_pick_box().'<p>'.$clonetext."\n".
                   15938:                '<input type="hidden" name="updater" value="" />'."\n".
                   15939:                '<input type="submit" name="gosearch" value="'.
                   15940:                &mt('Search').'" /></p>'."\n".'</form>'."\n".'<hr />'."\n";
                   15941:     return $jscript.$clonewarning.$output;
                   15942: }
                   15943: 
                   15944: =pod 
                   15945: 
                   15946: =item * &timebased_select_form()
                   15947: 
1.1182    raeburn  15948: Create markup for a dropdown list used to select a time-based
1.1181    raeburn  15949: filter e.g., Course Activity, Course Created, when searching for courses
                   15950: or communities
                   15951: 
                   15952: Inputs:
                   15953: 
                   15954: item - name of form element (sincefilter or createdfilter)
                   15955: 
                   15956: filter - anonymous hash of criteria and their values
                   15957: 
                   15958: Returns: HTML for a select box contained a blank, then six time selections,
                   15959:          with value set in incoming form variables currently selected. 
                   15960: 
                   15961: Side Effects: None
                   15962: 
                   15963: =cut
                   15964: 
                   15965: sub timebased_select_form {
                   15966:     my ($item,$filter) = @_;
                   15967:     if (ref($filter) eq 'HASH') {
                   15968:         $filter->{$item} =~ s/[^\d-]//g;
                   15969:         if (!$filter->{$item}) { $filter->{$item}=-1; }
                   15970:         return &select_form(
                   15971:                             $filter->{$item},
                   15972:                             $item,
                   15973:                             {      '-1' => '',
                   15974:                                 '86400' => &mt('today'),
                   15975:                                '604800' => &mt('last week'),
                   15976:                               '2592000' => &mt('last month'),
                   15977:                               '7776000' => &mt('last three months'),
                   15978:                              '15552000' => &mt('last six months'),
                   15979:                              '31104000' => &mt('last year'),
                   15980:                     'select_form_order' =>
                   15981:                            ['-1','86400','604800','2592000','7776000',
                   15982:                             '15552000','31104000']});
                   15983:     }
                   15984: }
                   15985: 
                   15986: =pod
                   15987: 
                   15988: =item * &js_changer()
                   15989: 
                   15990: Create script tag containing Javascript used to submit course search form
1.1183    raeburn  15991: when course type or domain is changed, and also to hide 'Searching ...' on
                   15992: page load completion for page showing search result.
1.1181    raeburn  15993: 
                   15994: Inputs: None
                   15995: 
1.1183    raeburn  15996: Returns: markup containing updateFilters() and hideSearching() javascript functions. 
1.1181    raeburn  15997: 
                   15998: Side Effects: None
                   15999: 
                   16000: =cut
                   16001: 
                   16002: sub js_changer {
                   16003:     return <<ENDJS;
                   16004: <script type="text/javascript">
                   16005: // <![CDATA[
                   16006: function updateFilters(caller) {
                   16007:     if (typeof(caller) != "undefined") {
                   16008:         document.filterpicker.updater.value = caller.name;
                   16009:     }
                   16010:     document.filterpicker.submit();
                   16011: }
1.1183    raeburn  16012: 
                   16013: function hideSearching() {
                   16014:     if (document.getElementById('searching')) {
                   16015:         document.getElementById('searching').style.display = 'none';
                   16016:     }
                   16017:     return;
                   16018: }
                   16019: 
1.1181    raeburn  16020: // ]]>
                   16021: </script>
                   16022: 
                   16023: ENDJS
                   16024: }
                   16025: 
                   16026: =pod
                   16027: 
1.1182    raeburn  16028: =item * &search_courses()
                   16029: 
                   16030: Process selected filters form course search form and pass to lonnet::courseiddump
                   16031: to retrieve a hash for which keys are courseIDs which match the selected filters.
                   16032: 
                   16033: Inputs:
                   16034: 
                   16035: dom - domain being searched 
                   16036: 
                   16037: type - course type ('Course' or 'Community' or '.' if any).
                   16038: 
                   16039: filter - anonymous hash of criteria and their values
                   16040: 
                   16041: numtitles - for institutional codes - number of categories
                   16042: 
                   16043: cloneruname - optional username of new course owner
                   16044: 
                   16045: clonerudom - optional domain of new course owner
                   16046: 
1.1221    raeburn  16047: domcloner - optional "domcloner" flag; has value=1 if user has ccc priv in domain being filtered by, 
1.1182    raeburn  16048:             (used when DC is using course creation form)
                   16049: 
                   16050: codetitles - reference to array of titles of components in institutional codes (official courses).
                   16051: 
1.1221    raeburn  16052: cc_clone - escaped comma separated list of courses for which course cloner has active CC role
                   16053:            (and so can clone automatically)
                   16054: 
                   16055: reqcrsdom - domain of new course, where search_courses is used to identify potential courses to clone
                   16056: 
                   16057: reqinstcode - institutional code of new course, where search_courses is used to identify potential 
                   16058:               courses to clone 
1.1182    raeburn  16059: 
                   16060: Returns: %courses - hash of courses satisfying search criteria, keys = course IDs, values are corresponding colon-separated escaped description, institutional code, owner and type.
                   16061: 
                   16062: 
                   16063: Side Effects: None
                   16064: 
                   16065: =cut
                   16066: 
                   16067: 
                   16068: sub search_courses {
1.1221    raeburn  16069:     my ($dom,$type,$filter,$numtitles,$cloneruname,$clonerudom,$domcloner,$codetitles,
                   16070:         $cc_clone,$reqcrsdom,$reqinstcode) = @_;
1.1182    raeburn  16071:     my (%courses,%showcourses,$cloner);
                   16072:     if (($filter->{'ownerfilter'} ne '') ||
                   16073:         ($filter->{'ownerdomfilter'} ne '')) {
                   16074:         $filter->{'combownerfilter'} = $filter->{'ownerfilter'}.':'.
                   16075:                                        $filter->{'ownerdomfilter'};
                   16076:     }
                   16077:     foreach my $item ('descriptfilter','coursefilter','combownerfilter') {
                   16078:         if (!$filter->{$item}) {
                   16079:             $filter->{$item}='.';
                   16080:         }
                   16081:     }
                   16082:     my $now = time;
                   16083:     my $timefilter =
                   16084:        ($filter->{'sincefilter'}==-1?1:$now-$filter->{'sincefilter'});
                   16085:     my ($createdbefore,$createdafter);
                   16086:     if (($filter->{'createdfilter'} ne '') && ($filter->{'createdfilter'} !=-1)) {
                   16087:         $createdbefore = $now;
                   16088:         $createdafter = $now-$filter->{'createdfilter'};
                   16089:     }
                   16090:     my ($instcodefilter,$regexpok);
                   16091:     if ($numtitles) {
                   16092:         if ($env{'form.official'} eq 'on') {
                   16093:             $instcodefilter =
                   16094:                 &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
                   16095:             $regexpok = 1;
                   16096:         } elsif ($env{'form.official'} eq 'off') {
                   16097:             $instcodefilter = &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
                   16098:             unless ($instcodefilter eq '') {
                   16099:                 $regexpok = -1;
                   16100:             }
                   16101:         }
                   16102:     } else {
                   16103:         $instcodefilter = $filter->{'instcodefilter'};
                   16104:     }
                   16105:     if ($instcodefilter eq '') { $instcodefilter = '.'; }
                   16106:     if ($type eq '') { $type = '.'; }
                   16107: 
                   16108:     if (($clonerudom ne '') && ($cloneruname ne '')) {
                   16109:         $cloner = $cloneruname.':'.$clonerudom;
                   16110:     }
                   16111:     %courses = &Apache::lonnet::courseiddump($dom,
                   16112:                                              $filter->{'descriptfilter'},
                   16113:                                              $timefilter,
                   16114:                                              $instcodefilter,
                   16115:                                              $filter->{'combownerfilter'},
                   16116:                                              $filter->{'coursefilter'},
                   16117:                                              undef,undef,$type,$regexpok,undef,undef,
1.1221    raeburn  16118:                                              undef,undef,$cloner,$cc_clone,
1.1182    raeburn  16119:                                              $filter->{'cloneableonly'},
                   16120:                                              $createdbefore,$createdafter,undef,
1.1221    raeburn  16121:                                              $domcloner,undef,$reqcrsdom,$reqinstcode);
1.1182    raeburn  16122:     if (($filter->{'personfilter'} ne '') && ($filter->{'persondomfilter'} ne '')) {
                   16123:         my $ccrole;
                   16124:         if ($type eq 'Community') {
                   16125:             $ccrole = 'co';
                   16126:         } else {
                   16127:             $ccrole = 'cc';
                   16128:         }
                   16129:         my %rolehash = &Apache::lonnet::get_my_roles($filter->{'personfilter'},
                   16130:                                                      $filter->{'persondomfilter'},
                   16131:                                                      'userroles',undef,
                   16132:                                                      [$ccrole,'in','ad','ep','ta','cr'],
                   16133:                                                      $dom);
                   16134:         foreach my $role (keys(%rolehash)) {
                   16135:             my ($cnum,$cdom,$courserole) = split(':',$role);
                   16136:             my $cid = $cdom.'_'.$cnum;
                   16137:             if (exists($courses{$cid})) {
                   16138:                 if (ref($courses{$cid}) eq 'HASH') {
                   16139:                     if (ref($courses{$cid}{roles}) eq 'ARRAY') {
                   16140:                         if (!grep(/^\Q$courserole\E$/,@{$courses{$cid}{roles}})) {
                   16141:                             push (@{$courses{$cid}{roles}},$courserole);
                   16142:                         }
                   16143:                     } else {
                   16144:                         $courses{$cid}{roles} = [$courserole];
                   16145:                     }
                   16146:                     $showcourses{$cid} = $courses{$cid};
                   16147:                 }
                   16148:             }
                   16149:         }
                   16150:         %courses = %showcourses;
                   16151:     }
                   16152:     return %courses;
                   16153: }
                   16154: 
                   16155: =pod
                   16156: 
1.1181    raeburn  16157: =back
                   16158: 
1.1207    raeburn  16159: =head1 Routines for version requirements for current course.
                   16160: 
                   16161: =over 4
                   16162: 
                   16163: =item * &check_release_required()
                   16164: 
                   16165: Compares required LON-CAPA version with version on server, and
                   16166: if required version is newer looks for a server with the required version.
                   16167: 
                   16168: Looks first at servers in user's owen domain; if none suitable, looks at
                   16169: servers in course's domain are permitted to host sessions for user's domain.
                   16170: 
                   16171: Inputs:
                   16172: 
                   16173: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
                   16174: 
                   16175: $courseid - Course ID of current course
                   16176: 
                   16177: $rolecode - User's current role in course (for switchserver query string).
                   16178: 
                   16179: $required - LON-CAPA version needed by course (format: Major.Minor).
                   16180: 
                   16181: 
                   16182: Returns:
                   16183: 
                   16184: $switchserver - query string tp append to /adm/switchserver call (if 
                   16185:                 current server's LON-CAPA version is too old. 
                   16186: 
                   16187: $warning - Message is displayed if no suitable server could be found.
                   16188: 
                   16189: =cut
                   16190: 
                   16191: sub check_release_required {
                   16192:     my ($loncaparev,$courseid,$rolecode,$required) = @_;
                   16193:     my ($switchserver,$warning);
                   16194:     if ($required ne '') {
                   16195:         my ($reqdmajor,$reqdminor) = ($required =~ /^(\d+)\.(\d+)$/);
                   16196:         my ($major,$minor) = ($loncaparev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                   16197:         if ($reqdmajor ne '' && $reqdminor ne '') {
                   16198:             my $otherserver;
                   16199:             if (($major eq '' && $minor eq '') ||
                   16200:                 (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)))) {
                   16201:                 my ($userdomserver) = &Apache::lonnet::choose_server($env{'user.domain'},undef,$required,1);
                   16202:                 my $switchlcrev =
                   16203:                     &Apache::lonnet::get_server_loncaparev($env{'user.domain'},
                   16204:                                                            $userdomserver);
                   16205:                 my ($swmajor,$swminor) = ($switchlcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                   16206:                 if (($swmajor eq '' && $swminor eq '') || ($reqdmajor > $swmajor) ||
                   16207:                     (($reqdmajor == $swmajor) && ($reqdminor > $swminor))) {
                   16208:                     my $cdom = $env{'course.'.$courseid.'.domain'};
                   16209:                     if ($cdom ne $env{'user.domain'}) {
                   16210:                         my ($coursedomserver,$coursehostname) = &Apache::lonnet::choose_server($cdom,undef,$required,1);
                   16211:                         my $serverhomeID = &Apache::lonnet::get_server_homeID($coursehostname);
                   16212:                         my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
                   16213:                         my %defdomdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
                   16214:                         my %udomdefaults = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
                   16215:                         my $remoterev = &Apache::lonnet::get_server_loncaparev($serverhomedom,$coursedomserver);
                   16216:                         my $canhost =
                   16217:                             &Apache::lonnet::can_host_session($env{'user.domain'},
                   16218:                                                               $coursedomserver,
                   16219:                                                               $remoterev,
                   16220:                                                               $udomdefaults{'remotesessions'},
                   16221:                                                               $defdomdefaults{'hostedsessions'});
                   16222: 
                   16223:                         if ($canhost) {
                   16224:                             $otherserver = $coursedomserver;
                   16225:                         } else {
                   16226:                             $warning = &mt('Requires LON-CAPA version [_1].',$env{'course.'.$courseid.'.internal.releaserequired'}).'<br />'. &mt("No suitable server could be found amongst servers in either your own domain or in the course's domain.");
                   16227:                         }
                   16228:                     } else {
                   16229:                         $warning = &mt('Requires LON-CAPA version [_1].',$env{'course.'.$courseid.'.internal.releaserequired'}).'<br />'.&mt("No suitable server could be found amongst servers in your own domain (which is also the course's domain).");
                   16230:                     }
                   16231:                 } else {
                   16232:                     $otherserver = $userdomserver;
                   16233:                 }
                   16234:             }
                   16235:             if ($otherserver ne '') {
                   16236:                 $switchserver = 'otherserver='.$otherserver.'&amp;role='.$rolecode;
                   16237:             }
                   16238:         }
                   16239:     }
                   16240:     return ($switchserver,$warning);
                   16241: }
                   16242: 
                   16243: =pod
                   16244: 
                   16245: =item * &check_release_result()
                   16246: 
                   16247: Inputs:
                   16248: 
                   16249: $switchwarning - Warning message if no suitable server found to host session.
                   16250: 
                   16251: $switchserver - query string to append to /adm/switchserver containing lonHostID
                   16252:                 and current role.
                   16253: 
                   16254: Returns: HTML to display with information about requirement to switch server.
                   16255:          Either displaying warning with link to Roles/Courses screen or
                   16256:          display link to switchserver.
                   16257: 
1.1181    raeburn  16258: =cut
                   16259: 
1.1207    raeburn  16260: sub check_release_result {
                   16261:     my ($switchwarning,$switchserver) = @_;
                   16262:     my $output = &start_page('Selected course unavailable on this server').
                   16263:                  '<p class="LC_warning">';
                   16264:     if ($switchwarning) {
                   16265:         $output .= $switchwarning.'<br /><a href="/adm/roles">';
                   16266:         if (&show_course()) {
                   16267:             $output .= &mt('Display courses');
                   16268:         } else {
                   16269:             $output .= &mt('Display roles');
                   16270:         }
                   16271:         $output .= '</a>';
                   16272:     } elsif ($switchserver) {
                   16273:         $output .= &mt('This course requires a newer version of LON-CAPA than is installed on this server.').
                   16274:                    '<br />'.
                   16275:                    '<a href="/adm/switchserver?'.$switchserver.'">'.
                   16276:                    &mt('Switch Server').
                   16277:                    '</a>';
                   16278:     }
                   16279:     $output .= '</p>'.&end_page();
                   16280:     return $output;
                   16281: }
                   16282: 
                   16283: =pod
                   16284: 
                   16285: =item * &needs_coursereinit()
                   16286: 
                   16287: Determine if course contents stored for user's session needs to be
                   16288: refreshed, because content has changed since "Big Hash" last tied.
                   16289: 
                   16290: Check for change is made if time last checked is more than 10 minutes ago
                   16291: (by default).
                   16292: 
                   16293: Inputs:
                   16294: 
                   16295: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
                   16296: 
                   16297: $interval (optional) - Time which may elapse (in s) between last check for content
                   16298:                        change in current course. (default: 600 s).  
                   16299: 
                   16300: Returns: an array; first element is:
                   16301: 
                   16302: =over 4
                   16303: 
                   16304: 'switch' - if content updates mean user's session
                   16305:            needs to be switched to a server running a newer LON-CAPA version
                   16306:  
                   16307: 'update' - if course session needs to be refreshed (i.e., Big Hash needs to be reloaded)
                   16308:            on current server hosting user's session                
                   16309: 
                   16310: ''       - if no action required.
                   16311: 
                   16312: =back
                   16313: 
                   16314: If first item element is 'switch':
                   16315: 
                   16316: second item is $switchwarning - Warning message if no suitable server found to host session. 
                   16317: 
                   16318: third item is $switchserver - query string to append to /adm/switchserver containing lonHostID
                   16319:                               and current role. 
                   16320: 
                   16321: otherwise: no other elements returned.
                   16322: 
                   16323: =back
                   16324: 
                   16325: =cut
                   16326: 
                   16327: sub needs_coursereinit {
                   16328:     my ($loncaparev,$interval) = @_;
                   16329:     return() unless ($env{'request.course.id'} && $env{'request.course.tied'});
                   16330:     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   16331:     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   16332:     my $now = time;
                   16333:     if ($interval eq '') {
                   16334:         $interval = 600;
                   16335:     }
                   16336:     if (($now-$env{'request.course.timechecked'})>$interval) {
                   16337:         my $lastchange = &Apache::lonnet::get_coursechange($cdom,$cnum);
                   16338:         &Apache::lonnet::appenv({'request.course.timechecked'=>$now});
                   16339:         if ($lastchange > $env{'request.course.tied'}) {
                   16340:             my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
                   16341:             if ($curr_reqd_hash{'internal.releaserequired'} ne '') {
                   16342:                 my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'};
                   16343:                 if ($curr_reqd_hash{'internal.releaserequired'} ne $required) {
                   16344:                     &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' =>
                   16345:                                              $curr_reqd_hash{'internal.releaserequired'}});
                   16346:                     my ($switchserver,$switchwarning) =
                   16347:                         &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'},
                   16348:                                                 $curr_reqd_hash{'internal.releaserequired'});
                   16349:                     if ($switchwarning ne '' || $switchserver ne '') {
                   16350:                         return ('switch',$switchwarning,$switchserver);
                   16351:                     }
                   16352:                 }
                   16353:             }
                   16354:             return ('update');
                   16355:         }
                   16356:     }
                   16357:     return ();
                   16358: }
1.1181    raeburn  16359: 
1.1083    raeburn  16360: sub update_content_constraints {
                   16361:     my ($cdom,$cnum,$chome,$cid) = @_;
                   16362:     my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
                   16363:     my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
                   16364:     my %checkresponsetypes;
                   16365:     foreach my $key (keys(%Apache::lonnet::needsrelease)) {
1.1236    raeburn  16366:         my ($item,$name,$value) = split(/:/,$key);
1.1083    raeburn  16367:         if ($item eq 'resourcetag') {
                   16368:             if ($name eq 'responsetype') {
                   16369:                 $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
                   16370:             }
                   16371:         }
                   16372:     }
                   16373:     my $navmap = Apache::lonnavmaps::navmap->new();
                   16374:     if (defined($navmap)) {
                   16375:         my %allresponses;
                   16376:         foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() },1,0)) {
                   16377:             my %responses = $res->responseTypes();
                   16378:             foreach my $key (keys(%responses)) {
                   16379:                 next unless(exists($checkresponsetypes{$key}));
                   16380:                 $allresponses{$key} += $responses{$key};
                   16381:             }
                   16382:         }
                   16383:         foreach my $key (keys(%allresponses)) {
                   16384:             my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
                   16385:             if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
                   16386:                 ($reqdmajor,$reqdminor) = ($major,$minor);
                   16387:             }
                   16388:         }
                   16389:         undef($navmap);
                   16390:     }
                   16391:     unless (($reqdmajor eq '') && ($reqdminor eq '')) {
                   16392:         &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
                   16393:     }
                   16394:     return;
                   16395: }
                   16396: 
1.1110    raeburn  16397: sub allmaps_incourse {
                   16398:     my ($cdom,$cnum,$chome,$cid) = @_;
                   16399:     if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
                   16400:         $cid = $env{'request.course.id'};
                   16401:         $cdom = $env{'course.'.$cid.'.domain'};
                   16402:         $cnum = $env{'course.'.$cid.'.num'};
                   16403:         $chome = $env{'course.'.$cid.'.home'};
                   16404:     }
                   16405:     my %allmaps = ();
                   16406:     my $lastchange =
                   16407:         &Apache::lonnet::get_coursechange($cdom,$cnum);
                   16408:     if ($lastchange > $env{'request.course.tied'}) {
                   16409:         my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
                   16410:         unless ($ferr) {
                   16411:             &update_content_constraints($cdom,$cnum,$chome,$cid);
                   16412:         }
                   16413:     }
                   16414:     my $navmap = Apache::lonnavmaps::navmap->new();
                   16415:     if (defined($navmap)) {
                   16416:         foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) {
                   16417:             $allmaps{$res->src()} = 1;
                   16418:         }
                   16419:     }
                   16420:     return \%allmaps;
                   16421: }
                   16422: 
1.1083    raeburn  16423: sub parse_supplemental_title {
                   16424:     my ($title) = @_;
                   16425: 
                   16426:     my ($foldertitle,$renametitle);
                   16427:     if ($title =~ /&amp;&amp;&amp;/) {
                   16428:         $title = &HTML::Entites::decode($title);
                   16429:     }
                   16430:     if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
                   16431:         $renametitle=$4;
                   16432:         my ($time,$uname,$udom) = ($1,$2,$3);
                   16433:         $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
                   16434:         my $name =  &plainname($uname,$udom);
                   16435:         $name = &HTML::Entities::encode($name,'"<>&\'');
                   16436:         $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
                   16437:         $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.
                   16438:             $name.': <br />'.$foldertitle;
                   16439:     }
                   16440:     if (wantarray) {
                   16441:         return ($title,$foldertitle,$renametitle);
                   16442:     }
                   16443:     return $title;
                   16444: }
                   16445: 
1.1143    raeburn  16446: sub recurse_supplemental {
                   16447:     my ($cnum,$cdom,$suppmap,$numfiles,$errors) = @_;
                   16448:     if ($suppmap) {
                   16449:         my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
                   16450:         if ($fatal) {
                   16451:             $errors ++;
                   16452:         } else {
                   16453:             if ($#LONCAPA::map::resources > 0) {
                   16454:                 foreach my $res (@LONCAPA::map::resources) {
                   16455:                     my ($title,$src,$ext,$type,$status)=split(/\:/,$res);
                   16456:                     if (($src ne '') && ($status eq 'res')) {
1.1146    raeburn  16457:                         if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
                   16458:                             ($numfiles,$errors) = &recurse_supplemental($cnum,$cdom,$1,$numfiles,$errors);
1.1143    raeburn  16459:                         } else {
                   16460:                             $numfiles ++;
                   16461:                         }
                   16462:                     }
                   16463:                 }
                   16464:             }
                   16465:         }
                   16466:     }
                   16467:     return ($numfiles,$errors);
                   16468: }
                   16469: 
1.1101    raeburn  16470: sub symb_to_docspath {
                   16471:     my ($symb) = @_;
                   16472:     return unless ($symb);
                   16473:     my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
                   16474:     if ($resurl=~/\.(sequence|page)$/) {
                   16475:         $mapurl=$resurl;
                   16476:     } elsif ($resurl eq 'adm/navmaps') {
                   16477:         $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
                   16478:     }
                   16479:     my $mapresobj;
                   16480:     my $navmap = Apache::lonnavmaps::navmap->new();
                   16481:     if (ref($navmap)) {
                   16482:         $mapresobj = $navmap->getResourceByUrl($mapurl);
                   16483:     }
                   16484:     $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
                   16485:     my $type=$2;
                   16486:     my $path;
                   16487:     if (ref($mapresobj)) {
                   16488:         my $pcslist = $mapresobj->map_hierarchy();
                   16489:         if ($pcslist ne '') {
                   16490:             foreach my $pc (split(/,/,$pcslist)) {
                   16491:                 next if ($pc <= 1);
                   16492:                 my $res = $navmap->getByMapPc($pc);
                   16493:                 if (ref($res)) {
                   16494:                     my $thisurl = $res->src();
                   16495:                     $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
                   16496:                     my $thistitle = $res->title();
                   16497:                     $path .= '&'.
                   16498:                              &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
1.1146    raeburn  16499:                              &escape($thistitle).
1.1101    raeburn  16500:                              ':'.$res->randompick().
                   16501:                              ':'.$res->randomout().
                   16502:                              ':'.$res->encrypted().
                   16503:                              ':'.$res->randomorder().
                   16504:                              ':'.$res->is_page();
                   16505:                 }
                   16506:             }
                   16507:         }
                   16508:         $path =~ s/^\&//;
                   16509:         my $maptitle = $mapresobj->title();
                   16510:         if ($mapurl eq 'default') {
1.1129    raeburn  16511:             $maptitle = 'Main Content';
1.1101    raeburn  16512:         }
                   16513:         $path .= (($path ne '')? '&' : '').
                   16514:                  &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146    raeburn  16515:                  &escape($maptitle).
1.1101    raeburn  16516:                  ':'.$mapresobj->randompick().
                   16517:                  ':'.$mapresobj->randomout().
                   16518:                  ':'.$mapresobj->encrypted().
                   16519:                  ':'.$mapresobj->randomorder().
                   16520:                  ':'.$mapresobj->is_page();
                   16521:     } else {
                   16522:         my $maptitle = &Apache::lonnet::gettitle($mapurl);
                   16523:         my $ispage = (($type eq 'page')? 1 : '');
                   16524:         if ($mapurl eq 'default') {
1.1129    raeburn  16525:             $maptitle = 'Main Content';
1.1101    raeburn  16526:         }
                   16527:         $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146    raeburn  16528:                 &escape($maptitle).':::::'.$ispage;
1.1101    raeburn  16529:     }
                   16530:     unless ($mapurl eq 'default') {
                   16531:         $path = 'default&'.
1.1146    raeburn  16532:                 &escape('Main Content').
1.1101    raeburn  16533:                 ':::::&'.$path;
                   16534:     }
                   16535:     return $path;
                   16536: }
                   16537: 
1.1094    raeburn  16538: sub captcha_display {
                   16539:     my ($context,$lonhost) = @_;
                   16540:     my ($output,$error);
1.1234    raeburn  16541:     my ($captcha,$pubkey,$privkey,$version) = 
                   16542:         &get_captcha_config($context,$lonhost);
1.1095    raeburn  16543:     if ($captcha eq 'original') {
1.1094    raeburn  16544:         $output = &create_captcha();
                   16545:         unless ($output) {
1.1172    raeburn  16546:             $error = 'captcha';
1.1094    raeburn  16547:         }
                   16548:     } elsif ($captcha eq 'recaptcha') {
1.1234    raeburn  16549:         $output = &create_recaptcha($pubkey,$version);
1.1094    raeburn  16550:         unless ($output) {
1.1172    raeburn  16551:             $error = 'recaptcha';
1.1094    raeburn  16552:         }
                   16553:     }
1.1234    raeburn  16554:     return ($output,$error,$captcha,$version);
1.1094    raeburn  16555: }
                   16556: 
                   16557: sub captcha_response {
                   16558:     my ($context,$lonhost) = @_;
                   16559:     my ($captcha_chk,$captcha_error);
1.1234    raeburn  16560:     my ($captcha,$pubkey,$privkey,$version) = &get_captcha_config($context,$lonhost);
1.1095    raeburn  16561:     if ($captcha eq 'original') {
1.1094    raeburn  16562:         ($captcha_chk,$captcha_error) = &check_captcha();
                   16563:     } elsif ($captcha eq 'recaptcha') {
1.1234    raeburn  16564:         $captcha_chk = &check_recaptcha($privkey,$version);
1.1094    raeburn  16565:     } else {
                   16566:         $captcha_chk = 1;
                   16567:     }
                   16568:     return ($captcha_chk,$captcha_error);
                   16569: }
                   16570: 
                   16571: sub get_captcha_config {
                   16572:     my ($context,$lonhost) = @_;
1.1234    raeburn  16573:     my ($captcha,$pubkey,$privkey,$version,$hashtocheck);
1.1094    raeburn  16574:     my $hostname = &Apache::lonnet::hostname($lonhost);
                   16575:     my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
                   16576:     my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
1.1095    raeburn  16577:     if ($context eq 'usercreation') {
                   16578:         my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
                   16579:         if (ref($domconfig{$context}) eq 'HASH') {
                   16580:             $hashtocheck = $domconfig{$context}{'cancreate'};
                   16581:             if (ref($hashtocheck) eq 'HASH') {
                   16582:                 if ($hashtocheck->{'captcha'} eq 'recaptcha') {
                   16583:                     if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
                   16584:                         $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
                   16585:                         $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
                   16586:                     }
                   16587:                     if ($privkey && $pubkey) {
                   16588:                         $captcha = 'recaptcha';
1.1234    raeburn  16589:                         $version = $hashtocheck->{'recaptchaversion'};
                   16590:                         if ($version ne '2') {
                   16591:                             $version = 1;
                   16592:                         }
1.1095    raeburn  16593:                     } else {
                   16594:                         $captcha = 'original';
                   16595:                     }
                   16596:                 } elsif ($hashtocheck->{'captcha'} ne 'notused') {
                   16597:                     $captcha = 'original';
                   16598:                 }
1.1094    raeburn  16599:             }
1.1095    raeburn  16600:         } else {
                   16601:             $captcha = 'captcha';
                   16602:         }
                   16603:     } elsif ($context eq 'login') {
                   16604:         my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
                   16605:         if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
                   16606:             $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
                   16607:             $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
1.1094    raeburn  16608:             if ($privkey && $pubkey) {
                   16609:                 $captcha = 'recaptcha';
1.1234    raeburn  16610:                 $version = $domconfhash{$serverhomedom.'.login.recaptchaversion'};
                   16611:                 if ($version ne '2') {
                   16612:                     $version = 1; 
                   16613:                 }
1.1095    raeburn  16614:             } else {
                   16615:                 $captcha = 'original';
1.1094    raeburn  16616:             }
1.1095    raeburn  16617:         } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
                   16618:             $captcha = 'original';
1.1094    raeburn  16619:         }
                   16620:     }
1.1234    raeburn  16621:     return ($captcha,$pubkey,$privkey,$version);
1.1094    raeburn  16622: }
                   16623: 
                   16624: sub create_captcha {
                   16625:     my %captcha_params = &captcha_settings();
                   16626:     my ($output,$maxtries,$tries) = ('',10,0);
                   16627:     while ($tries < $maxtries) {
                   16628:         $tries ++;
                   16629:         my $captcha = Authen::Captcha->new (
                   16630:                                            output_folder => $captcha_params{'output_dir'},
                   16631:                                            data_folder   => $captcha_params{'db_dir'},
                   16632:                                           );
                   16633:         my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
                   16634: 
                   16635:         if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
                   16636:             $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
                   16637:                       &mt('Type in the letters/numbers shown below').'&nbsp;'.
1.1176    raeburn  16638:                       '<input type="text" size="5" name="code" value="" autocomplete="off" />'.
                   16639:                       '<br />'.
                   16640:                       '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
1.1094    raeburn  16641:             last;
                   16642:         }
                   16643:     }
                   16644:     return $output;
                   16645: }
                   16646: 
                   16647: sub captcha_settings {
                   16648:     my %captcha_params = (
                   16649:                            output_dir     => $Apache::lonnet::perlvar{'lonCaptchaDir'},
                   16650:                            www_output_dir => "/captchaspool",
                   16651:                            db_dir         => $Apache::lonnet::perlvar{'lonCaptchaDb'},
                   16652:                            numchars       => '5',
                   16653:                          );
                   16654:     return %captcha_params;
                   16655: }
                   16656: 
                   16657: sub check_captcha {
                   16658:     my ($captcha_chk,$captcha_error);
                   16659:     my $code = $env{'form.code'};
                   16660:     my $md5sum = $env{'form.crypt'};
                   16661:     my %captcha_params = &captcha_settings();
                   16662:     my $captcha = Authen::Captcha->new(
                   16663:                       output_folder => $captcha_params{'output_dir'},
                   16664:                       data_folder   => $captcha_params{'db_dir'},
                   16665:                   );
1.1109    raeburn  16666:     $captcha_chk = $captcha->check_code($code,$md5sum);
1.1094    raeburn  16667:     my %captcha_hash = (
                   16668:                         0       => 'Code not checked (file error)',
                   16669:                        -1      => 'Failed: code expired',
                   16670:                        -2      => 'Failed: invalid code (not in database)',
                   16671:                        -3      => 'Failed: invalid code (code does not match crypt)',
                   16672:     );
                   16673:     if ($captcha_chk != 1) {
                   16674:         $captcha_error = $captcha_hash{$captcha_chk}
                   16675:     }
                   16676:     return ($captcha_chk,$captcha_error);
                   16677: }
                   16678: 
                   16679: sub create_recaptcha {
1.1234    raeburn  16680:     my ($pubkey,$version) = @_;
                   16681:     if ($version >= 2) {
                   16682:         return '<div class="g-recaptcha" data-sitekey="'.$pubkey.'"></div>';
                   16683:     } else {
                   16684:         my $use_ssl;
                   16685:         if ($ENV{'SERVER_PORT'} == 443) {
                   16686:             $use_ssl = 1;
                   16687:         }
                   16688:         my $captcha = Captcha::reCAPTCHA->new;
                   16689:         return $captcha->get_options_setter({theme => 'white'})."\n".
                   16690:                $captcha->get_html($pubkey,undef,$use_ssl).
                   16691:                &mt('If the text is hard to read, [_1] will replace them.',
                   16692:                    '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
                   16693:                '<br /><br />';
                   16694:     }
1.1094    raeburn  16695: }
                   16696: 
                   16697: sub check_recaptcha {
1.1234    raeburn  16698:     my ($privkey,$version) = @_;
1.1094    raeburn  16699:     my $captcha_chk;
1.1234    raeburn  16700:     if ($version >= 2) {
                   16701:         my $ua = LWP::UserAgent->new;
                   16702:         $ua->timeout(10);
                   16703:         my %info = (
                   16704:                      secret   => $privkey, 
                   16705:                      response => $env{'form.g-recaptcha-response'},
                   16706:                      remoteip => $ENV{'REMOTE_ADDR'},
                   16707:                    );
                   16708:         my $response = $ua->post('https://www.google.com/recaptcha/api/siteverify',\%info);
                   16709:         if ($response->is_success)  {
                   16710:             my $data = JSON::DWIW->from_json($response->decoded_content);
                   16711:             if (ref($data) eq 'HASH') {
                   16712:                 if ($data->{'success'}) {
                   16713:                     $captcha_chk = 1;
                   16714:                 }
                   16715:             }
                   16716:         }
                   16717:     } else {
                   16718:         my $captcha = Captcha::reCAPTCHA->new;
                   16719:         my $captcha_result =
                   16720:             $captcha->check_answer(
                   16721:                                     $privkey,
                   16722:                                     $ENV{'REMOTE_ADDR'},
                   16723:                                     $env{'form.recaptcha_challenge_field'},
                   16724:                                     $env{'form.recaptcha_response_field'},
                   16725:                                   );
                   16726:         if ($captcha_result->{is_valid}) {
                   16727:             $captcha_chk = 1;
                   16728:         }
1.1094    raeburn  16729:     }
                   16730:     return $captcha_chk;
                   16731: }
                   16732: 
1.1174    raeburn  16733: sub emailusername_info {
1.1177    raeburn  16734:     my @fields = ('firstname','lastname','institution','web','location','officialemail');
1.1174    raeburn  16735:     my %titles = &Apache::lonlocal::texthash (
                   16736:                      lastname      => 'Last Name',
                   16737:                      firstname     => 'First Name',
                   16738:                      institution   => 'School/college/university',
                   16739:                      location      => "School's city, state/province, country",
                   16740:                      web           => "School's web address",
                   16741:                      officialemail => 'E-mail address at institution (if different)',
                   16742:                  );
                   16743:     return (\@fields,\%titles);
                   16744: }
                   16745: 
1.1161    raeburn  16746: sub cleanup_html {
                   16747:     my ($incoming) = @_;
                   16748:     my $outgoing;
                   16749:     if ($incoming ne '') {
                   16750:         $outgoing = $incoming;
                   16751:         $outgoing =~ s/;/&#059;/g;
                   16752:         $outgoing =~ s/\#/&#035;/g;
                   16753:         $outgoing =~ s/\&/&#038;/g;
                   16754:         $outgoing =~ s/</&#060;/g;
                   16755:         $outgoing =~ s/>/&#062;/g;
                   16756:         $outgoing =~ s/\(/&#040/g;
                   16757:         $outgoing =~ s/\)/&#041;/g;
                   16758:         $outgoing =~ s/"/&#034;/g;
                   16759:         $outgoing =~ s/'/&#039;/g;
                   16760:         $outgoing =~ s/\$/&#036;/g;
                   16761:         $outgoing =~ s{/}{&#047;}g;
                   16762:         $outgoing =~ s/=/&#061;/g;
                   16763:         $outgoing =~ s/\\/&#092;/g
                   16764:     }
                   16765:     return $outgoing;
                   16766: }
                   16767: 
1.1190    musolffc 16768: # Checks for critical messages and returns a redirect url if one exists.
                   16769: # $interval indicates how often to check for messages.
                   16770: sub critical_redirect {
                   16771:     my ($interval) = @_;
                   16772:     if ((time-$env{'user.criticalcheck.time'})>$interval) {
                   16773:         my @what=&Apache::lonnet::dump('critical', $env{'user.domain'}, 
                   16774:                                         $env{'user.name'});
                   16775:         &Apache::lonnet::appenv({'user.criticalcheck.time'=>time});
1.1191    raeburn  16776:         my $redirecturl;
1.1190    musolffc 16777:         if ($what[0]) {
                   16778: 	    if (($what[0] ne 'con_lost') && ($what[0]!~/^error\:/)) {
                   16779: 	        $redirecturl='/adm/email?critical=display';
1.1191    raeburn  16780: 	        my $url=&Apache::lonnet::absolute_url().$redirecturl;
                   16781:                 return (1, $url);
1.1190    musolffc 16782:             }
1.1191    raeburn  16783:         }
                   16784:     } 
                   16785:     return ();
1.1190    musolffc 16786: }
                   16787: 
1.1174    raeburn  16788: # Use:
                   16789: #   my $answer=reply("encrypt:passwd:$udom:$uname:$upass",$tryserver);
                   16790: #
                   16791: ##################################################
                   16792: #          password associated functions         #
                   16793: ##################################################
                   16794: sub des_keys {
                   16795:     # Make a new key for DES encryption.
                   16796:     # Each key has two parts which are returned separately.
                   16797:     # Please note:  Each key must be passed through the &hex function
                   16798:     # before it is output to the web browser.  The hex versions cannot
                   16799:     # be used to decrypt.
                   16800:     my @hexstr=('0','1','2','3','4','5','6','7',
                   16801:                 '8','9','a','b','c','d','e','f');
                   16802:     my $lkey='';
                   16803:     for (0..7) {
                   16804:         $lkey.=$hexstr[rand(15)];
                   16805:     }
                   16806:     my $ukey='';
                   16807:     for (0..7) {
                   16808:         $ukey.=$hexstr[rand(15)];
                   16809:     }
                   16810:     return ($lkey,$ukey);
                   16811: }
                   16812: 
                   16813: sub des_decrypt {
                   16814:     my ($key,$cyphertext) = @_;
                   16815:     my $keybin=pack("H16",$key);
                   16816:     my $cypher;
                   16817:     if ($Crypt::DES::VERSION>=2.03) {
                   16818:         $cypher=new Crypt::DES $keybin;
                   16819:     } else {
                   16820:         $cypher=new DES $keybin;
                   16821:     }
1.1233    raeburn  16822:     my $plaintext='';
                   16823:     my $cypherlength = length($cyphertext);
                   16824:     my $numchunks = int($cypherlength/32);
                   16825:     for (my $j=0; $j<$numchunks; $j++) {
                   16826:         my $start = $j*32;
                   16827:         my $cypherblock = substr($cyphertext,$start,32);
                   16828:         my $chunk =
                   16829:             $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,0,16))));
                   16830:         $chunk .=
                   16831:             $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,16,16))));
                   16832:         $chunk=substr($chunk,1,ord(substr($chunk,0,1)) );
                   16833:         $plaintext .= $chunk;
                   16834:     }
1.1174    raeburn  16835:     return $plaintext;
                   16836: }
                   16837: 
1.112     bowersj2 16838: 1;
                   16839: __END__;
1.41      ng       16840: 

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