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

1.10      albertel    1: # The LearningOnline Network with CAPA
1.1       albertel    2: # a pile of common routines
1.10      albertel    3: #
1.1378  ! raeburn     4: # $Id: loncommon.pm,v 1.1377 2022/02/16 16:28:49 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.1280    raeburn    74: use LONCAPA::LWPReq;
1.1328    raeburn    75: use HTTP::Request;
1.657     raeburn    76: use DateTime::TimeZone;
1.1241    raeburn    77: use DateTime::Locale;
1.1220    raeburn    78: use Encode();
1.1091    foxr       79: use Text::Aspell;
1.1094    raeburn    80: use Authen::Captcha;
                     81: use Captcha::reCAPTCHA;
1.1234    raeburn    82: use JSON::DWIW;
1.1174    raeburn    83: use Crypt::DES;
                     84: use DynaLoader; # for Crypt::DES version
1.1223    musolffc   85: use MIME::Lite;
                     86: use MIME::Types;
1.1292    raeburn    87: use File::Copy();
1.1300    raeburn    88: use File::Path();
1.1309    raeburn    89: use String::CRC32();
                     90: use Short::URL();
1.117     www        91: 
1.517     raeburn    92: # ---------------------------------------------- Designs
                     93: use vars qw(%defaultdesign);
                     94: 
1.22      www        95: my $readit;
                     96: 
1.517     raeburn    97: 
1.157     matthew    98: ##
                     99: ## Global Variables
                    100: ##
1.46      matthew   101: 
1.643     foxr      102: 
                    103: # ----------------------------------------------- SSI with retries:
                    104: #
                    105: 
                    106: =pod
                    107: 
1.648     raeburn   108: =head1 Server Side include with retries:
1.643     foxr      109: 
                    110: =over 4
                    111: 
1.648     raeburn   112: =item * &ssi_with_retries(resource,retries form)
1.643     foxr      113: 
                    114: Performs an ssi with some number of retries.  Retries continue either
                    115: until the result is ok or until the retry count supplied by the
                    116: caller is exhausted.  
                    117: 
                    118: Inputs:
1.648     raeburn   119: 
                    120: =over 4
                    121: 
1.643     foxr      122: resource   - Identifies the resource to insert.
1.648     raeburn   123: 
1.643     foxr      124: retries    - Count of the number of retries allowed.
1.648     raeburn   125: 
1.643     foxr      126: form       - Hash that identifies the rendering options.
                    127: 
1.648     raeburn   128: =back
                    129: 
                    130: Returns:
                    131: 
                    132: =over 4
                    133: 
1.643     foxr      134: content    - The content of the response.  If retries were exhausted this is empty.
1.648     raeburn   135: 
1.643     foxr      136: response   - The response from the last attempt (which may or may not have been successful.
                    137: 
1.648     raeburn   138: =back
                    139: 
                    140: =back
                    141: 
1.643     foxr      142: =cut
                    143: 
                    144: sub ssi_with_retries {
                    145:     my ($resource, $retries, %form) = @_;
                    146: 
                    147: 
                    148:     my $ok = 0;			# True if we got a good response.
                    149:     my $content;
                    150:     my $response;
                    151: 
                    152:     # Try to get the ssi done. within the retries count:
                    153: 
                    154:     do {
                    155: 	($content, $response) = &Apache::lonnet::ssi($resource, %form);
                    156: 	$ok      = $response->is_success;
1.650     www       157:         if (!$ok) {
                    158:             &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
                    159:         }
1.643     foxr      160: 	$retries--;
                    161:     } while (!$ok && ($retries > 0));
                    162: 
                    163:     if (!$ok) {
                    164: 	$content = '';		# On error return an empty content.
                    165:     }
                    166:     return ($content, $response);
                    167: 
                    168: }
                    169: 
                    170: 
                    171: 
1.20      www       172: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12      harris41  173: my %language;
1.124     www       174: my %supported_language;
1.1088    foxr      175: my %supported_codes;
1.1048    foxr      176: my %latex_language;		# For choosing hyphenation in <transl..>
                    177: my %latex_language_bykey;	# for choosing hyphenation from metadata
1.12      harris41  178: my %cprtag;
1.192     taceyjo1  179: my %scprtag;
1.351     www       180: my %fe; my %fd; my %fm;
1.41      ng        181: my %category_extensions;
1.12      harris41  182: 
1.46      matthew   183: # ---------------------------------------------- Thesaurus variables
1.144     matthew   184: #
                    185: # %Keywords:
                    186: #      A hash used by &keyword to determine if a word is considered a keyword.
                    187: # $thesaurus_db_file 
                    188: #      Scalar containing the full path to the thesaurus database.
1.46      matthew   189: 
                    190: my %Keywords;
                    191: my $thesaurus_db_file;
                    192: 
1.144     matthew   193: #
                    194: # Initialize values from language.tab, copyright.tab, filetypes.tab,
                    195: # thesaurus.tab, and filecategories.tab.
                    196: #
1.18      www       197: BEGIN {
1.46      matthew   198:     # Variable initialization
                    199:     $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
                    200:     #
1.22      www       201:     unless ($readit) {
1.12      harris41  202: # ------------------------------------------------------------------- languages
                    203:     {
1.158     raeburn   204:         my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    205:                                    '/language.tab';
1.1317    raeburn   206:         if ( open(my $fh,'<',$langtabfile) ) {
1.356     albertel  207:             while (my $line = <$fh>) {
                    208:                 next if ($line=~/^\#/);
                    209:                 chomp($line);
1.1088    foxr      210:                 my ($key,$code,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
1.158     raeburn   211:                 $language{$key}=$val.' - '.$enc;
                    212:                 if ($sup) {
                    213:                     $supported_language{$key}=$sup;
1.1088    foxr      214: 		    $supported_codes{$key}   = $code;
1.158     raeburn   215:                 }
1.1048    foxr      216: 		if ($latex) {
                    217: 		    $latex_language_bykey{$key} = $latex;
1.1088    foxr      218: 		    $latex_language{$code} = $latex;
1.1048    foxr      219: 		}
1.158     raeburn   220:             }
                    221:             close($fh);
                    222:         }
1.12      harris41  223:     }
                    224: # ------------------------------------------------------------------ copyrights
                    225:     {
1.158     raeburn   226:         my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    227:                                   '/copyright.tab';
1.1317    raeburn   228:         if ( open (my $fh,'<',$copyrightfile) ) {
1.356     albertel  229:             while (my $line = <$fh>) {
                    230:                 next if ($line=~/^\#/);
                    231:                 chomp($line);
                    232:                 my ($key,$val)=(split(/\s+/,$line,2));
1.158     raeburn   233:                 $cprtag{$key}=$val;
                    234:             }
                    235:             close($fh);
                    236:         }
1.12      harris41  237:     }
1.351     www       238: # ----------------------------------------------------------- source copyrights
1.192     taceyjo1  239:     {
                    240:         my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
                    241:                                   '/source_copyright.tab';
1.1317    raeburn   242:         if ( open (my $fh,'<',$sourcecopyrightfile) ) {
1.356     albertel  243:             while (my $line = <$fh>) {
                    244:                 next if ($line =~ /^\#/);
                    245:                 chomp($line);
                    246:                 my ($key,$val)=(split(/\s+/,$line,2));
1.192     taceyjo1  247:                 $scprtag{$key}=$val;
                    248:             }
                    249:             close($fh);
                    250:         }
                    251:     }
1.63      www       252: 
1.517     raeburn   253: # -------------------------------------------------------------- default domain designs
1.63      www       254:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517     raeburn   255:     my $designfile = $designdir.'/default.tab';
1.1317    raeburn   256:     if ( open (my $fh,'<',$designfile) ) {
1.517     raeburn   257:         while (my $line = <$fh>) {
                    258:             next if ($line =~ /^\#/);
                    259:             chomp($line);
                    260:             my ($key,$val)=(split(/\=/,$line));
                    261:             if ($val) { $defaultdesign{$key}=$val; }
                    262:         }
                    263:         close($fh);
1.63      www       264:     }
                    265: 
1.15      harris41  266: # ------------------------------------------------------------- file categories
                    267:     {
1.158     raeburn   268:         my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    269:                                   '/filecategories.tab';
1.1317    raeburn   270:         if ( open (my $fh,'<',$categoryfile) ) {
1.356     albertel  271: 	    while (my $line = <$fh>) {
                    272: 		next if ($line =~ /^\#/);
                    273: 		chomp($line);
                    274:                 my ($extension,$category)=(split(/\s+/,$line,2));
1.1263    raeburn   275:                 push(@{$category_extensions{lc($category)}},$extension);
1.158     raeburn   276:             }
                    277:             close($fh);
                    278:         }
                    279: 
1.15      harris41  280:     }
1.12      harris41  281: # ------------------------------------------------------------------ file types
                    282:     {
1.158     raeburn   283:         my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
                    284:                '/filetypes.tab';
1.1317    raeburn   285:         if ( open (my $fh,'<',$typesfile) ) {
1.356     albertel  286:             while (my $line = <$fh>) {
                    287: 		next if ($line =~ /^\#/);
                    288: 		chomp($line);
                    289:                 my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158     raeburn   290:                 if ($descr ne '') {
                    291:                     $fe{$ending}=lc($emb);
                    292:                     $fd{$ending}=$descr;
1.351     www       293:                     if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158     raeburn   294:                 }
                    295:             }
                    296:             close($fh);
                    297:         }
1.12      harris41  298:     }
1.22      www       299:     &Apache::lonnet::logthis(
1.705     tempelho  300:              "<span style='color:yellow;'>INFO: Read file types</span>");
1.22      www       301:     $readit=1;
1.46      matthew   302:     }  # end of unless($readit) 
1.32      matthew   303:     
                    304: }
1.112     bowersj2  305: 
1.42      matthew   306: ###############################################################
                    307: ##           HTML and Javascript Helper Functions            ##
                    308: ###############################################################
                    309: 
                    310: =pod 
                    311: 
1.112     bowersj2  312: =head1 HTML and Javascript Functions
1.42      matthew   313: 
1.112     bowersj2  314: =over 4
                    315: 
1.648     raeburn   316: =item * &browser_and_searcher_javascript()
1.112     bowersj2  317: 
                    318: X<browsing, javascript>X<searching, javascript>Returns a string
                    319: containing javascript with two functions, C<openbrowser> and
                    320: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
                    321: tags.
1.42      matthew   322: 
1.648     raeburn   323: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42      matthew   324: 
                    325: inputs: formname, elementname, only, omit
                    326: 
                    327: formname and elementname indicate the name of the html form and name of
                    328: the element that the results of the browsing selection are to be placed in. 
                    329: 
                    330: Specifying 'only' will restrict the browser to displaying only files
1.185     www       331: with the given extension.  Can be a comma separated list.
1.42      matthew   332: 
                    333: Specifying 'omit' will restrict the browser to NOT displaying files
1.185     www       334: with the given extension.  Can be a comma separated list.
1.42      matthew   335: 
1.648     raeburn   336: =item * &opensearcher(formname,elementname) [javascript]
1.42      matthew   337: 
                    338: Inputs: formname, elementname
                    339: 
                    340: formname and elementname specify the name of the html form and the name
                    341: of the element the selection from the search results will be placed in.
1.542     raeburn   342: 
1.42      matthew   343: =cut
                    344: 
                    345: sub browser_and_searcher_javascript {
1.199     albertel  346:     my ($mode)=@_;
                    347:     if (!defined($mode)) { $mode='edit'; }
1.453     albertel  348:     my $resurl=&escape_single(&lastresurl());
1.42      matthew   349:     return <<END;
1.219     albertel  350: // <!-- BEGIN LON-CAPA Internal
1.50      matthew   351:     var editbrowser = null;
1.135     albertel  352:     function openbrowser(formname,elementname,only,omit,titleelement) {
1.170     www       353:         var url = '$resurl/?';
1.42      matthew   354:         if (editbrowser == null) {
                    355:             url += 'launch=1&';
                    356:         }
                    357:         url += 'catalogmode=interactive&';
1.199     albertel  358:         url += 'mode=$mode&';
1.611     albertel  359:         url += 'inhibitmenu=yes&';
1.42      matthew   360:         url += 'form=' + formname + '&';
                    361:         if (only != null) {
                    362:             url += 'only=' + only + '&';
1.217     albertel  363:         } else {
                    364:             url += 'only=&';
                    365: 	}
1.42      matthew   366:         if (omit != null) {
                    367:             url += 'omit=' + omit + '&';
1.217     albertel  368:         } else {
                    369:             url += 'omit=&';
                    370: 	}
1.135     albertel  371:         if (titleelement != null) {
                    372:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  373:         } else {
                    374: 	    url += 'titleelement=&';
                    375: 	}
1.42      matthew   376:         url += 'element=' + elementname + '';
                    377:         var title = 'Browser';
1.435     albertel  378:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   379:         options += ',width=700,height=600';
                    380:         editbrowser = open(url,title,options,'1');
                    381:         editbrowser.focus();
                    382:     }
                    383:     var editsearcher;
1.135     albertel  384:     function opensearcher(formname,elementname,titleelement) {
1.42      matthew   385:         var url = '/adm/searchcat?';
                    386:         if (editsearcher == null) {
                    387:             url += 'launch=1&';
                    388:         }
                    389:         url += 'catalogmode=interactive&';
1.199     albertel  390:         url += 'mode=$mode&';
1.42      matthew   391:         url += 'form=' + formname + '&';
1.135     albertel  392:         if (titleelement != null) {
                    393:             url += 'titleelement=' + titleelement + '&';
1.217     albertel  394:         } else {
                    395: 	    url += 'titleelement=&';
                    396: 	}
1.42      matthew   397:         url += 'element=' + elementname + '';
                    398:         var title = 'Search';
1.435     albertel  399:         var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42      matthew   400:         options += ',width=700,height=600';
                    401:         editsearcher = open(url,title,options,'1');
                    402:         editsearcher.focus();
                    403:     }
1.219     albertel  404: // END LON-CAPA Internal -->
1.42      matthew   405: END
1.170     www       406: }
                    407: 
                    408: sub lastresurl {
1.258     albertel  409:     if ($env{'environment.lastresurl'}) {
                    410: 	return $env{'environment.lastresurl'}
1.170     www       411:     } else {
                    412: 	return '/res';
                    413:     }
                    414: }
                    415: 
                    416: sub storeresurl {
                    417:     my $resurl=&Apache::lonnet::clutter(shift);
                    418:     unless ($resurl=~/^\/res/) { return 0; }
                    419:     $resurl=~s/\/$//;
                    420:     &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646     raeburn   421:     &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170     www       422:     return 1;
1.42      matthew   423: }
                    424: 
1.74      www       425: sub studentbrowser_javascript {
1.111     www       426:    unless (
1.258     albertel  427:             (($env{'request.course.id'}) && 
1.302     albertel  428:              (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    429: 	      || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    430: 					  '/'.$env{'request.course.sec'})
                    431: 	      ))
1.258     albertel  432:          || ($env{'request.role'}=~/^(au|dc|su)/)
1.111     www       433:           ) { return ''; }  
1.74      www       434:    return (<<'ENDSTDBRW');
1.776     bisitz    435: <script type="text/javascript" language="Javascript">
1.824     bisitz    436: // <![CDATA[
1.74      www       437:     var stdeditbrowser;
1.1337    raeburn   438:     function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadv) {
1.74      www       439:         var url = '/adm/pickstudent?';
                    440:         var filter;
1.558     albertel  441: 	if (!ignorefilter) {
                    442: 	    eval('filter=document.'+formname+'.'+uname+'.value;');
                    443: 	}
1.74      www       444:         if (filter != null) {
                    445:            if (filter != '') {
                    446:                url += 'filter='+filter+'&';
                    447: 	   }
                    448:         }
                    449:         url += 'form=' + formname + '&unameelement='+uname+
1.999     www       450:                                     '&udomelement='+udom+
                    451:                                     '&clicker='+clicker;
1.111     www       452: 	if (roleflag) { url+="&roles=1"; }
1.1337    raeburn   453:         if (courseadv == 'condition') {
                    454:             if (document.getElementById('courseadv')) {
                    455:                 courseadv = document.getElementById('courseadv').value;
                    456:             }
                    457:         }
                    458:         if ((courseadv == 'only') || (courseadv == 'none')) { url+="&courseadv="+courseadv; }
1.102     www       459:         var title = 'Student_Browser';
1.74      www       460:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    461:         options += ',width=700,height=600';
                    462:         stdeditbrowser = open(url,title,options,'1');
                    463:         stdeditbrowser.focus();
                    464:     }
1.824     bisitz    465: // ]]>
1.74      www       466: </script>
                    467: ENDSTDBRW
                    468: }
1.42      matthew   469: 
1.1003    www       470: sub resourcebrowser_javascript {
                    471:    unless ($env{'request.course.id'}) { return ''; }
1.1004    www       472:    return (<<'ENDRESBRW');
1.1003    www       473: <script type="text/javascript" language="Javascript">
                    474: // <![CDATA[
                    475:     var reseditbrowser;
1.1004    www       476:     function openresbrowser(formname,reslink) {
1.1005    www       477:         var url = '/adm/pickresource?form='+formname+'&reslink='+reslink;
1.1003    www       478:         var title = 'Resource_Browser';
                    479:         var options = 'scrollbars=1,resizable=1,menubar=0';
1.1005    www       480:         options += ',width=700,height=500';
1.1004    www       481:         reseditbrowser = open(url,title,options,'1');
                    482:         reseditbrowser.focus();
1.1003    www       483:     }
                    484: // ]]>
                    485: </script>
1.1004    www       486: ENDRESBRW
1.1003    www       487: }
                    488: 
1.74      www       489: sub selectstudent_link {
1.1337    raeburn   490:    my ($form,$unameele,$udomele,$courseadv,$clickerid)=@_;
1.999     www       491:    my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
                    492:                       &Apache::lonhtmlcommon::entity_encode($unameele)."','".
                    493:                       &Apache::lonhtmlcommon::entity_encode($udomele)."'";
1.258     albertel  494:    if ($env{'request.course.id'}) {  
1.302     albertel  495:        if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
                    496: 	   && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
                    497: 					'/'.$env{'request.course.sec'})) {
1.111     www       498: 	   return '';
                    499:        }
1.999     www       500:        $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
1.1337    raeburn   501:        if ($courseadv eq 'only') {
                    502:            $callargs .= ",'',1,'$courseadv'";
                    503:        } elsif ($courseadv eq 'none') {
                    504:            $callargs .= ",'','','$courseadv'";
                    505:        } elsif ($courseadv eq 'condition') {
                    506:            $callargs .= ",'','','$courseadv'";
1.793     raeburn   507:        }
                    508:        return '<span class="LC_nobreak">'.
                    509:               '<a href="javascript:openstdbrowser('.$callargs.');">'.
                    510:               &mt('Select User').'</a></span>';
1.74      www       511:    }
1.258     albertel  512:    if ($env{'request.role'}=~/^(au|dc|su)/) {
1.1012    www       513:        $callargs .= ",'',1"; 
1.793     raeburn   514:        return '<span class="LC_nobreak">'.
                    515:               '<a href="javascript:openstdbrowser('.$callargs.');">'.
                    516:               &mt('Select User').'</a></span>';
1.111     www       517:    }
                    518:    return '';
1.91      www       519: }
                    520: 
1.1004    www       521: sub selectresource_link {
                    522:    my ($form,$reslink,$arg)=@_;
                    523:    
                    524:    my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
                    525:                       &Apache::lonhtmlcommon::entity_encode($reslink)."'";
                    526:    unless ($env{'request.course.id'}) { return $arg; }
                    527:    return '<span class="LC_nobreak">'.
                    528:               '<a href="javascript:openresbrowser('.$callargs.');">'.
                    529:               $arg.'</a></span>';
                    530: }
                    531: 
                    532: 
                    533: 
1.653     raeburn   534: sub authorbrowser_javascript {
                    535:     return <<"ENDAUTHORBRW";
1.776     bisitz    536: <script type="text/javascript" language="JavaScript">
1.824     bisitz    537: // <![CDATA[
1.653     raeburn   538: var stdeditbrowser;
                    539: 
                    540: function openauthorbrowser(formname,udom) {
                    541:     var url = '/adm/pickauthor?';
                    542:     url += 'form='+formname+'&roledom='+udom;
                    543:     var title = 'Author_Browser';
                    544:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    545:     options += ',width=700,height=600';
                    546:     stdeditbrowser = open(url,title,options,'1');
                    547:     stdeditbrowser.focus();
                    548: }
                    549: 
1.824     bisitz    550: // ]]>
1.653     raeburn   551: </script>
                    552: ENDAUTHORBRW
                    553: }
                    554: 
1.91      www       555: sub coursebrowser_javascript {
1.1116    raeburn   556:     my ($domainfilter,$sec_element,$formname,$role_element,$crstype,
1.1221    raeburn   557:         $credits_element,$instcode) = @_;
1.932     raeburn   558:     my $wintitle = 'Course_Browser';
1.931     raeburn   559:     if ($crstype eq 'Community') {
1.932     raeburn   560:         $wintitle = 'Community_Browser';
1.909     raeburn   561:     }
1.876     raeburn   562:     my $id_functions = &javascript_index_functions();
                    563:     my $output = '
1.776     bisitz    564: <script type="text/javascript" language="JavaScript">
1.824     bisitz    565: // <![CDATA[
1.468     raeburn   566:     var stdeditbrowser;'."\n";
1.876     raeburn   567: 
                    568:     $output .= <<"ENDSTDBRW";
1.909     raeburn   569:     function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,type,type_elem) {
1.91      www       570:         var url = '/adm/pickcourse?';
1.895     raeburn   571:         var formid = getFormIdByName(formname);
1.876     raeburn   572:         var domainfilter = getDomainFromSelectbox(formname,udom);
1.128     albertel  573:         if (domainfilter != null) {
                    574:            if (domainfilter != '') {
                    575:                url += 'domainfilter='+domainfilter+'&';
                    576: 	   }
                    577:         }
1.91      www       578:         url += 'form=' + formname + '&cnumelement='+uname+
1.187     albertel  579: 	                            '&cdomelement='+udom+
                    580:                                     '&cnameelement='+desc;
1.468     raeburn   581:         if (extra_element !=null && extra_element != '') {
1.594     raeburn   582:             if (formname == 'rolechoice' || formname == 'studentform') {
1.468     raeburn   583:                 url += '&roleelement='+extra_element;
                    584:                 if (domainfilter == null || domainfilter == '') {
                    585:                     url += '&domainfilter='+extra_element;
                    586:                 }
1.234     raeburn   587:             }
1.468     raeburn   588:             else {
                    589:                 if (formname == 'portform') {
                    590:                     url += '&setroles='+extra_element;
1.800     raeburn   591:                 } else {
                    592:                     if (formname == 'rules') {
                    593:                         url += '&fixeddom='+extra_element; 
                    594:                     }
1.468     raeburn   595:                 }
                    596:             }     
1.230     raeburn   597:         }
1.909     raeburn   598:         if (type != null && type != '') {
                    599:             url += '&type='+type;
                    600:         }
                    601:         if (type_elem != null && type_elem != '') {
                    602:             url += '&typeelement='+type_elem;
                    603:         }
1.872     raeburn   604:         if (formname == 'ccrs') {
                    605:             var ownername = document.forms[formid].ccuname.value;
                    606:             var ownerdom =  document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
1.1238    raeburn   607:             url += '&cloner='+ownername+':'+ownerdom;
                    608:             if (type == 'Course') {
                    609:                 url += '&crscode='+document.forms[formid].crscode.value;
                    610:             }
1.1221    raeburn   611:         }
                    612:         if (formname == 'requestcrs') {
                    613:             url += '&crsdom=$domainfilter&crscode=$instcode';
1.872     raeburn   614:         }
1.293     raeburn   615:         if (multflag !=null && multflag != '') {
                    616:             url += '&multiple='+multflag;
                    617:         }
1.909     raeburn   618:         var title = '$wintitle';
1.91      www       619:         var options = 'scrollbars=1,resizable=1,menubar=0';
                    620:         options += ',width=700,height=600';
                    621:         stdeditbrowser = open(url,title,options,'1');
                    622:         stdeditbrowser.focus();
                    623:     }
1.876     raeburn   624: $id_functions
                    625: ENDSTDBRW
1.1116    raeburn   626:     if (($sec_element ne '') || ($role_element ne '') || ($credits_element ne '')) {
                    627:         $output .= &setsec_javascript($sec_element,$formname,$role_element,
                    628:                                       $credits_element);
1.876     raeburn   629:     }
                    630:     $output .= '
                    631: // ]]>
                    632: </script>';
                    633:     return $output;
                    634: }
                    635: 
                    636: sub javascript_index_functions {
                    637:     return <<"ENDJS";
                    638: 
                    639: function getFormIdByName(formname) {
                    640:     for (var i=0;i<document.forms.length;i++) {
                    641:         if (document.forms[i].name == formname) {
                    642:             return i;
                    643:         }
                    644:     }
                    645:     return -1;
                    646: }
                    647: 
                    648: function getIndexByName(formid,item) {
                    649:     for (var i=0;i<document.forms[formid].elements.length;i++) {
                    650:         if (document.forms[formid].elements[i].name == item) {
                    651:             return i;
                    652:         }
                    653:     }
                    654:     return -1;
                    655: }
1.468     raeburn   656: 
1.876     raeburn   657: function getDomainFromSelectbox(formname,udom) {
                    658:     var userdom;
                    659:     var formid = getFormIdByName(formname);
                    660:     if (formid > -1) {
                    661:         var domid = getIndexByName(formid,udom);
                    662:         if (domid > -1) {
                    663:             if (document.forms[formid].elements[domid].type == 'select-one') {
                    664:                 userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
                    665:             }
                    666:             if (document.forms[formid].elements[domid].type == 'hidden') {
                    667:                 userdom=document.forms[formid].elements[domid].value;
1.468     raeburn   668:             }
                    669:         }
                    670:     }
1.876     raeburn   671:     return userdom;
                    672: }
                    673: 
                    674: ENDJS
1.468     raeburn   675: 
1.876     raeburn   676: }
                    677: 
1.1017    raeburn   678: sub javascript_array_indexof {
1.1018    raeburn   679:     return <<ENDJS;
1.1017    raeburn   680: <script type="text/javascript" language="JavaScript">
                    681: // <![CDATA[
                    682: 
                    683: if (!Array.prototype.indexOf) {
                    684:     Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
                    685:         "use strict";
                    686:         if (this === void 0 || this === null) {
                    687:             throw new TypeError();
                    688:         }
                    689:         var t = Object(this);
                    690:         var len = t.length >>> 0;
                    691:         if (len === 0) {
                    692:             return -1;
                    693:         }
                    694:         var n = 0;
                    695:         if (arguments.length > 0) {
                    696:             n = Number(arguments[1]);
1.1088    foxr      697:             if (n !== n) { // shortcut for verifying if it is NaN
1.1017    raeburn   698:                 n = 0;
                    699:             } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
                    700:                 n = (n > 0 || -1) * Math.floor(Math.abs(n));
                    701:             }
                    702:         }
                    703:         if (n >= len) {
                    704:             return -1;
                    705:         }
                    706:         var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
                    707:         for (; k < len; k++) {
                    708:             if (k in t && t[k] === searchElement) {
                    709:                 return k;
                    710:             }
                    711:         }
                    712:         return -1;
                    713:     }
                    714: }
                    715: 
                    716: // ]]>
                    717: </script>
                    718: 
                    719: ENDJS
                    720: 
                    721: }
                    722: 
1.876     raeburn   723: sub userbrowser_javascript {
                    724:     my $id_functions = &javascript_index_functions();
                    725:     return <<"ENDUSERBRW";
                    726: 
1.888     raeburn   727: function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
1.876     raeburn   728:     var url = '/adm/pickuser?';
                    729:     var userdom = getDomainFromSelectbox(formname,udom);
                    730:     if (userdom != null) {
                    731:        if (userdom != '') {
                    732:            url += 'srchdom='+userdom+'&';
                    733:        }
                    734:     }
                    735:     url += 'form=' + formname + '&unameelement='+uname+
                    736:                                 '&udomelement='+udom+
                    737:                                 '&ulastelement='+ulast+
                    738:                                 '&ufirstelement='+ufirst+
                    739:                                 '&uemailelement='+uemail+
1.881     raeburn   740:                                 '&hideudomelement='+hideudom+
                    741:                                 '&coursedom='+crsdom;
1.888     raeburn   742:     if ((caller != null) && (caller != undefined)) {
                    743:         url += '&caller='+caller;
                    744:     }
1.876     raeburn   745:     var title = 'User_Browser';
                    746:     var options = 'scrollbars=1,resizable=1,menubar=0';
                    747:     options += ',width=700,height=600';
                    748:     var stdeditbrowser = open(url,title,options,'1');
                    749:     stdeditbrowser.focus();
                    750: }
                    751: 
1.888     raeburn   752: function fix_domain (formname,udom,origdom,uname) {
1.876     raeburn   753:     var formid = getFormIdByName(formname);
                    754:     if (formid > -1) {
1.888     raeburn   755:         var unameid = getIndexByName(formid,uname);
1.876     raeburn   756:         var domid = getIndexByName(formid,udom);
                    757:         var hidedomid = getIndexByName(formid,origdom);
                    758:         if (hidedomid > -1) {
                    759:             var fixeddom = document.forms[formid].elements[hidedomid].value;
1.888     raeburn   760:             var unameval = document.forms[formid].elements[unameid].value;
                    761:             if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
                    762:                 if (domid > -1) {
                    763:                     var slct = document.forms[formid].elements[domid];
                    764:                     if (slct.type == 'select-one') {
                    765:                         var i;
                    766:                         for (i=0;i<slct.length;i++) {
                    767:                             if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
                    768:                         }
                    769:                     }
                    770:                     if (slct.type == 'hidden') {
                    771:                         slct.value = fixeddom;
1.876     raeburn   772:                     }
                    773:                 }
1.468     raeburn   774:             }
                    775:         }
                    776:     }
1.876     raeburn   777:     return;
                    778: }
                    779: 
                    780: $id_functions
                    781: ENDUSERBRW
1.468     raeburn   782: }
                    783: 
                    784: sub setsec_javascript {
1.1116    raeburn   785:     my ($sec_element,$formname,$role_element,$credits_element) = @_;
1.905     raeburn   786:     my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
                    787:         $communityrolestr);
                    788:     if ($role_element ne '') {
                    789:         my @allroles = ('st','ta','ep','in','ad');
                    790:         foreach my $crstype ('Course','Community') {
                    791:             if ($crstype eq 'Community') {
                    792:                 foreach my $role (@allroles) {
                    793:                     push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
                    794:                 }
                    795:                 push(@communityrolenames,&Apache::lonnet::plaintext('co'));
                    796:             } else {
                    797:                 foreach my $role (@allroles) {
                    798:                     push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
                    799:                 }
                    800:                 push(@courserolenames,&Apache::lonnet::plaintext('cc'));
                    801:             }
                    802:         }
                    803:         $rolestr = '"'.join('","',@allroles).'"';
                    804:         $courserolestr = '"'.join('","',@courserolenames).'"';
                    805:         $communityrolestr = '"'.join('","',@communityrolenames).'"';
                    806:     }
1.468     raeburn   807:     my $setsections = qq|
                    808: function setSect(sectionlist) {
1.629     raeburn   809:     var sectionsArray = new Array();
                    810:     if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
                    811:         sectionsArray = sectionlist.split(",");
                    812:     }
1.468     raeburn   813:     var numSections = sectionsArray.length;
                    814:     document.$formname.$sec_element.length = 0;
                    815:     if (numSections == 0) {
                    816:         document.$formname.$sec_element.multiple=false;
                    817:         document.$formname.$sec_element.size=1;
                    818:         document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
                    819:     } else {
                    820:         if (numSections == 1) {
                    821:             document.$formname.$sec_element.multiple=false;
                    822:             document.$formname.$sec_element.size=1;
                    823:             document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
                    824:             document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
                    825:             document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
                    826:         } else {
                    827:             for (var i=0; i<numSections; i++) {
                    828:                 document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
                    829:             }
                    830:             document.$formname.$sec_element.multiple=true
                    831:             if (numSections < 3) {
                    832:                 document.$formname.$sec_element.size=numSections;
                    833:             } else {
                    834:                 document.$formname.$sec_element.size=3;
                    835:             }
                    836:             document.$formname.$sec_element.options[0].selected = false
                    837:         }
                    838:     }
1.91      www       839: }
1.905     raeburn   840: 
                    841: function setRole(crstype) {
1.468     raeburn   842: |;
1.905     raeburn   843:     if ($role_element eq '') {
                    844:         $setsections .= '    return;
                    845: }
                    846: ';
                    847:     } else {
                    848:         $setsections .= qq|
                    849:     var elementLength = document.$formname.$role_element.length;
                    850:     var allroles = Array($rolestr);
                    851:     var courserolenames = Array($courserolestr);
                    852:     var communityrolenames = Array($communityrolestr);
                    853:     if (elementLength != undefined) {
                    854:         if (document.$formname.$role_element.options[5].value == 'cc') {
                    855:             if (crstype == 'Course') {
                    856:                 return;
                    857:             } else {
                    858:                 allroles[5] = 'co';
                    859:                 for (var i=0; i<6; i++) {
                    860:                     document.$formname.$role_element.options[i].value = allroles[i];
                    861:                     document.$formname.$role_element.options[i].text = communityrolenames[i];
                    862:                 }
                    863:             }
                    864:         } else {
                    865:             if (crstype == 'Community') {
                    866:                 return;
                    867:             } else {
                    868:                 allroles[5] = 'cc';
                    869:                 for (var i=0; i<6; i++) {
                    870:                     document.$formname.$role_element.options[i].value = allroles[i];
                    871:                     document.$formname.$role_element.options[i].text = courserolenames[i];
                    872:                 }
                    873:             }
                    874:         }
                    875:     }
                    876:     return;
                    877: }
                    878: |;
                    879:     }
1.1116    raeburn   880:     if ($credits_element) {
                    881:         $setsections .= qq|
                    882: function setCredits(defaultcredits) {
                    883:     document.$formname.$credits_element.value = defaultcredits;
                    884:     return;
                    885: }
                    886: |;
                    887:     }
1.468     raeburn   888:     return $setsections;
                    889: }
                    890: 
1.91      www       891: sub selectcourse_link {
1.909     raeburn   892:    my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype,
                    893:        $typeelement) = @_;
                    894:    my $type = $selecttype;
1.871     raeburn   895:    my $linktext = &mt('Select Course');
                    896:    if ($selecttype eq 'Community') {
1.909     raeburn   897:        $linktext = &mt('Select Community');
1.1239    raeburn   898:    } elsif ($selecttype eq 'Placement') {
                    899:        $linktext = &mt('Select Placement Test'); 
1.906     raeburn   900:    } elsif ($selecttype eq 'Course/Community') {
                    901:        $linktext = &mt('Select Course/Community');
1.909     raeburn   902:        $type = '';
1.1019    raeburn   903:    } elsif ($selecttype eq 'Select') {
                    904:        $linktext = &mt('Select');
                    905:        $type = '';
1.871     raeburn   906:    }
1.787     bisitz    907:    return '<span class="LC_nobreak">'
                    908:          ."<a href='"
                    909:          .'javascript:opencrsbrowser("'.$form.'","'.$unameele
                    910:          .'","'.$udomele.'","'.$desc.'","'.$extra_element
1.909     raeburn   911:          .'","'.$multflag.'","'.$type.'","'.$typeelement.'");'
1.871     raeburn   912:          ."'>".$linktext.'</a>'
1.787     bisitz    913:          .'</span>';
1.74      www       914: }
1.42      matthew   915: 
1.653     raeburn   916: sub selectauthor_link {
                    917:    my ($form,$udom)=@_;
                    918:    return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
                    919:           &mt('Select Author').'</a>';
                    920: }
                    921: 
1.876     raeburn   922: sub selectuser_link {
1.881     raeburn   923:     my ($form,$unameelem,$domelem,$lastelem,$firstelem,$emailelem,$hdomelem,
1.888     raeburn   924:         $coursedom,$linktext,$caller) = @_;
1.876     raeburn   925:     return '<a href="javascript:openuserbrowser('."'$form','$unameelem','$domelem',".
1.888     raeburn   926:            "'$lastelem','$firstelem','$emailelem','$hdomelem','$coursedom','$caller'".
1.881     raeburn   927:            ');">'.$linktext.'</a>';
1.876     raeburn   928: }
                    929: 
1.273     raeburn   930: sub check_uncheck_jscript {
                    931:     my $jscript = <<"ENDSCRT";
                    932: function checkAll(field) {
                    933:     if (field.length > 0) {
                    934:         for (i = 0; i < field.length; i++) {
1.1093    raeburn   935:             if (!field[i].disabled) { 
                    936:                 field[i].checked = true;
                    937:             }
1.273     raeburn   938:         }
                    939:     } else {
1.1093    raeburn   940:         if (!field.disabled) { 
                    941:             field.checked = true;
                    942:         }
1.273     raeburn   943:     }
                    944: }
                    945:  
                    946: function uncheckAll(field) {
                    947:     if (field.length > 0) {
                    948:         for (i = 0; i < field.length; i++) {
                    949:             field[i].checked = false ;
1.543     albertel  950:         }
                    951:     } else {
1.273     raeburn   952:         field.checked = false ;
                    953:     }
                    954: }
                    955: ENDSCRT
                    956:     return $jscript;
                    957: }
                    958: 
1.656     www       959: sub select_timezone {
1.1256    raeburn   960:    my ($name,$selected,$onchange,$includeempty,$disabled)=@_;
                    961:    my $output='<select name="'.$name.'" '.$onchange.$disabled.'>'."\n";
1.659     raeburn   962:    if ($includeempty) {
                    963:        $output .= '<option value=""';
                    964:        if (($selected eq '') || ($selected eq 'local')) {
                    965:            $output .= ' selected="selected" ';
                    966:        }
                    967:        $output .= '> </option>';
                    968:    }
1.657     raeburn   969:    my @timezones = DateTime::TimeZone->all_names;
                    970:    foreach my $tzone (@timezones) {
                    971:        $output.= '<option value="'.$tzone.'"';
                    972:        if ($tzone eq $selected) {
                    973:            $output.=' selected="selected"';
                    974:        }
                    975:        $output.=">$tzone</option>\n";
1.656     www       976:    }
                    977:    $output.="</select>";
                    978:    return $output;
                    979: }
1.273     raeburn   980: 
1.687     raeburn   981: sub select_datelocale {
1.1256    raeburn   982:     my ($name,$selected,$onchange,$includeempty,$disabled)=@_;
                    983:     my $output='<select name="'.$name.'" '.$onchange.$disabled.'>'."\n";
1.687     raeburn   984:     if ($includeempty) {
                    985:         $output .= '<option value=""';
                    986:         if ($selected eq '') {
                    987:             $output .= ' selected="selected" ';
                    988:         }
                    989:         $output .= '> </option>';
                    990:     }
1.1241    raeburn   991:     my @languages = &Apache::lonlocal::preferred_languages();
1.687     raeburn   992:     my (@possibles,%locale_names);
1.1241    raeburn   993:     my @locales = DateTime::Locale->ids();
                    994:     foreach my $id (@locales) {
                    995:         if ($id ne '') {
                    996:             my ($en_terr,$native_terr);
                    997:             my $loc = DateTime::Locale->load($id);
                    998:             if (ref($loc)) {
                    999:                 $en_terr = $loc->name();
                   1000:                 $native_terr = $loc->native_name();
1.687     raeburn  1001:                 if (grep(/^en$/,@languages) || !@languages) {
                   1002:                     if ($en_terr ne '') {
                   1003:                         $locale_names{$id} = '('.$en_terr.')';
                   1004:                     } elsif ($native_terr ne '') {
                   1005:                         $locale_names{$id} = $native_terr;
                   1006:                     }
                   1007:                 } else {
                   1008:                     if ($native_terr ne '') {
                   1009:                         $locale_names{$id} = $native_terr.' ';
                   1010:                     } elsif ($en_terr ne '') {
                   1011:                         $locale_names{$id} = '('.$en_terr.')';
                   1012:                     }
                   1013:                 }
1.1220    raeburn  1014:                 $locale_names{$id} = Encode::encode('UTF-8',$locale_names{$id});
1.1241    raeburn  1015:                 push(@possibles,$id);
                   1016:             } 
1.687     raeburn  1017:         }
                   1018:     }
                   1019:     foreach my $item (sort(@possibles)) {
                   1020:         $output.= '<option value="'.$item.'"';
                   1021:         if ($item eq $selected) {
                   1022:             $output.=' selected="selected"';
                   1023:         }
                   1024:         $output.=">$item";
                   1025:         if ($locale_names{$item} ne '') {
1.1220    raeburn  1026:             $output.='  '.$locale_names{$item};
1.687     raeburn  1027:         }
                   1028:         $output.="</option>\n";
                   1029:     }
                   1030:     $output.="</select>";
                   1031:     return $output;
                   1032: }
                   1033: 
1.792     raeburn  1034: sub select_language {
1.1256    raeburn  1035:     my ($name,$selected,$includeempty,$noedit) = @_;
1.792     raeburn  1036:     my %langchoices;
                   1037:     if ($includeempty) {
1.1117    raeburn  1038:         %langchoices = ('' => 'No language preference');
1.792     raeburn  1039:     }
                   1040:     foreach my $id (&languageids()) {
                   1041:         my $code = &supportedlanguagecode($id);
                   1042:         if ($code) {
                   1043:             $langchoices{$code} = &plainlanguagedescription($id);
                   1044:         }
                   1045:     }
1.1117    raeburn  1046:     %langchoices = &Apache::lonlocal::texthash(%langchoices);
1.1256    raeburn  1047:     return &select_form($selected,$name,\%langchoices,undef,$noedit);
1.792     raeburn  1048: }
                   1049: 
1.42      matthew  1050: =pod
1.36      matthew  1051: 
1.1088    foxr     1052: 
                   1053: =item * &list_languages()
                   1054: 
                   1055: Returns an array reference that is suitable for use in language prompters.
                   1056: Each array element is itself a two element array.  The first element
                   1057: is the language code.  The second element a descsriptiuon of the 
                   1058: language itself.  This is suitable for use in e.g.
                   1059: &Apache::edit::select_arg (once dereferenced that is).
                   1060: 
                   1061: =cut 
                   1062: 
                   1063: sub list_languages {
                   1064:     my @lang_choices;
                   1065: 
                   1066:     foreach my $id (&languageids()) {
                   1067: 	my $code = &supportedlanguagecode($id);
                   1068: 	if ($code) {
                   1069: 	    my $selector    = $supported_codes{$id};
                   1070: 	    my $description = &plainlanguagedescription($id);
1.1263    raeburn  1071: 	    push(@lang_choices, [$selector, $description]);
1.1088    foxr     1072: 	}
                   1073:     }
                   1074:     return \@lang_choices;
                   1075: }
                   1076: 
                   1077: =pod
                   1078: 
1.648     raeburn  1079: =item * &linked_select_forms(...)
1.36      matthew  1080: 
                   1081: linked_select_forms returns a string containing a <script></script> block
                   1082: and html for two <select> menus.  The select menus will be linked in that
                   1083: changing the value of the first menu will result in new values being placed
                   1084: in the second menu.  The values in the select menu will appear in alphabetical
1.609     raeburn  1085: order unless a defined order is provided.
1.36      matthew  1086: 
                   1087: linked_select_forms takes the following ordered inputs:
                   1088: 
                   1089: =over 4
                   1090: 
1.112     bowersj2 1091: =item * $formname, the name of the <form> tag
1.36      matthew  1092: 
1.112     bowersj2 1093: =item * $middletext, the text which appears between the <select> tags
1.36      matthew  1094: 
1.112     bowersj2 1095: =item * $firstdefault, the default value for the first menu
1.36      matthew  1096: 
1.112     bowersj2 1097: =item * $firstselectname, the name of the first <select> tag
1.36      matthew  1098: 
1.112     bowersj2 1099: =item * $secondselectname, the name of the second <select> tag
1.36      matthew  1100: 
1.112     bowersj2 1101: =item * $hashref, a reference to a hash containing the data for the menus.
1.36      matthew  1102: 
1.609     raeburn  1103: =item * $menuorder, the order of values in the first menu
                   1104: 
1.1115    raeburn  1105: =item * $onchangefirst, additional javascript call to execute for an onchange
                   1106:         event for the first <select> tag
                   1107: 
                   1108: =item * $onchangesecond, additional javascript call to execute for an onchange
                   1109:         event for the second <select> tag
                   1110: 
1.1245    raeburn  1111: =item * $suffix, to differentiate separate uses of select2data javascript
                   1112:         objects in a page.
                   1113: 
1.41      ng       1114: =back 
                   1115: 
1.36      matthew  1116: Below is an example of such a hash.  Only the 'text', 'default', and 
                   1117: 'select2' keys must appear as stated.  keys(%menu) are the possible 
                   1118: values for the first select menu.  The text that coincides with the 
1.41      ng       1119: first menu value is given in $menu{$choice1}->{'text'}.  The values 
1.36      matthew  1120: and text for the second menu are given in the hash pointed to by 
                   1121: $menu{$choice1}->{'select2'}.  
                   1122: 
1.112     bowersj2 1123:  my %menu = ( A1 => { text =>"Choice A1" ,
                   1124:                        default => "B3",
                   1125:                        select2 => { 
                   1126:                            B1 => "Choice B1",
                   1127:                            B2 => "Choice B2",
                   1128:                            B3 => "Choice B3",
                   1129:                            B4 => "Choice B4"
1.609     raeburn  1130:                            },
                   1131:                        order => ['B4','B3','B1','B2'],
1.112     bowersj2 1132:                    },
                   1133:                A2 => { text =>"Choice A2" ,
                   1134:                        default => "C2",
                   1135:                        select2 => { 
                   1136:                            C1 => "Choice C1",
                   1137:                            C2 => "Choice C2",
                   1138:                            C3 => "Choice C3"
1.609     raeburn  1139:                            },
                   1140:                        order => ['C2','C1','C3'],
1.112     bowersj2 1141:                    },
                   1142:                A3 => { text =>"Choice A3" ,
                   1143:                        default => "D6",
                   1144:                        select2 => { 
                   1145:                            D1 => "Choice D1",
                   1146:                            D2 => "Choice D2",
                   1147:                            D3 => "Choice D3",
                   1148:                            D4 => "Choice D4",
                   1149:                            D5 => "Choice D5",
                   1150:                            D6 => "Choice D6",
                   1151:                            D7 => "Choice D7"
1.609     raeburn  1152:                            },
                   1153:                        order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112     bowersj2 1154:                    }
                   1155:                );
1.36      matthew  1156: 
                   1157: =cut
                   1158: 
                   1159: sub linked_select_forms {
                   1160:     my ($formname,
                   1161:         $middletext,
                   1162:         $firstdefault,
                   1163:         $firstselectname,
                   1164:         $secondselectname, 
1.609     raeburn  1165:         $hashref,
                   1166:         $menuorder,
1.1115    raeburn  1167:         $onchangefirst,
1.1245    raeburn  1168:         $onchangesecond,
                   1169:         $suffix
1.36      matthew  1170:         ) = @_;
                   1171:     my $second = "document.$formname.$secondselectname";
                   1172:     my $first = "document.$formname.$firstselectname";
                   1173:     # output the javascript to do the changing
                   1174:     my $result = '';
1.776     bisitz   1175:     $result.='<script type="text/javascript" language="JavaScript">'."\n";
1.824     bisitz   1176:     $result.="// <![CDATA[\n";
1.1245    raeburn  1177:     $result.="var select2data${suffix} = new Object();\n";
1.36      matthew  1178:     $" = '","';
                   1179:     my $debug = '';
                   1180:     foreach my $s1 (sort(keys(%$hashref))) {
1.1245    raeburn  1181:         $result.="select2data${suffix}['d_$s1'] = new Object();\n";        
                   1182:         $result.="select2data${suffix}['d_$s1'].def = new String('".
1.36      matthew  1183:             $hashref->{$s1}->{'default'}."');\n";
1.1245    raeburn  1184:         $result.="select2data${suffix}['d_$s1'].values = new Array(";
1.36      matthew  1185:         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609     raeburn  1186:         if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
                   1187:             @s2values = @{$hashref->{$s1}->{'order'}};
                   1188:         }
1.36      matthew  1189:         $result.="\"@s2values\");\n";
1.1245    raeburn  1190:         $result.="select2data${suffix}['d_$s1'].texts = new Array(";        
1.36      matthew  1191:         my @s2texts;
                   1192:         foreach my $value (@s2values) {
1.1263    raeburn  1193:             push(@s2texts, $hashref->{$s1}->{'select2'}->{$value});
1.36      matthew  1194:         }
                   1195:         $result.="\"@s2texts\");\n";
                   1196:     }
                   1197:     $"=' ';
                   1198:     $result.= <<"END";
                   1199: 
1.1245    raeburn  1200: function select1${suffix}_changed() {
1.36      matthew  1201:     // Determine new choice
1.1245    raeburn  1202:     var newvalue = "d_" + $first.options[$first.selectedIndex].value;
1.36      matthew  1203:     // update select2
1.1245    raeburn  1204:     var values     = select2data${suffix}[newvalue].values;
                   1205:     var texts      = select2data${suffix}[newvalue].texts;
                   1206:     var select2def = select2data${suffix}[newvalue].def;
1.36      matthew  1207:     var i;
                   1208:     // out with the old
1.1245    raeburn  1209:     $second.options.length = 0;
                   1210:     // in with the new
1.36      matthew  1211:     for (i=0;i<values.length; i++) {
                   1212:         $second.options[i] = new Option(values[i]);
1.143     matthew  1213:         $second.options[i].value = values[i];
1.36      matthew  1214:         $second.options[i].text = texts[i];
                   1215:         if (values[i] == select2def) {
                   1216:             $second.options[i].selected = true;
                   1217:         }
                   1218:     }
                   1219: }
1.824     bisitz   1220: // ]]>
1.36      matthew  1221: </script>
                   1222: END
                   1223:     # output the initial values for the selection lists
1.1245    raeburn  1224:     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1${suffix}_changed();$onchangefirst\">\n";
1.609     raeburn  1225:     my @order = sort(keys(%{$hashref}));
                   1226:     if (ref($menuorder) eq 'ARRAY') {
                   1227:         @order = @{$menuorder};
                   1228:     }
                   1229:     foreach my $value (@order) {
1.36      matthew  1230:         $result.="    <option value=\"$value\" ";
1.253     albertel 1231:         $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119     www      1232:         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36      matthew  1233:     }
                   1234:     $result .= "</select>\n";
                   1235:     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
                   1236:     $result .= $middletext;
1.1115    raeburn  1237:     $result .= "<select size=\"1\" name=\"$secondselectname\"";
                   1238:     if ($onchangesecond) {
                   1239:         $result .= ' onchange="'.$onchangesecond.'"';
                   1240:     }
                   1241:     $result .= ">\n";
1.36      matthew  1242:     my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609     raeburn  1243:     
                   1244:     my @secondorder = sort(keys(%select2));
                   1245:     if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
                   1246:         @secondorder = @{$hashref->{$firstdefault}->{'order'}};
                   1247:     }
                   1248:     foreach my $value (@secondorder) {
1.36      matthew  1249:         $result.="    <option value=\"$value\" ";        
1.253     albertel 1250:         $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119     www      1251:         $result.=">".&mt($select2{$value})."</option>\n";
1.36      matthew  1252:     }
                   1253:     $result .= "</select>\n";
                   1254:     #    return $debug;
                   1255:     return $result;
                   1256: }   #  end of sub linked_select_forms {
                   1257: 
1.45      matthew  1258: =pod
1.44      bowersj2 1259: 
1.973     raeburn  1260: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid)
1.44      bowersj2 1261: 
1.112     bowersj2 1262: Returns a string corresponding to an HTML link to the given help
                   1263: $topic, where $topic corresponds to the name of a .tex file in
                   1264: /home/httpd/html/adm/help/tex, with underscores replaced by
                   1265: spaces. 
                   1266: 
                   1267: $text will optionally be linked to the same topic, allowing you to
                   1268: link text in addition to the graphic. If you do not want to link
                   1269: text, but wish to specify one of the later parameters, pass an
                   1270: empty string. 
                   1271: 
                   1272: $stayOnPage is a value that will be interpreted as a boolean. If true,
                   1273: the link will not open a new window. If false, the link will open
                   1274: a new window using Javascript. (Default is false.) 
                   1275: 
                   1276: $width and $height are optional numerical parameters that will
                   1277: override the width and height of the popped up window, which may
1.973     raeburn  1278: be useful for certain help topics with big pictures included.
                   1279: 
                   1280: $imgid is the id of the img tag used for the help icon. This may be
                   1281: used in a javascript call to switch the image src.  See 
                   1282: lonhtmlcommon::htmlareaselectactive() for an example.
1.44      bowersj2 1283: 
                   1284: =cut
                   1285: 
                   1286: sub help_open_topic {
1.973     raeburn  1287:     my ($topic, $text, $stayOnPage, $width, $height, $imgid) = @_;
1.48      bowersj2 1288:     $text = "" if (not defined $text);
1.44      bowersj2 1289:     $stayOnPage = 0 if (not defined $stayOnPage);
1.1033    www      1290:     $width = 500 if (not defined $width);
1.44      bowersj2 1291:     $height = 400 if (not defined $height);
                   1292:     my $filename = $topic;
                   1293:     $filename =~ s/ /_/g;
                   1294: 
1.48      bowersj2 1295:     my $template = "";
                   1296:     my $link;
1.572     banghart 1297:     
1.159     www      1298:     $topic=~s/\W/\_/g;
1.44      bowersj2 1299: 
1.572     banghart 1300:     if (!$stayOnPage) {
1.1033    www      1301: 	$link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
1.1037    www      1302:     } elsif ($stayOnPage eq 'popup') {
                   1303:         $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 1304:     } else {
1.48      bowersj2 1305: 	$link = "/adm/help/${filename}.hlp";
                   1306:     }
                   1307: 
                   1308:     # Add the text
1.1314    raeburn  1309:     my $target = ' target="_top"';
                   1310:     if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
                   1311:         $target = '';
                   1312:     }
1.1378  ! raeburn  1313:     if (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'})) {
        !          1314:         $target = ' target="'.$env{'request.deeplink.target'}.'"';
        !          1315:     }
1.755     neumanie 1316:     if ($text ne "") {	
1.763     bisitz   1317: 	$template.='<span class="LC_help_open_topic">'
1.1314    raeburn  1318:                   .'<a'.$target.' href="'.$link.'">'
1.763     bisitz   1319:                   .$text.'</a>';
1.48      bowersj2 1320:     }
                   1321: 
1.763     bisitz   1322:     # (Always) Add the graphic
1.179     matthew  1323:     my $title = &mt('Online Help');
1.667     raeburn  1324:     my $helpicon=&lonhttpdurl("/adm/help/help.png");
1.973     raeburn  1325:     if ($imgid ne '') {
                   1326:         $imgid = ' id="'.$imgid.'"';
                   1327:     }
1.1314    raeburn  1328:     $template.=' <a'.$target.' href="'.$link.'" title="'.$title.'">'
1.763     bisitz   1329:               .'<img src="'.$helpicon.'" border="0"'
                   1330:               .' alt="'.&mt('Help: [_1]',$topic).'"'
1.973     raeburn  1331:               .' title="'.$title.'" style="vertical-align:middle;"'.$imgid 
1.763     bisitz   1332:               .' /></a>';
                   1333:     if ($text ne "") {	
                   1334:         $template.='</span>';
                   1335:     }
1.44      bowersj2 1336:     return $template;
                   1337: 
1.106     bowersj2 1338: }
                   1339: 
                   1340: # This is a quicky function for Latex cheatsheet editing, since it 
                   1341: # appears in at least four places
                   1342: sub helpLatexCheatsheet {
1.1037    www      1343:     my ($topic,$text,$not_author,$stayOnPage) = @_;
1.732     raeburn  1344:     my $out;
1.106     bowersj2 1345:     my $addOther = '';
1.732     raeburn  1346:     if ($topic) {
1.1037    www      1347: 	$addOther = '<span>'.&help_open_topic($topic,&mt($text),$stayOnPage, undef, 600).'</span> ';
1.763     bisitz   1348:     }
                   1349:     $out = '<span>' # Start cheatsheet
                   1350: 	  .$addOther
                   1351:           .'<span>'
1.1037    www      1352: 	  .&help_open_topic('Greek_Symbols',&mt('Greek Symbols'),$stayOnPage,undef,600)
1.763     bisitz   1353: 	  .'</span> <span>'
1.1037    www      1354: 	  .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
1.763     bisitz   1355: 	  .'</span>';
1.732     raeburn  1356:     unless ($not_author) {
1.1186    kruse    1357:         $out .= '<span>'
                   1358:                .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
                   1359:                .'</span> <span>'
                   1360:                .&help_open_topic('Authoring_Multilingual_Problems',&mt('How to create problems in different languages'),$stayOnPage,undef,600)
1.763     bisitz   1361: 	       .'</span>';
1.732     raeburn  1362:     }
1.763     bisitz   1363:     $out .= '</span>'; # End cheatsheet
1.732     raeburn  1364:     return $out;
1.172     www      1365: }
                   1366: 
1.430     albertel 1367: sub general_help {
                   1368:     my $helptopic='Student_Intro';
                   1369:     if ($env{'request.role'}=~/^(ca|au)/) {
                   1370: 	$helptopic='Authoring_Intro';
1.907     raeburn  1371:     } elsif ($env{'request.role'}=~/^(cc|co)/) {
1.430     albertel 1372: 	$helptopic='Course_Coordination_Intro';
1.672     raeburn  1373:     } elsif ($env{'request.role'}=~/^dc/) {
                   1374:         $helptopic='Domain_Coordination_Intro';
1.430     albertel 1375:     }
                   1376:     return $helptopic;
                   1377: }
                   1378: 
                   1379: sub update_help_link {
                   1380:     my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
                   1381:     my $origurl = $ENV{'REQUEST_URI'};
                   1382:     $origurl=~s|^/~|/priv/|;
                   1383:     my $timestamp = time;
                   1384:     foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
                   1385:         $$datum = &escape($$datum);
                   1386:     }
                   1387: 
                   1388:     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";
                   1389:     my $output .= <<"ENDOUTPUT";
                   1390: <script type="text/javascript">
1.824     bisitz   1391: // <![CDATA[
1.430     albertel 1392: banner_link = '$banner_link';
1.824     bisitz   1393: // ]]>
1.430     albertel 1394: </script>
                   1395: ENDOUTPUT
                   1396:     return $output;
                   1397: }
                   1398: 
                   1399: # now just updates the help link and generates a blue icon
1.193     raeburn  1400: sub help_open_menu {
1.430     albertel 1401:     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text) 
1.552     banghart 1402: 	= @_;    
1.949     droeschl 1403:     $stayOnPage = 1;
1.430     albertel 1404:     my $output;
                   1405:     if ($component_help) {
                   1406: 	if (!$text) {
                   1407: 	    $output=&help_open_topic($component_help,undef,$stayOnPage,
                   1408: 				       $width,$height);
                   1409: 	} else {
                   1410: 	    my $help_text;
                   1411: 	    $help_text=&unescape($topic);
                   1412: 	    $output='<table><tr><td>'.
                   1413: 		&help_open_topic($component_help,$help_text,$stayOnPage,
                   1414: 				 $width,$height).'</td></tr></table>';
                   1415: 	}
                   1416:     }
                   1417:     my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
                   1418:     return $output.$banner_link;
                   1419: }
                   1420: 
                   1421: sub top_nav_help {
1.1369    raeburn  1422:     my ($text,$linkattr) = @_;
1.436     albertel 1423:     $text = &mt($text);
1.949     droeschl 1424:     my $stay_on_page = 1;
                   1425: 
1.1168    raeburn  1426:     my ($link,$banner_link);
                   1427:     unless ($env{'request.noversionuri'} =~ m{^/adm/helpmenu}) {
                   1428:         $link = ($stay_on_page) ? "javascript:helpMenu('display')"
                   1429: 	                         : "javascript:helpMenu('open')";
                   1430:         $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
                   1431:     }
1.201     raeburn  1432:     my $title = &mt('Get help');
1.1168    raeburn  1433:     if ($link) {
                   1434:         return <<"END";
1.436     albertel 1435: $banner_link
1.1369    raeburn  1436: <a href="$link" title="$title" $linkattr>$text</a>
1.436     albertel 1437: END
1.1168    raeburn  1438:     } else {
                   1439:         return '&nbsp;'.$text.'&nbsp;';
                   1440:     }
1.436     albertel 1441: }
                   1442: 
                   1443: sub help_menu_js {
1.1154    raeburn  1444:     my ($httphost) = @_;
1.949     droeschl 1445:     my $stayOnPage = 1;
1.436     albertel 1446:     my $width = 620;
                   1447:     my $height = 600;
1.430     albertel 1448:     my $helptopic=&general_help();
1.1154    raeburn  1449:     my $details_link = $httphost.'/adm/help/'.$helptopic.'.hlp';
1.261     albertel 1450:     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331     albertel 1451:     my $start_page =
                   1452:         &Apache::loncommon::start_page('Help Menu', undef,
                   1453: 				       {'frameset'    => 1,
                   1454: 					'js_ready'    => 1,
1.1154    raeburn  1455:                                         'use_absolute' => $httphost,
1.331     albertel 1456: 					'add_entries' => {
1.1168    raeburn  1457: 					    'border' => '0', 
1.579     raeburn  1458: 					    'rows'   => "110,*",},});
1.331     albertel 1459:     my $end_page =
                   1460:         &Apache::loncommon::end_page({'frameset' => 1,
                   1461: 				      'js_ready' => 1,});
                   1462: 
1.436     albertel 1463:     my $template .= <<"ENDTEMPLATE";
                   1464: <script type="text/javascript">
1.877     bisitz   1465: // <![CDATA[
1.253     albertel 1466: // <!-- BEGIN LON-CAPA Internal
1.430     albertel 1467: var banner_link = '';
1.243     raeburn  1468: function helpMenu(target) {
                   1469:     var caller = this;
                   1470:     if (target == 'open') {
                   1471:         var newWindow = null;
                   1472:         try {
1.262     albertel 1473:             newWindow =  window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243     raeburn  1474:         }
                   1475:         catch(error) {
                   1476:             writeHelp(caller);
                   1477:             return;
                   1478:         }
                   1479:         if (newWindow) {
                   1480:             caller = newWindow;
                   1481:         }
1.193     raeburn  1482:     }
1.243     raeburn  1483:     writeHelp(caller);
                   1484:     return;
                   1485: }
                   1486: function writeHelp(caller) {
1.1168    raeburn  1487:     caller.document.writeln('$start_page\\n<frame name="bannerframe" src="'+banner_link+'" marginwidth="0" marginheight="0" frameborder="0">\\n');
                   1488:     caller.document.writeln('<frame name="bodyframe" src="$details_link" marginwidth="0" marginheight="0" frameborder="0">\\n$end_page');
                   1489:     caller.document.close();
                   1490:     caller.focus();
1.193     raeburn  1491: }
1.877     bisitz   1492: // END LON-CAPA Internal -->
1.253     albertel 1493: // ]]>
1.436     albertel 1494: </script>
1.193     raeburn  1495: ENDTEMPLATE
                   1496:     return $template;
                   1497: }
                   1498: 
1.172     www      1499: sub help_open_bug {
                   1500:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1501:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1502:     unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
                   1503:     $text = "" if (not defined $text);
                   1504: 	$stayOnPage=1;
1.184     albertel 1505:     $width = 600 if (not defined $width);
                   1506:     $height = 600 if (not defined $height);
1.172     www      1507: 
                   1508:     $topic=~s/\W+/\+/g;
                   1509:     my $link='';
                   1510:     my $template='';
1.379     albertel 1511:     my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&amp;bug_file_loc='.
                   1512: 	&escape($ENV{'REQUEST_URI'}).'&amp;component='.$topic;
1.172     www      1513:     if (!$stayOnPage)
                   1514:     {
                   1515: 	$link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1516:     }
                   1517:     else
                   1518:     {
                   1519: 	$link = $url;
                   1520:     }
1.1314    raeburn  1521: 
                   1522:     my $target = ' target="_top"';
                   1523:     if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
                   1524:         $target = '';
                   1525:     }
1.1378  ! raeburn  1526:     if (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'})) {
        !          1527:         $target = ' target="'.$env{'request.deeplink.target'}.'"';
        !          1528:     }
1.172     www      1529:     # Add the text
                   1530:     if ($text ne "")
                   1531:     {
                   1532: 	$template .= 
                   1533:   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.1314    raeburn  1534:   "<td bgcolor='#FF5555'><a".$target." href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
1.172     www      1535:     }
                   1536: 
                   1537:     # Add the graphic
1.179     matthew  1538:     my $title = &mt('Report a Bug');
1.215     albertel 1539:     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172     www      1540:     $template .= <<"ENDTEMPLATE";
1.1314    raeburn  1541:  <a$target href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172     www      1542: ENDTEMPLATE
                   1543:     if ($text ne '') { $template.='</td></tr></table>' };
                   1544:     return $template;
                   1545: 
                   1546: }
                   1547: 
                   1548: sub help_open_faq {
                   1549:     my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258     albertel 1550:     unless ($env{'user.adv'}) { return ''; }
1.172     www      1551:     unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
                   1552:     $text = "" if (not defined $text);
                   1553: 	$stayOnPage=1;
                   1554:     $width = 350 if (not defined $width);
                   1555:     $height = 400 if (not defined $height);
                   1556: 
                   1557:     $topic=~s/\W+/\+/g;
                   1558:     my $link='';
                   1559:     my $template='';
                   1560:     my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
                   1561:     if (!$stayOnPage)
                   1562:     {
                   1563: 	$link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
                   1564:     }
                   1565:     else
                   1566:     {
                   1567: 	$link = $url;
                   1568:     }
                   1569: 
                   1570:     # Add the text
                   1571:     if ($text ne "")
                   1572:     {
                   1573: 	$template .= 
1.173     www      1574:   "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705     tempelho 1575:   "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>";
1.172     www      1576:     }
                   1577: 
                   1578:     # Add the graphic
1.179     matthew  1579:     my $title = &mt('View the FAQ');
1.215     albertel 1580:     my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172     www      1581:     $template .= <<"ENDTEMPLATE";
1.436     albertel 1582:  <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172     www      1583: ENDTEMPLATE
                   1584:     if ($text ne '') { $template.='</td></tr></table>' };
                   1585:     return $template;
                   1586: 
1.44      bowersj2 1587: }
1.37      matthew  1588: 
1.180     matthew  1589: ###############################################################
                   1590: ###############################################################
                   1591: 
1.45      matthew  1592: =pod
                   1593: 
1.648     raeburn  1594: =item * &change_content_javascript():
1.256     matthew  1595: 
                   1596: This and the next function allow you to create small sections of an
                   1597: otherwise static HTML page that you can update on the fly with
                   1598: Javascript, even in Netscape 4.
                   1599: 
                   1600: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
                   1601: must be written to the HTML page once. It will prove the Javascript
                   1602: function "change(name, content)". Calling the change function with the
                   1603: name of the section 
                   1604: you want to update, matching the name passed to C<changable_area>, and
                   1605: the new content you want to put in there, will put the content into
                   1606: that area.
                   1607: 
                   1608: B<Note>: Netscape 4 only reserves enough space for the changable area
                   1609: to contain room for the original contents. You need to "make space"
                   1610: for whatever changes you wish to make, and be B<sure> to check your
                   1611: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
                   1612: it's adequate for updating a one-line status display, but little more.
                   1613: This script will set the space to 100% width, so you only need to
                   1614: worry about height in Netscape 4.
                   1615: 
                   1616: Modern browsers are much less limiting, and if you can commit to the
                   1617: user not using Netscape 4, this feature may be used freely with
                   1618: pretty much any HTML.
                   1619: 
                   1620: =cut
                   1621: 
                   1622: sub change_content_javascript {
                   1623:     # If we're on Netscape 4, we need to use Layer-based code
1.258     albertel 1624:     if ($env{'browser.type'} eq 'netscape' &&
                   1625: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1626: 	return (<<NETSCAPE4);
                   1627: 	function change(name, content) {
                   1628: 	    doc = document.layers[name+"___escape"].layers[0].document;
                   1629: 	    doc.open();
                   1630: 	    doc.write(content);
                   1631: 	    doc.close();
                   1632: 	}
                   1633: NETSCAPE4
                   1634:     } else {
                   1635: 	# Otherwise, we need to use semi-standards-compliant code
                   1636: 	# (technically, "innerHTML" isn't standard but the equivalent
                   1637: 	# is really scary, and every useful browser supports it
                   1638: 	return (<<DOMBASED);
                   1639: 	function change(name, content) {
                   1640: 	    element = document.getElementById(name);
                   1641: 	    element.innerHTML = content;
                   1642: 	}
                   1643: DOMBASED
                   1644:     }
                   1645: }
                   1646: 
                   1647: =pod
                   1648: 
1.648     raeburn  1649: =item * &changable_area($name,$origContent):
1.256     matthew  1650: 
                   1651: This provides a "changable area" that can be modified on the fly via
                   1652: the Javascript code provided in C<change_content_javascript>. $name is
                   1653: the name you will use to reference the area later; do not repeat the
                   1654: same name on a given HTML page more then once. $origContent is what
                   1655: the area will originally contain, which can be left blank.
                   1656: 
                   1657: =cut
                   1658: 
                   1659: sub changable_area {
                   1660:     my ($name, $origContent) = @_;
                   1661: 
1.258     albertel 1662:     if ($env{'browser.type'} eq 'netscape' &&
                   1663: 	$env{'browser.version'} =~ /^4\./) {
1.256     matthew  1664: 	# If this is netscape 4, we need to use the Layer tag
                   1665: 	return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
                   1666:     } else {
                   1667: 	return "<span id='$name'>$origContent</span>";
                   1668:     }
                   1669: }
                   1670: 
                   1671: =pod
                   1672: 
1.648     raeburn  1673: =item * &viewport_geometry_js 
1.590     raeburn  1674: 
                   1675: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
                   1676: 
                   1677: =cut
                   1678: 
                   1679: 
                   1680: sub viewport_geometry_js { 
                   1681:     return <<"GEOMETRY";
                   1682: var Geometry = {};
                   1683: function init_geometry() {
                   1684:     if (Geometry.init) { return };
                   1685:     Geometry.init=1;
                   1686:     if (window.innerHeight) {
                   1687:         Geometry.getViewportHeight   = function() { return window.innerHeight; };
                   1688:         Geometry.getViewportWidth   = function() { return window.innerWidth; };
                   1689:         Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
                   1690:         Geometry.getVerticalScroll   = function() { return window.pageYOffset; };
                   1691:     }
                   1692:     else if (document.documentElement && document.documentElement.clientHeight) {
                   1693:         Geometry.getViewportHeight =
                   1694:             function() { return document.documentElement.clientHeight; };
                   1695:         Geometry.getViewportWidth =
                   1696:             function() { return document.documentElement.clientWidth; };
                   1697: 
                   1698:         Geometry.getHorizontalScroll =
                   1699:             function() { return document.documentElement.scrollLeft; };
                   1700:         Geometry.getVerticalScroll =
                   1701:             function() { return document.documentElement.scrollTop; };
                   1702:     }
                   1703:     else if (document.body.clientHeight) {
                   1704:         Geometry.getViewportHeight =
                   1705:             function() { return document.body.clientHeight; };
                   1706:         Geometry.getViewportWidth =
                   1707:             function() { return document.body.clientWidth; };
                   1708:         Geometry.getHorizontalScroll =
                   1709:             function() { return document.body.scrollLeft; };
                   1710:         Geometry.getVerticalScroll =
                   1711:             function() { return document.body.scrollTop; };
                   1712:     }
                   1713: }
                   1714: 
                   1715: GEOMETRY
                   1716: }
                   1717: 
                   1718: =pod
                   1719: 
1.648     raeburn  1720: =item * &viewport_size_js()
1.590     raeburn  1721: 
                   1722: 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. 
                   1723: 
                   1724: =cut
                   1725: 
                   1726: sub viewport_size_js {
                   1727:     my $geometry = &viewport_geometry_js();
                   1728:     return <<"DIMS";
                   1729: 
                   1730: $geometry
                   1731: 
                   1732: function getViewportDims(width,height) {
                   1733:     init_geometry();
                   1734:     width.value = Geometry.getViewportWidth();
                   1735:     height.value = Geometry.getViewportHeight();
                   1736:     return;
                   1737: }
                   1738: 
                   1739: DIMS
                   1740: }
                   1741: 
                   1742: =pod
                   1743: 
1.648     raeburn  1744: =item * &resize_textarea_js()
1.565     albertel 1745: 
                   1746: emits the needed javascript to resize a textarea to be as big as possible
                   1747: 
                   1748: creates a function resize_textrea that takes two IDs first should be
                   1749: the id of the element to resize, second should be the id of a div that
                   1750: surrounds everything that comes after the textarea, this routine needs
                   1751: to be attached to the <body> for the onload and onresize events.
                   1752: 
1.648     raeburn  1753: =back
1.565     albertel 1754: 
                   1755: =cut
                   1756: 
                   1757: sub resize_textarea_js {
1.590     raeburn  1758:     my $geometry = &viewport_geometry_js();
1.565     albertel 1759:     return <<"RESIZE";
                   1760:     <script type="text/javascript">
1.824     bisitz   1761: // <![CDATA[
1.590     raeburn  1762: $geometry
1.565     albertel 1763: 
1.588     albertel 1764: function getX(element) {
                   1765:     var x = 0;
                   1766:     while (element) {
                   1767: 	x += element.offsetLeft;
                   1768: 	element = element.offsetParent;
                   1769:     }
                   1770:     return x;
                   1771: }
                   1772: function getY(element) {
                   1773:     var y = 0;
                   1774:     while (element) {
                   1775: 	y += element.offsetTop;
                   1776: 	element = element.offsetParent;
                   1777:     }
                   1778:     return y;
                   1779: }
                   1780: 
                   1781: 
1.565     albertel 1782: function resize_textarea(textarea_id,bottom_id) {
                   1783:     init_geometry();
                   1784:     var textarea        = document.getElementById(textarea_id);
                   1785:     //alert(textarea);
                   1786: 
1.588     albertel 1787:     var textarea_top    = getY(textarea);
1.565     albertel 1788:     var textarea_height = textarea.offsetHeight;
                   1789:     var bottom          = document.getElementById(bottom_id);
1.588     albertel 1790:     var bottom_top      = getY(bottom);
1.565     albertel 1791:     var bottom_height   = bottom.offsetHeight;
                   1792:     var window_height   = Geometry.getViewportHeight();
1.588     albertel 1793:     var fudge           = 23;
1.565     albertel 1794:     var new_height      = window_height-fudge-textarea_top-bottom_height;
                   1795:     if (new_height < 300) {
                   1796: 	new_height = 300;
                   1797:     }
                   1798:     textarea.style.height=new_height+'px';
                   1799: }
1.824     bisitz   1800: // ]]>
1.565     albertel 1801: </script>
                   1802: RESIZE
                   1803: 
                   1804: }
                   1805: 
1.1205    golterma 1806: sub colorfuleditor_js {
1.1248    raeburn  1807:     my $browse_or_search;
                   1808:     my $respath;
                   1809:     my ($cnum,$cdom) = &crsauthor_url();
                   1810:     if ($cnum) {
                   1811:         $respath = "/res/$cdom/$cnum/";
                   1812:         my %js_lt = &Apache::lonlocal::texthash(
                   1813:             sunm => 'Sub-directory name',
                   1814:             save => 'Save page to make this permanent',
                   1815:         );
                   1816:         &js_escape(\%js_lt);
                   1817:         $browse_or_search = <<"END";
                   1818: 
                   1819:     function toggleChooser(form,element,titleid,only,search) {
                   1820:         var disp = 'none';
                   1821:         if (document.getElementById('chooser_'+element)) {
                   1822:             var curr = document.getElementById('chooser_'+element).style.display;
                   1823:             if (curr == 'none') {
                   1824:                 disp='inline';
                   1825:                 if (form.elements['chooser_'+element].length) {
                   1826:                     for (var i=0; i<form.elements['chooser_'+element].length; i++) {
                   1827:                         form.elements['chooser_'+element][i].checked = false;
                   1828:                     }
                   1829:                 }
                   1830:                 toggleResImport(form,element);
                   1831:             }
                   1832:             document.getElementById('chooser_'+element).style.display = disp;
                   1833:         }
                   1834:     }
                   1835: 
                   1836:     function toggleCrsFile(form,element,numdirs) {
                   1837:         if (document.getElementById('chooser_'+element+'_crsres')) {
                   1838:             var curr = document.getElementById('chooser_'+element+'_crsres').style.display;
                   1839:             if (curr == 'none') {
                   1840:                 if (numdirs) {
                   1841:                     form.elements['coursepath_'+element].selectedIndex = 0;
                   1842:                     if (numdirs > 1) {
                   1843:                         window['select1'+element+'_changed']();
                   1844:                     }
                   1845:                 }
                   1846:             } 
                   1847:             document.getElementById('chooser_'+element+'_crsres').style.display = 'block';
                   1848:             
                   1849:         }
                   1850:         if (document.getElementById('chooser_'+element+'_upload')) {
                   1851:             document.getElementById('chooser_'+element+'_upload').style.display = 'none';
                   1852:             if (document.getElementById('uploadcrsres_'+element)) {
                   1853:                 document.getElementById('uploadcrsres_'+element).value = '';
                   1854:             }
                   1855:         }
                   1856:         return;
                   1857:     }
                   1858: 
                   1859:     function toggleCrsUpload(form,element,numcrsdirs) {
                   1860:         if (document.getElementById('chooser_'+element+'_crsres')) {
                   1861:             document.getElementById('chooser_'+element+'_crsres').style.display = 'none';
                   1862:         }
                   1863:         if (document.getElementById('chooser_'+element+'_upload')) {
                   1864:             var curr = document.getElementById('chooser_'+element+'_upload').style.display;
                   1865:             if (curr == 'none') {
                   1866:                 if (numcrsdirs) {
                   1867:                    form.elements['crsauthorpath_'+element].selectedIndex = 0;
                   1868:                    form.elements['newsubdir_'+element][0].checked = true;
                   1869:                    toggleNewsubdir(form,element);
                   1870:                 }
                   1871:             }
                   1872:             document.getElementById('chooser_'+element+'_upload').style.display = 'block';
                   1873:         }
                   1874:         return;
                   1875:     }
                   1876: 
                   1877:     function toggleResImport(form,element) {
                   1878:         var choices = new Array('crsres','upload');
                   1879:         for (var i=0; i<choices.length; i++) {
                   1880:             if (document.getElementById('chooser_'+element+'_'+choices[i])) {
                   1881:                 document.getElementById('chooser_'+element+'_'+choices[i]).style.display = 'none';
                   1882:             }
                   1883:         }
                   1884:     }
                   1885: 
                   1886:     function toggleNewsubdir(form,element) {
                   1887:         var newsub = form.elements['newsubdir_'+element];
                   1888:         if (newsub) {
                   1889:             if (newsub.length) {
                   1890:                 for (var j=0; j<newsub.length; j++) {
                   1891:                     if (newsub[j].checked) {
                   1892:                         if (document.getElementById('newsubdirname_'+element)) {
                   1893:                             if (newsub[j].value == '1') {
                   1894:                                 document.getElementById('newsubdirname_'+element).type = "text";
                   1895:                                 if (document.getElementById('newsubdir_'+element)) {
                   1896:                                     document.getElementById('newsubdir_'+element).innerHTML = '<br />$js_lt{sunm}';
                   1897:                                 }
                   1898:                             } else {
                   1899:                                 document.getElementById('newsubdirname_'+element).type = "hidden";
                   1900:                                 document.getElementById('newsubdirname_'+element).value = "";
                   1901:                                 document.getElementById('newsubdir_'+element).innerHTML = "";
                   1902:                             }
                   1903:                         }
                   1904:                         break; 
                   1905:                     }
                   1906:                 }
                   1907:             }
                   1908:         }
                   1909:     }
                   1910: 
                   1911:     function updateCrsFile(form,element) {
                   1912:         var directory = form.elements['coursepath_'+element];
                   1913:         var filename = form.elements['coursefile_'+element];
                   1914:         var path = directory.options[directory.selectedIndex].value;
                   1915:         var file = filename.options[filename.selectedIndex].value;
                   1916:         form.elements[element].value = '$respath';
                   1917:         if (path == '/') {
                   1918:             form.elements[element].value += file;
                   1919:         } else {
                   1920:             form.elements[element].value += path+'/'+file;
                   1921:         }
                   1922:         unClean();
                   1923:         if (document.getElementById('previewimg_'+element)) {
                   1924:             document.getElementById('previewimg_'+element).src = form.elements[element].value;
                   1925:             var newsrc = document.getElementById('previewimg_'+element).src; 
                   1926:         }
                   1927:         if (document.getElementById('showimg_'+element)) {
                   1928:             document.getElementById('showimg_'+element).innerHTML = '($js_lt{save})';
                   1929:         }
                   1930:         toggleChooser(form,element);
                   1931:         return;
                   1932:     }
                   1933: 
                   1934:     function uploadDone(suffix,name) {
                   1935:         if (name) {
                   1936: 	    document.forms["lonhomework"].elements[suffix].value = name;
                   1937:             unClean();
                   1938:             toggleChooser(document.forms["lonhomework"],suffix);
                   1939:         }
                   1940:     }
                   1941: 
                   1942: \$(document).ready(function(){
                   1943: 
                   1944:     \$(document).delegate('form :submit', 'click', function( event ) {
                   1945:         if ( \$( this ).hasClass( "LC_uploadcrsres" ) ) {
                   1946:             var buttonId = this.id;
                   1947:             var suffix = buttonId.toString();
                   1948:             suffix = suffix.replace(/^crsupload_/,'');
                   1949:             event.preventDefault();
                   1950:             document.lonhomework.target = 'crsupload_target_'+suffix;
                   1951:             document.lonhomework.action = '/adm/coursepub?LC_uploadcrsres='+suffix;
                   1952:             \$(this.form).submit();
                   1953:             document.lonhomework.target = '';
                   1954:             if (document.getElementById('crsuploadto_'+suffix)) {
                   1955:                 document.lonhomework.action = document.getElementById('crsuploadto_'+suffix).value;
                   1956:             }
                   1957:             return false;
                   1958:         }
                   1959:     });
                   1960: });
                   1961: END
                   1962:     }
1.1205    golterma 1963:     return <<"COLORFULEDIT"
                   1964: <script type="text/javascript">
                   1965: // <![CDATA[>
                   1966:     function fold_box(curDepth, lastresource){
                   1967: 
                   1968:     // we need a list because there can be several blocks you need to fold in one tag
                   1969:         var block = document.getElementsByName('foldblock_'+curDepth);
                   1970:     // but there is only one folding button per tag
                   1971:         var foldbutton = document.getElementById('folding_btn_'+curDepth);
                   1972: 
                   1973:         if(block.item(0).style.display == 'none'){
                   1974: 
                   1975:             foldbutton.value = '@{[&mt("Hide")]}';
                   1976:             for (i = 0; i < block.length; i++){
                   1977:                 block.item(i).style.display = '';
                   1978:             }
                   1979:         }else{
                   1980: 
                   1981:             foldbutton.value = '@{[&mt("Show")]}';
                   1982:             for (i = 0; i < block.length; i++){
                   1983:                 // block.item(i).style.visibility = 'collapse';
                   1984:                 block.item(i).style.display = 'none';
                   1985:             }
                   1986:         };
                   1987:         saveState(lastresource);
                   1988:     }
                   1989: 
                   1990:     function saveState (lastresource) {
                   1991: 
                   1992:         var tag_list = getTagList();
                   1993:         if(tag_list != null){
                   1994:             var timestamp = new Date().getTime();
                   1995:             var key = lastresource;
                   1996: 
                   1997:             // the value pattern is: 'time;key1,value1;key2,value2; ... '
                   1998:             // starting with timestamp
                   1999:             var value = timestamp+';';
                   2000: 
                   2001:             // building the list of key-value pairs
                   2002:             for(var i = 0; i < tag_list.length; i++){
                   2003:                 value += tag_list[i]+',';
                   2004:                 value += document.getElementsByName(tag_list[i])[0].style.display+';';
                   2005:             }
                   2006: 
                   2007:             // only iterate whole storage if nothing to override
                   2008:             if(localStorage.getItem(key) == null){        
                   2009: 
                   2010:                 // prevent storage from growing large
                   2011:                 if(localStorage.length > 50){
                   2012:                     var regex_getTimestamp = /^(?:\d)+;/;
                   2013:                     var oldest_timestamp = regex_getTimestamp.exec(localStorage.key(0));
                   2014:                     var oldest_key;
                   2015:                     
                   2016:                     for(var i = 1; i < localStorage.length; i++){
                   2017:                         if (regex_getTimestamp.exec(localStorage.key(i)) < oldest_timestamp) {
                   2018:                             oldest_key = localStorage.key(i);
                   2019:                             oldest_timestamp = regex_getTimestamp.exec(oldest_key);
                   2020:                         }
                   2021:                     }
                   2022:                     localStorage.removeItem(oldest_key);
                   2023:                 }
                   2024:             }
                   2025:             localStorage.setItem(key,value);
                   2026:         }
                   2027:     }
                   2028: 
                   2029:     // restore folding status of blocks (on page load)
                   2030:     function restoreState (lastresource) {
                   2031:         if(localStorage.getItem(lastresource) != null){
                   2032:             var key = lastresource;
                   2033:             var value = localStorage.getItem(key);
                   2034:             var regex_delTimestamp = /^\d+;/;
                   2035: 
                   2036:             value.replace(regex_delTimestamp, '');
                   2037: 
                   2038:             var valueArr = value.split(';');
                   2039:             var pairs;
                   2040:             var elements;
                   2041:             for (var i = 0; i < valueArr.length; i++){
                   2042:                 pairs = valueArr[i].split(',');
                   2043:                 elements = document.getElementsByName(pairs[0]);
                   2044: 
                   2045:                 for (var j = 0; j < elements.length; j++){  
                   2046:                     elements[j].style.display = pairs[1];
                   2047:                     if (pairs[1] == "none"){
                   2048:                         var regex_id = /([_\\d]+)\$/;
                   2049:                         regex_id.exec(pairs[0]);
                   2050:                         document.getElementById("folding_btn"+RegExp.\$1).value = "Show";
                   2051:                     }
                   2052:                 }
                   2053:             }
                   2054:         }
                   2055:     }
                   2056: 
                   2057:     function getTagList () {
                   2058:         
                   2059:         var stringToSearch = document.lonhomework.innerHTML;
                   2060: 
                   2061:         var ret = new Array();
                   2062:         var regex_findBlock = /(foldblock_.*?)"/g;
                   2063:         var tag_list = stringToSearch.match(regex_findBlock);
                   2064: 
                   2065:         if(tag_list != null){
                   2066:             for(var i = 0; i < tag_list.length; i++){            
                   2067:                 ret.push(tag_list[i].replace(/"/, ''));
                   2068:             }
                   2069:         }
                   2070:         return ret;
                   2071:     }
                   2072: 
                   2073:     function saveScrollPosition (resource) {
                   2074:         var tag_list = getTagList();
                   2075: 
                   2076:         // we dont always want to jump to the first block
                   2077:         // 170 is roughly above the "Problem Editing" header. we just want to save if the user scrolled down further than this
                   2078:         if(\$(window).scrollTop() > 170){
                   2079:             if(tag_list != null){
                   2080:                 var result;
                   2081:                 for(var i = 0; i < tag_list.length; i++){
                   2082:                     if(isElementInViewport(tag_list[i])){
                   2083:                         result += tag_list[i]+';';
                   2084:                     }
                   2085:                 }
                   2086:                 sessionStorage.setItem('anchor_'+resource, result);
                   2087:             }
                   2088:         } else {
                   2089:             // we dont need to save zero, just delete the item to leave everything tidy
                   2090:             sessionStorage.removeItem('anchor_'+resource);
                   2091:         }
                   2092:     }
                   2093: 
                   2094:     function restoreScrollPosition(resource){
                   2095: 
                   2096:         var elem = sessionStorage.getItem('anchor_'+resource);
                   2097:         if(elem != null){
                   2098:             var tag_list = elem.split(';');
                   2099:             var elem_list;
                   2100: 
                   2101:             for(var i = 0; i < tag_list.length; i++){
                   2102:                 elem_list = document.getElementsByName(tag_list[i]);
                   2103:                 
                   2104:                 if(elem_list.length > 0){
                   2105:                     elem = elem_list[0];
                   2106:                     break;
                   2107:                 }
                   2108:             }
                   2109:             elem.scrollIntoView();
                   2110:         }
                   2111:     }
                   2112: 
                   2113:     function isElementInViewport(el) {
                   2114: 
                   2115:         // change to last element instead of first
                   2116:         var elem = document.getElementsByName(el);
                   2117:         var rect = elem[0].getBoundingClientRect();
                   2118: 
                   2119:         return (
                   2120:             rect.top >= 0 &&
                   2121:             rect.left >= 0 &&
                   2122:             rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
                   2123:             rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
                   2124:         );
                   2125:     }
                   2126:     
                   2127:     function autosize(depth){
                   2128:         var cmInst = window['cm'+depth];
                   2129:         var fitsizeButton = document.getElementById('fitsize'+depth);
                   2130: 
                   2131:         // is fixed size, switching to dynamic
                   2132:         if (sessionStorage.getItem("autosized_"+depth) == null) {
                   2133:             cmInst.setSize("","auto");
                   2134:             fitsizeButton.value = "@{[&mt('Fixed size')]}";
                   2135:             sessionStorage.setItem("autosized_"+depth, "yes");
                   2136: 
                   2137:         // is dynamic size, switching to fixed
                   2138:         } else {
                   2139:             cmInst.setSize("","300px");
                   2140:             fitsizeButton.value = "@{[&mt('Dynamic size')]}";
                   2141:             sessionStorage.removeItem("autosized_"+depth);
                   2142:         }
                   2143:     }
                   2144: 
1.1248    raeburn  2145: $browse_or_search
1.1205    golterma 2146: 
                   2147: // ]]>
                   2148: </script>
                   2149: COLORFULEDIT
                   2150: }
                   2151: 
                   2152: sub xmleditor_js {
                   2153:     return <<XMLEDIT
                   2154: <script type="text/javascript" src="/adm/jQuery/addons/jquery-scrolltofixed.js"></script>
                   2155: <script type="text/javascript">
                   2156: // <![CDATA[>
                   2157: 
                   2158:     function saveScrollPosition (resource) {
                   2159: 
                   2160:         var scrollPos = \$(window).scrollTop();
                   2161:         sessionStorage.setItem(resource,scrollPos);
                   2162:     }
                   2163: 
                   2164:     function restoreScrollPosition(resource){
                   2165: 
                   2166:         var scrollPos = sessionStorage.getItem(resource);
                   2167:         \$(window).scrollTop(scrollPos);
                   2168:     }
                   2169: 
                   2170:     // unless internet explorer
                   2171:     if (!(window.navigator.appName == "Microsoft Internet Explorer" && (document.documentMode || document.compatMode))){
                   2172: 
                   2173:         \$(document).ready(function() {
                   2174:              \$(".LC_edit_actionbar").scrollToFixed(\{zIndex: 100\});
                   2175:         });
                   2176:     }
                   2177: 
                   2178:     // inserts text at cursor position into codemirror (xml editor only)
                   2179:     function insertText(text){
                   2180:         cm.focus();
                   2181:         var curPos = cm.getCursor();
                   2182:         cm.replaceRange(text.replace(/ESCAPEDSCRIPT/g,'script'), {line: curPos.line,ch: curPos.ch});
                   2183:     }
                   2184: // ]]>
                   2185: </script>
                   2186: XMLEDIT
                   2187: }
                   2188: 
                   2189: sub insert_folding_button {
                   2190:     my $curDepth = $Apache::lonxml::curdepth;
                   2191:     my $lastresource = $env{'request.ambiguous'};
                   2192: 
                   2193:     return "<input type=\"button\" id=\"folding_btn_$curDepth\" 
                   2194:             value=\"".&mt('Hide')."\" onclick=\"fold_box('$curDepth','$lastresource')\">";
                   2195: }
                   2196: 
1.1248    raeburn  2197: sub crsauthor_url {
                   2198:     my ($url) = @_;
                   2199:     if ($url eq '') {
                   2200:         $url = $ENV{'REQUEST_URI'};
                   2201:     }
                   2202:     my ($cnum,$cdom);
                   2203:     if ($env{'request.course.id'}) {
                   2204:         my ($audom,$auname) = ($url =~ m{^/priv/($match_domain)/($match_name)/});
                   2205:         if ($audom ne '' && $auname ne '') {
                   2206:             if (($env{'course.'.$env{'request.course.id'}.'.num'} eq $auname) &&
                   2207:                 ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $audom)) {
                   2208:                 $cnum = $auname;
                   2209:                 $cdom = $audom;
                   2210:             }
                   2211:         }
                   2212:     }
                   2213:     return ($cnum,$cdom);
                   2214: }
                   2215: 
                   2216: sub import_crsauthor_form {
1.1265    raeburn  2217:     my ($form,$firstselectname,$secondselectname,$onchangefirst,$only,$suffix,$disabled) = @_;
1.1248    raeburn  2218:     return (0) unless ($env{'request.course.id'});
                   2219:     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   2220:     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   2221:     my $crshome = $env{'course.'.$env{'request.course.id'}.'.home'};
                   2222:     return (0) unless (($cnum ne '') && ($cdom ne ''));
                   2223:     my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
                   2224:     my @ids=&Apache::lonnet::current_machine_ids();
                   2225:     my ($output,$is_home,$relpath,%subdirs,%files,%selimport_menus);
                   2226:     
                   2227:     if (grep(/^\Q$crshome\E$/,@ids)) {
                   2228:         $is_home = 1;
                   2229:     }
                   2230:     $relpath = "/priv/$cdom/$cnum";
                   2231:     &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$relpath,'',\%subdirs,\%files);
                   2232:     my %lt = &Apache::lonlocal::texthash (
                   2233:         fnam => 'Filename',
                   2234:         dire => 'Directory',
                   2235:     );
                   2236:     my $numdirs = scalar(keys(%files));
                   2237:     my (%possexts,$singledir,@singledirfiles);
                   2238:     if ($only) {
                   2239:         map { $possexts{$_} = 1; } split(/\s*,\s*/,$only);
                   2240:     }
                   2241:     my (%nonemptydirs,$possdirs);
                   2242:     if ($numdirs > 1) {
                   2243:         my @order;
                   2244:         foreach my $key (sort { lc($a) cmp lc($b) } (keys(%files))) {
                   2245:             if (ref($files{$key}) eq 'HASH') {
                   2246:                 my $shown = $key;
                   2247:                 if ($key eq '') {
                   2248:                     $shown = '/';
                   2249:                 }
                   2250:                 my @ordered = ();
                   2251:                 foreach my $file (sort { lc($a) cmp lc($b) } (keys(%{$files{$key}}))) {
1.1315    raeburn  2252:                     next if ($file =~ /\.rights$/);
1.1248    raeburn  2253:                     if ($only) {
                   2254:                         my ($ext) = ($file =~ /\.([^.]+)$/);
                   2255:                         unless ($possexts{lc($ext)}) {
                   2256:                             next;
                   2257:                         }
                   2258:                     }
                   2259:                     $selimport_menus{$key}->{'select2'}->{$file} = $file;
                   2260:                     push(@ordered,$file);
                   2261:                 }
                   2262:                 if (@ordered) {
                   2263:                     push(@order,$key);
                   2264:                     $nonemptydirs{$key} = 1;
                   2265:                     $selimport_menus{$key}->{'text'} = $shown;
                   2266:                     $selimport_menus{$key}->{'default'} = '';
                   2267:                     $selimport_menus{$key}->{'select2'}->{''} = '';
                   2268:                     $selimport_menus{$key}->{'order'} = \@ordered;
                   2269:                 }
                   2270:             }
                   2271:         }
                   2272:         $possdirs = scalar(keys(%nonemptydirs));
                   2273:         if ($possdirs > 1) {
                   2274:             my @order = sort { lc($a) cmp lc($b) } (keys(%nonemptydirs));
                   2275:             $output = $lt{'dire'}.
                   2276:                       &linked_select_forms($form,'<br />'.
                   2277:                                            $lt{'fnam'},'',
                   2278:                                            $firstselectname,$secondselectname,
                   2279:                                            \%selimport_menus,\@order,
                   2280:                                            $onchangefirst,'',$suffix).'<br />';
                   2281:         } elsif ($possdirs == 1) {
                   2282:             $singledir = (keys(%nonemptydirs))[0];
                   2283:             if (ref($selimport_menus{$singledir}->{'order'}) eq 'ARRAY') {
                   2284:                 @singledirfiles = @{$selimport_menus{$singledir}->{'order'}};
                   2285:             }
                   2286:             delete($selimport_menus{$singledir});
                   2287:         }
                   2288:     } elsif ($numdirs == 1) {
                   2289:         $singledir = (keys(%files))[0];
                   2290:         foreach my $file (sort { lc($a) cmp lc($b) } (keys(%{$files{$singledir}}))) {
                   2291:             if ($only) {
                   2292:                 my ($ext) = ($file =~ /\.([^.]+)$/);
                   2293:                 unless ($possexts{lc($ext)}) {
                   2294:                     next;
                   2295:                 }
1.1315    raeburn  2296:             } else {
                   2297:                 next if ($file =~ /\.rights$/);
1.1248    raeburn  2298:             }
                   2299:             push(@singledirfiles,$file);
                   2300:         }
                   2301:         if (@singledirfiles) {
1.1315    raeburn  2302:             $possdirs = 1;
1.1248    raeburn  2303:         }
                   2304:     }
                   2305:     if (($possdirs == 1) && (@singledirfiles)) {
                   2306:         my $showdir = $singledir;
                   2307:         if ($singledir eq '') {
                   2308:             $showdir = '/';
                   2309:         }
                   2310:         $output = $lt{'dire'}.
                   2311:                   '<select name="'.$firstselectname.'">'.
                   2312:                   '<option value="'.$singledir.'">'.$showdir.'</option>'."\n".
                   2313:                   '</select><br />'.
                   2314:                   $lt{'fnam'}.'<select name="'.$secondselectname.'">'."\n".
                   2315:                   '<option value="" selected="selected">'.$lt{'se'}.'</option>'."\n";
                   2316:         foreach my $file (@singledirfiles) {
                   2317:             $output .= '<option value="'.$file.'">'.$file.'</option>'."\n";
                   2318:         }
                   2319:         $output .= '</select><br />'."\n";
                   2320:     }
                   2321:     return ($possdirs,$output);
                   2322: }
                   2323: 
1.565     albertel 2324: =pod
                   2325: 
1.256     matthew  2326: =head1 Excel and CSV file utility routines
                   2327: 
                   2328: =cut
                   2329: 
                   2330: ###############################################################
                   2331: ###############################################################
                   2332: 
                   2333: =pod
                   2334: 
1.1162    raeburn  2335: =over 4
                   2336: 
1.648     raeburn  2337: =item * &csv_translate($text) 
1.37      matthew  2338: 
1.185     www      2339: Translate $text to allow it to be output as a 'comma separated values' 
1.37      matthew  2340: format.
                   2341: 
                   2342: =cut
                   2343: 
1.180     matthew  2344: ###############################################################
                   2345: ###############################################################
1.37      matthew  2346: sub csv_translate {
                   2347:     my $text = shift;
                   2348:     $text =~ s/\"/\"\"/g;
1.209     albertel 2349:     $text =~ s/\n/ /g;
1.37      matthew  2350:     return $text;
                   2351: }
1.180     matthew  2352: 
                   2353: ###############################################################
                   2354: ###############################################################
                   2355: 
                   2356: =pod
                   2357: 
1.648     raeburn  2358: =item * &define_excel_formats()
1.180     matthew  2359: 
                   2360: Define some commonly used Excel cell formats.
                   2361: 
                   2362: Currently supported formats:
                   2363: 
                   2364: =over 4
                   2365: 
                   2366: =item header
                   2367: 
                   2368: =item bold
                   2369: 
                   2370: =item h1
                   2371: 
                   2372: =item h2
                   2373: 
                   2374: =item h3
                   2375: 
1.256     matthew  2376: =item h4
                   2377: 
                   2378: =item i
                   2379: 
1.180     matthew  2380: =item date
                   2381: 
                   2382: =back
                   2383: 
                   2384: Inputs: $workbook
                   2385: 
                   2386: Returns: $format, a hash reference.
                   2387: 
1.1057    foxr     2388: 
1.180     matthew  2389: =cut
                   2390: 
                   2391: ###############################################################
                   2392: ###############################################################
                   2393: sub define_excel_formats {
                   2394:     my ($workbook) = @_;
                   2395:     my $format;
                   2396:     $format->{'header'} = $workbook->add_format(bold      => 1, 
                   2397:                                                 bottom    => 1,
                   2398:                                                 align     => 'center');
                   2399:     $format->{'bold'} = $workbook->add_format(bold=>1);
                   2400:     $format->{'h1'}   = $workbook->add_format(bold=>1, size=>18);
                   2401:     $format->{'h2'}   = $workbook->add_format(bold=>1, size=>16);
                   2402:     $format->{'h3'}   = $workbook->add_format(bold=>1, size=>14);
1.255     matthew  2403:     $format->{'h4'}   = $workbook->add_format(bold=>1, size=>12);
1.246     matthew  2404:     $format->{'i'}    = $workbook->add_format(italic=>1);
1.180     matthew  2405:     $format->{'date'} = $workbook->add_format(num_format=>
1.207     matthew  2406:                                             'mm/dd/yyyy hh:mm:ss');
1.180     matthew  2407:     return $format;
                   2408: }
                   2409: 
                   2410: ###############################################################
                   2411: ###############################################################
1.113     bowersj2 2412: 
                   2413: =pod
                   2414: 
1.648     raeburn  2415: =item * &create_workbook()
1.255     matthew  2416: 
                   2417: Create an Excel worksheet.  If it fails, output message on the
                   2418: request object and return undefs.
                   2419: 
                   2420: Inputs: Apache request object
                   2421: 
                   2422: Returns (undef) on failure, 
                   2423:     Excel worksheet object, scalar with filename, and formats 
                   2424:     from &Apache::loncommon::define_excel_formats on success
                   2425: 
                   2426: =cut
                   2427: 
                   2428: ###############################################################
                   2429: ###############################################################
                   2430: sub create_workbook {
                   2431:     my ($r) = @_;
                   2432:         #
                   2433:     # Create the excel spreadsheet
                   2434:     my $filename = '/prtspool/'.
1.258     albertel 2435:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255     matthew  2436:         time.'_'.rand(1000000000).'.xls';
                   2437:     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
                   2438:     if (! defined($workbook)) {
                   2439:         $r->log_error("Error creating excel spreadsheet $filename: $!");
1.928     bisitz   2440:         $r->print(
                   2441:             '<p class="LC_error">'
                   2442:            .&mt('Problems occurred in creating the new Excel file.')
                   2443:            .' '.&mt('This error has been logged.')
                   2444:            .' '.&mt('Please alert your LON-CAPA administrator.')
                   2445:            .'</p>'
                   2446:         );
1.255     matthew  2447:         return (undef);
                   2448:     }
                   2449:     #
1.1014    foxr     2450:     $workbook->set_tempdir(LONCAPA::tempdir());
1.255     matthew  2451:     #
                   2452:     my $format = &Apache::loncommon::define_excel_formats($workbook);
                   2453:     return ($workbook,$filename,$format);
                   2454: }
                   2455: 
                   2456: ###############################################################
                   2457: ###############################################################
                   2458: 
                   2459: =pod
                   2460: 
1.648     raeburn  2461: =item * &create_text_file()
1.113     bowersj2 2462: 
1.542     raeburn  2463: Create a file to write to and eventually make available to the user.
1.256     matthew  2464: If file creation fails, outputs an error message on the request object and 
                   2465: return undefs.
1.113     bowersj2 2466: 
1.256     matthew  2467: Inputs: Apache request object, and file suffix
1.113     bowersj2 2468: 
1.256     matthew  2469: Returns (undef) on failure, 
                   2470:     Filehandle and filename on success.
1.113     bowersj2 2471: 
                   2472: =cut
                   2473: 
1.256     matthew  2474: ###############################################################
                   2475: ###############################################################
                   2476: sub create_text_file {
                   2477:     my ($r,$suffix) = @_;
                   2478:     if (! defined($suffix)) { $suffix = 'txt'; };
                   2479:     my $fh;
                   2480:     my $filename = '/prtspool/'.
1.258     albertel 2481:         $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256     matthew  2482:         time.'_'.rand(1000000000).'.'.$suffix;
                   2483:     $fh = Apache::File->new('>/home/httpd'.$filename);
                   2484:     if (! defined($fh)) {
                   2485:         $r->log_error("Couldn't open $filename for output $!");
1.928     bisitz   2486:         $r->print(
                   2487:             '<p class="LC_error">'
                   2488:            .&mt('Problems occurred in creating the output file.')
                   2489:            .' '.&mt('This error has been logged.')
                   2490:            .' '.&mt('Please alert your LON-CAPA administrator.')
                   2491:            .'</p>'
                   2492:         );
1.113     bowersj2 2493:     }
1.256     matthew  2494:     return ($fh,$filename)
1.113     bowersj2 2495: }
                   2496: 
                   2497: 
1.256     matthew  2498: =pod 
1.113     bowersj2 2499: 
                   2500: =back
                   2501: 
                   2502: =cut
1.37      matthew  2503: 
                   2504: ###############################################################
1.33      matthew  2505: ##        Home server <option> list generating code          ##
                   2506: ###############################################################
1.35      matthew  2507: 
1.169     www      2508: # ------------------------------------------
                   2509: 
                   2510: sub domain_select {
1.1289    raeburn  2511:     my ($name,$value,$multiple,$incdoms,$excdoms)=@_;
                   2512:     my @possdoms;
                   2513:     if (ref($incdoms) eq 'ARRAY') {
                   2514:         @possdoms = @{$incdoms};
                   2515:     } else {
                   2516:         @possdoms = &Apache::lonnet::all_domains();
                   2517:     }
                   2518: 
1.169     www      2519:     my %domains=map { 
1.514     albertel 2520: 	$_ => $_.' '. &Apache::lonnet::domain($_,'description') 
1.1289    raeburn  2521:     } @possdoms;
                   2522: 
                   2523:     if ((ref($excdoms) eq 'ARRAY') && (@{$excdoms} > 0)) {
                   2524:         foreach my $dom (@{$excdoms}) {
                   2525:             delete($domains{$dom});
                   2526:         }
                   2527:     }
                   2528: 
1.169     www      2529:     if ($multiple) {
                   2530: 	$domains{''}=&mt('Any domain');
1.550     albertel 2531: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287     albertel 2532: 	return &multiple_select_form($name,$value,4,\%domains);
1.169     www      2533:     } else {
1.550     albertel 2534: 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.970     raeburn  2535: 	return &select_form($name,$value,\%domains);
1.169     www      2536:     }
                   2537: }
                   2538: 
1.282     albertel 2539: #-------------------------------------------
                   2540: 
                   2541: =pod
                   2542: 
1.519     raeburn  2543: =head1 Routines for form select boxes
                   2544: 
                   2545: =over 4
                   2546: 
1.648     raeburn  2547: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282     albertel 2548: 
                   2549: Returns a string containing a <select> element int multiple mode
                   2550: 
                   2551: 
                   2552: Args:
                   2553:   $name - name of the <select> element
1.506     raeburn  2554:   $value - scalar or array ref of values that should already be selected
1.282     albertel 2555:   $size - number of rows long the select element is
1.283     albertel 2556:   $hash - the elements should be 'option' => 'shown text'
1.282     albertel 2557:           (shown text should already have been &mt())
1.506     raeburn  2558:   $order - (optional) array ref of the order to show the elements in
1.283     albertel 2559: 
1.282     albertel 2560: =cut
                   2561: 
                   2562: #-------------------------------------------
1.169     www      2563: sub multiple_select_form {
1.284     albertel 2564:     my ($name,$value,$size,$hash,$order)=@_;
1.169     www      2565:     my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
                   2566:     my $output='';
1.191     matthew  2567:     if (! defined($size)) {
                   2568:         $size = 4;
1.283     albertel 2569:         if (scalar(keys(%$hash))<4) {
                   2570:             $size = scalar(keys(%$hash));
1.191     matthew  2571:         }
                   2572:     }
1.734     bisitz   2573:     $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple">';
1.501     banghart 2574:     my @order;
1.506     raeburn  2575:     if (ref($order) eq 'ARRAY')  {
                   2576:         @order = @{$order};
                   2577:     } else {
                   2578:         @order = sort(keys(%$hash));
1.501     banghart 2579:     }
                   2580:     if (exists($$hash{'select_form_order'})) {
                   2581:         @order = @{$$hash{'select_form_order'}};
                   2582:     }
                   2583:         
1.284     albertel 2584:     foreach my $key (@order) {
1.356     albertel 2585:         $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284     albertel 2586:         $output.='selected="selected" ' if ($selected{$key});
                   2587:         $output.='>'.$hash->{$key}."</option>\n";
1.169     www      2588:     }
                   2589:     $output.="</select>\n";
                   2590:     return $output;
                   2591: }
                   2592: 
1.88      www      2593: #-------------------------------------------
                   2594: 
                   2595: =pod
                   2596: 
1.1254    raeburn  2597: =item * &select_form($defdom,$name,$hashref,$onchange,$readonly)
1.88      www      2598: 
                   2599: Returns a string containing a <select name='$name' size='1'> form to 
1.970     raeburn  2600: allow a user to select options from a ref to a hash containing:
                   2601: option_name => displayed text. An optional $onchange can include
1.1254    raeburn  2602: a javascript onchange item, e.g., onchange="this.form.submit();".
                   2603: An optional arg -- $readonly -- if true will cause the select form
                   2604: to be disabled, e.g., for the case where an instructor has a section-
                   2605: specific role, and is viewing/modifying parameters. 
1.970     raeburn  2606: 
1.88      www      2607: See lonrights.pm for an example invocation and use.
                   2608: 
                   2609: =cut
                   2610: 
                   2611: #-------------------------------------------
                   2612: sub select_form {
1.1228    raeburn  2613:     my ($def,$name,$hashref,$onchange,$readonly) = @_;
1.970     raeburn  2614:     return unless (ref($hashref) eq 'HASH');
                   2615:     if ($onchange) {
                   2616:         $onchange = ' onchange="'.$onchange.'"';
                   2617:     }
1.1228    raeburn  2618:     my $disabled;
                   2619:     if ($readonly) {
                   2620:         $disabled = ' disabled="disabled"';
                   2621:     }
                   2622:     my $selectform = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";
1.128     albertel 2623:     my @keys;
1.970     raeburn  2624:     if (exists($hashref->{'select_form_order'})) {
                   2625: 	@keys=@{$hashref->{'select_form_order'}};
1.128     albertel 2626:     } else {
1.970     raeburn  2627: 	@keys=sort(keys(%{$hashref}));
1.128     albertel 2628:     }
1.356     albertel 2629:     foreach my $key (@keys) {
                   2630:         $selectform.=
                   2631: 	    '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
                   2632:             ($key eq $def ? 'selected="selected" ' : '').
1.970     raeburn  2633:                 ">".$hashref->{$key}."</option>\n";
1.88      www      2634:     }
                   2635:     $selectform.="</select>";
                   2636:     return $selectform;
                   2637: }
                   2638: 
1.475     www      2639: # For display filters
                   2640: 
                   2641: sub display_filter {
1.1074    raeburn  2642:     my ($context) = @_;
1.475     www      2643:     if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477     www      2644:     if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.1074    raeburn  2645:     my $phraseinput = 'hidden';
                   2646:     my $includeinput = 'hidden';
                   2647:     my ($checked,$includetypestext);
                   2648:     if ($env{'form.displayfilter'} eq 'containing') {
                   2649:         $phraseinput = 'text'; 
                   2650:         if ($context eq 'parmslog') {
                   2651:             $includeinput = 'checkbox';
                   2652:             if ($env{'form.includetypes'}) {
                   2653:                 $checked = ' checked="checked"';
                   2654:             }
                   2655:             $includetypestext = &mt('Include parameter types');
                   2656:         }
                   2657:     } else {
                   2658:         $includetypestext = '&nbsp;';
                   2659:     }
                   2660:     my ($additional,$secondid,$thirdid);
                   2661:     if ($context eq 'parmslog') {
                   2662:         $additional = 
                   2663:             '<label><input type="'.$includeinput.'" name="includetypes"'. 
                   2664:             $checked.' name="includetypes" value="1" id="includetypes" />'.
                   2665:             '&nbsp;<span id="includetypestext">'.$includetypestext.'</span>'.
                   2666:             '</label>';
                   2667:         $secondid = 'includetypes';
                   2668:         $thirdid = 'includetypestext';
                   2669:     }
                   2670:     my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
                   2671:                                                     '$secondid','$thirdid')";
                   2672:     return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',
1.475     www      2673: 			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
                   2674: 							   (&mt('all'),10,20,50,100,1000,10000))).
1.714     bisitz   2675: 	   '</label></span> <span class="LC_nobreak">'.
1.1074    raeburn  2676:            &mt('Filter: [_1]',
1.477     www      2677: 	   &select_form($env{'form.displayfilter'},
                   2678: 			'displayfilter',
1.970     raeburn  2679: 			{'currentfolder' => 'Current folder/page',
1.477     www      2680: 			 'containing' => 'Containing phrase',
1.1074    raeburn  2681: 			 'none' => 'None'},$onchange)).'&nbsp;'.
                   2682: 			 '<input type="'.$phraseinput.'" name="containingphrase" id="containingphrase" size="30" value="'.
                   2683:                          &HTML::Entities::encode($env{'form.containingphrase'}).
                   2684:                          '" />'.$additional;
                   2685: }
                   2686: 
                   2687: sub display_filter_js {
                   2688:     my $includetext = &mt('Include parameter types');
                   2689:     return <<"ENDJS";
                   2690:   
                   2691: function toggleHistoryOptions(setter,firstid,context,secondid,thirdid) {
                   2692:     var firstType = 'hidden';
                   2693:     if (setter.options[setter.selectedIndex].value == 'containing') {
                   2694:         firstType = 'text';
                   2695:     }
                   2696:     firstObject = document.getElementById(firstid);
                   2697:     if (typeof(firstObject) == 'object') {
                   2698:         if (firstObject.type != firstType) {
                   2699:             changeInputType(firstObject,firstType);
                   2700:         }
                   2701:     }
                   2702:     if (context == 'parmslog') {
                   2703:         var secondType = 'hidden';
                   2704:         if (firstType == 'text') {
                   2705:             secondType = 'checkbox';
                   2706:         }
                   2707:         secondObject = document.getElementById(secondid);  
                   2708:         if (typeof(secondObject) == 'object') {
                   2709:             if (secondObject.type != secondType) {
                   2710:                 changeInputType(secondObject,secondType);
                   2711:             }
                   2712:         }
                   2713:         var textItem = document.getElementById(thirdid);
                   2714:         var currtext = textItem.innerHTML;
                   2715:         var newtext;
                   2716:         if (firstType == 'text') {
                   2717:             newtext = '$includetext';
                   2718:         } else {
                   2719:             newtext = '&nbsp;';
                   2720:         }
                   2721:         if (currtext != newtext) {
                   2722:             textItem.innerHTML = newtext;
                   2723:         }
                   2724:     }
                   2725:     return;
                   2726: }
                   2727: 
                   2728: function changeInputType(oldObject,newType) {
                   2729:     var newObject = document.createElement('input');
                   2730:     newObject.type = newType;
                   2731:     if (oldObject.size) {
                   2732:         newObject.size = oldObject.size;
                   2733:     }
                   2734:     if (oldObject.value) {
                   2735:         newObject.value = oldObject.value;
                   2736:     }
                   2737:     if (oldObject.name) {
                   2738:         newObject.name = oldObject.name;
                   2739:     }
                   2740:     if (oldObject.id) {
                   2741:         newObject.id = oldObject.id;
                   2742:     }
                   2743:     oldObject.parentNode.replaceChild(newObject,oldObject);
                   2744:     return;
                   2745: }
                   2746: 
                   2747: ENDJS
1.475     www      2748: }
                   2749: 
1.167     www      2750: sub gradeleveldescription {
                   2751:     my $gradelevel=shift;
                   2752:     my %gradelevels=(0 => 'Not specified',
                   2753: 		     1 => 'Grade 1',
                   2754: 		     2 => 'Grade 2',
                   2755: 		     3 => 'Grade 3',
                   2756: 		     4 => 'Grade 4',
                   2757: 		     5 => 'Grade 5',
                   2758: 		     6 => 'Grade 6',
                   2759: 		     7 => 'Grade 7',
                   2760: 		     8 => 'Grade 8',
                   2761: 		     9 => 'Grade 9',
                   2762: 		     10 => 'Grade 10',
                   2763: 		     11 => 'Grade 11',
                   2764: 		     12 => 'Grade 12',
                   2765: 		     13 => 'Grade 13',
                   2766: 		     14 => '100 Level',
                   2767: 		     15 => '200 Level',
                   2768: 		     16 => '300 Level',
                   2769: 		     17 => '400 Level',
                   2770: 		     18 => 'Graduate Level');
                   2771:     return &mt($gradelevels{$gradelevel});
                   2772: }
                   2773: 
1.163     www      2774: sub select_level_form {
                   2775:     my ($deflevel,$name)=@_;
                   2776:     unless ($deflevel) { $deflevel=0; }
1.167     www      2777:     my $selectform = "<select name=\"$name\" size=\"1\">\n";
                   2778:     for (my $i=0; $i<=18; $i++) {
                   2779:         $selectform.="<option value=\"$i\" ".
1.253     albertel 2780:             ($i==$deflevel ? 'selected="selected" ' : '').
1.167     www      2781:                 ">".&gradeleveldescription($i)."</option>\n";
                   2782:     }
                   2783:     $selectform.="</select>";
                   2784:     return $selectform;
1.163     www      2785: }
1.167     www      2786: 
1.35      matthew  2787: #-------------------------------------------
                   2788: 
1.45      matthew  2789: =pod
                   2790: 
1.1256    raeburn  2791: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled)
1.35      matthew  2792: 
                   2793: Returns a string containing a <select name='$name' size='1'> form to 
                   2794: allow a user to select the domain to preform an operation in.  
                   2795: See loncreateuser.pm for an example invocation and use.
                   2796: 
1.90      www      2797: If the $includeempty flag is set, it also includes an empty choice ("no domain
                   2798: selected");
                   2799: 
1.743     raeburn  2800: If the $showdomdesc flag is set, the domain name is followed by the domain description.
                   2801: 
1.910     raeburn  2802: 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.
                   2803: 
1.1121    raeburn  2804: The optional $incdoms is a reference to an array of domains which will be the only available options.
                   2805: 
                   2806: The optional $excdoms is a reference to an array of domains which will be excluded from the available options.
1.563     raeburn  2807: 
1.1256    raeburn  2808: The optional $disabled argument, if true, adds the disabled attribute to the select tag.
                   2809: 
1.35      matthew  2810: =cut
                   2811: 
                   2812: #-------------------------------------------
1.34      matthew  2813: sub select_dom_form {
1.1256    raeburn  2814:     my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled) = @_;
1.872     raeburn  2815:     if ($onchange) {
1.874     raeburn  2816:         $onchange = ' onchange="'.$onchange.'"';
1.743     raeburn  2817:     }
1.1256    raeburn  2818:     if ($disabled) {
                   2819:         $disabled = ' disabled="disabled"';
                   2820:     }
1.1121    raeburn  2821:     my (@domains,%exclude);
1.910     raeburn  2822:     if (ref($incdoms) eq 'ARRAY') {
                   2823:         @domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
                   2824:     } else {
                   2825:         @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
                   2826:     }
1.90      www      2827:     if ($includeempty) { @domains=('',@domains); }
1.1121    raeburn  2828:     if (ref($excdoms) eq 'ARRAY') {
                   2829:         map { $exclude{$_} = 1; } @{$excdoms}; 
                   2830:     }
1.1256    raeburn  2831:     my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";
1.356     albertel 2832:     foreach my $dom (@domains) {
1.1121    raeburn  2833:         next if ($exclude{$dom});
1.356     albertel 2834:         $selectdomain.="<option value=\"$dom\" ".
1.563     raeburn  2835:             ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
                   2836:         if ($showdomdesc) {
                   2837:             if ($dom ne '') {
                   2838:                 my $domdesc = &Apache::lonnet::domain($dom,'description');
                   2839:                 if ($domdesc ne '') {
                   2840:                     $selectdomain .= ' ('.$domdesc.')';
                   2841:                 }
                   2842:             } 
                   2843:         }
                   2844:         $selectdomain .= "</option>\n";
1.34      matthew  2845:     }
                   2846:     $selectdomain.="</select>";
                   2847:     return $selectdomain;
                   2848: }
                   2849: 
1.35      matthew  2850: #-------------------------------------------
                   2851: 
1.45      matthew  2852: =pod
                   2853: 
1.648     raeburn  2854: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35      matthew  2855: 
1.586     raeburn  2856: input: 4 arguments (two required, two optional) - 
                   2857:     $domain - domain of new user
                   2858:     $name - name of form element
                   2859:     $default - Value of 'default' causes a default item to be first 
                   2860:                             option, and selected by default. 
                   2861:     $hide - Value of 'hide' causes hiding of the name of the server, 
                   2862:                             if 1 server found, or default, if 0 found.
1.594     raeburn  2863: output: returns 2 items: 
1.586     raeburn  2864: (a) form element which contains either:
                   2865:    (i) <select name="$name">
                   2866:         <option value="$hostid1">$hostid $servers{$hostid}</option>
                   2867:         <option value="$hostid2">$hostid $servers{$hostid}</option>       
                   2868:        </select>
                   2869:        form item if there are multiple library servers in $domain, or
                   2870:    (ii) an <input type="hidden" name="$name" value="$hostid" /> form item 
                   2871:        if there is only one library server in $domain.
                   2872: 
                   2873: (b) number of library servers found.
                   2874: 
                   2875: See loncreateuser.pm for example of use.
1.35      matthew  2876: 
                   2877: =cut
                   2878: 
                   2879: #-------------------------------------------
1.586     raeburn  2880: sub home_server_form_item {
                   2881:     my ($domain,$name,$default,$hide) = @_;
1.513     albertel 2882:     my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586     raeburn  2883:     my $result;
                   2884:     my $numlib = keys(%servers);
                   2885:     if ($numlib > 1) {
                   2886:         $result .= '<select name="'.$name.'" />'."\n";
                   2887:         if ($default) {
1.804     bisitz   2888:             $result .= '<option value="default" selected="selected">'.&mt('default').
1.586     raeburn  2889:                        '</option>'."\n";
                   2890:         }
                   2891:         foreach my $hostid (sort(keys(%servers))) {
                   2892:             $result.= '<option value="'.$hostid.'">'.
                   2893: 	              $hostid.' '.$servers{$hostid}."</option>\n";
                   2894:         }
                   2895:         $result .= '</select>'."\n";
                   2896:     } elsif ($numlib == 1) {
                   2897:         my $hostid;
                   2898:         foreach my $item (keys(%servers)) {
                   2899:             $hostid = $item;
                   2900:         }
                   2901:         $result .= '<input type="hidden" name="'.$name.'" value="'.
                   2902:                    $hostid.'" />';
                   2903:                    if (!$hide) {
                   2904:                        $result .= $hostid.' '.$servers{$hostid};
                   2905:                    }
                   2906:                    $result .= "\n";
                   2907:     } elsif ($default) {
                   2908:         $result .= '<input type="hidden" name="'.$name.
                   2909:                    '" value="default" />';
                   2910:                    if (!$hide) {
                   2911:                        $result .= &mt('default');
                   2912:                    }
                   2913:                    $result .= "\n";
1.33      matthew  2914:     }
1.586     raeburn  2915:     return ($result,$numlib);
1.33      matthew  2916: }
1.112     bowersj2 2917: 
                   2918: =pod
                   2919: 
1.534     albertel 2920: =back 
                   2921: 
1.112     bowersj2 2922: =cut
1.87      matthew  2923: 
                   2924: ###############################################################
1.112     bowersj2 2925: ##                  Decoding User Agent                      ##
1.87      matthew  2926: ###############################################################
                   2927: 
                   2928: =pod
                   2929: 
1.112     bowersj2 2930: =head1 Decoding the User Agent
                   2931: 
                   2932: =over 4
                   2933: 
                   2934: =item * &decode_user_agent()
1.87      matthew  2935: 
                   2936: Inputs: $r
                   2937: 
                   2938: Outputs:
                   2939: 
                   2940: =over 4
                   2941: 
1.112     bowersj2 2942: =item * $httpbrowser
1.87      matthew  2943: 
1.112     bowersj2 2944: =item * $clientbrowser
1.87      matthew  2945: 
1.112     bowersj2 2946: =item * $clientversion
1.87      matthew  2947: 
1.112     bowersj2 2948: =item * $clientmathml
1.87      matthew  2949: 
1.112     bowersj2 2950: =item * $clientunicode
1.87      matthew  2951: 
1.112     bowersj2 2952: =item * $clientos
1.87      matthew  2953: 
1.1137    raeburn  2954: =item * $clientmobile
                   2955: 
1.1141    raeburn  2956: =item * $clientinfo
                   2957: 
1.1194    raeburn  2958: =item * $clientosversion
                   2959: 
1.87      matthew  2960: =back
                   2961: 
1.157     matthew  2962: =back 
                   2963: 
1.87      matthew  2964: =cut
                   2965: 
                   2966: ###############################################################
                   2967: ###############################################################
                   2968: sub decode_user_agent {
1.247     albertel 2969:     my ($r)=@_;
1.87      matthew  2970:     my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
                   2971:     my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
                   2972:     my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247     albertel 2973:     if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87      matthew  2974:     my $clientbrowser='unknown';
                   2975:     my $clientversion='0';
                   2976:     my $clientmathml='';
                   2977:     my $clientunicode='0';
1.1137    raeburn  2978:     my $clientmobile=0;
1.1194    raeburn  2979:     my $clientosversion='';
1.87      matthew  2980:     for (my $i=0;$i<=$#browsertype;$i++) {
1.1193    raeburn  2981:         my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\%/,$browsertype[$i]);
1.87      matthew  2982: 	if (($httpbrowser=~/$match/i)  && ($httpbrowser!~/$notmatch/i)) {
                   2983: 	    $clientbrowser=$bname;
                   2984:             $httpbrowser=~/$vreg/i;
                   2985: 	    $clientversion=$1;
                   2986:             $clientmathml=($clientversion>=$minv);
                   2987:             $clientunicode=($clientversion>=$univ);
                   2988: 	}
                   2989:     }
                   2990:     my $clientos='unknown';
1.1141    raeburn  2991:     my $clientinfo;
1.87      matthew  2992:     if (($httpbrowser=~/linux/i) ||
                   2993:         ($httpbrowser=~/unix/i) ||
                   2994:         ($httpbrowser=~/ux/i) ||
                   2995:         ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
                   2996:     if (($httpbrowser=~/vax/i) ||
                   2997:         ($httpbrowser=~/vms/i)) { $clientos='vms'; }
                   2998:     if ($httpbrowser=~/next/i) { $clientos='next'; }
                   2999:     if (($httpbrowser=~/mac/i) ||
                   3000:         ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
1.1194    raeburn  3001:     if ($httpbrowser=~/win/i) {
                   3002:         $clientos='win';
                   3003:         if ($httpbrowser =~/Windows\s+NT\s+(\d+\.\d+)/i) {
                   3004:             $clientosversion = $1;
                   3005:         }
                   3006:     }
1.87      matthew  3007:     if ($httpbrowser=~/embed/i) { $clientos='pda'; }
1.1137    raeburn  3008:     if ($httpbrowser=~/(Android|iPod|iPad|iPhone|webOS|Blackberry|Windows Phone|Opera m(?:ob|in)|Fennec)/i) {
                   3009:         $clientmobile=lc($1);
                   3010:     }
1.1141    raeburn  3011:     if ($httpbrowser=~ m{Firefox/(\d+\.\d+)}) {
                   3012:         $clientinfo = 'firefox-'.$1;
                   3013:     } elsif ($httpbrowser=~ m{chromeframe/(\d+\.\d+)\.}) {
                   3014:         $clientinfo = 'chromeframe-'.$1;
                   3015:     }
1.87      matthew  3016:     return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
1.1194    raeburn  3017:             $clientunicode,$clientos,$clientmobile,$clientinfo,
                   3018:             $clientosversion);
1.87      matthew  3019: }
                   3020: 
1.32      matthew  3021: ###############################################################
                   3022: ##    Authentication changing form generation subroutines    ##
                   3023: ###############################################################
                   3024: ##
                   3025: ## All of the authform_xxxxxxx subroutines take their inputs in a
                   3026: ## hash, and have reasonable default values.
                   3027: ##
                   3028: ##    formname = the name given in the <form> tag.
1.35      matthew  3029: #-------------------------------------------
                   3030: 
1.45      matthew  3031: =pod
                   3032: 
1.112     bowersj2 3033: =head1 Authentication Routines
                   3034: 
                   3035: =over 4
                   3036: 
1.648     raeburn  3037: =item * &authform_xxxxxx()
1.35      matthew  3038: 
                   3039: The authform_xxxxxx subroutines provide javascript and html forms which 
                   3040: handle some of the conveniences required for authentication forms.  
                   3041: This is not an optimal method, but it works.  
                   3042: 
                   3043: =over 4
                   3044: 
1.112     bowersj2 3045: =item * authform_header
1.35      matthew  3046: 
1.112     bowersj2 3047: =item * authform_authorwarning
1.35      matthew  3048: 
1.112     bowersj2 3049: =item * authform_nochange
1.35      matthew  3050: 
1.112     bowersj2 3051: =item * authform_kerberos
1.35      matthew  3052: 
1.112     bowersj2 3053: =item * authform_internal
1.35      matthew  3054: 
1.112     bowersj2 3055: =item * authform_filesystem
1.35      matthew  3056: 
1.1310    raeburn  3057: =item * authform_lti
                   3058: 
1.35      matthew  3059: =back
                   3060: 
1.648     raeburn  3061: See loncreateuser.pm for invocation and use examples.
1.157     matthew  3062: 
1.35      matthew  3063: =cut
                   3064: 
                   3065: #-------------------------------------------
1.32      matthew  3066: sub authform_header{  
                   3067:     my %in = (
                   3068:         formname => 'cu',
1.80      albertel 3069:         kerb_def_dom => '',
1.32      matthew  3070:         @_,
                   3071:     );
                   3072:     $in{'formname'} = 'document.' . $in{'formname'};
                   3073:     my $result='';
1.80      albertel 3074: 
                   3075: #---------------------------------------------- Code for upper case translation
                   3076:     my $Javascript_toUpperCase;
                   3077:     unless ($in{kerb_def_dom}) {
                   3078:         $Javascript_toUpperCase =<<"END";
                   3079:         switch (choice) {
                   3080:            case 'krb': currentform.elements[choicearg].value =
                   3081:                currentform.elements[choicearg].value.toUpperCase();
                   3082:                break;
                   3083:            default:
                   3084:         }
                   3085: END
                   3086:     } else {
                   3087:         $Javascript_toUpperCase = "";
                   3088:     }
                   3089: 
1.165     raeburn  3090:     my $radioval = "'nochange'";
1.591     raeburn  3091:     if (defined($in{'curr_authtype'})) {
                   3092:         if ($in{'curr_authtype'} ne '') {
                   3093:             $radioval = "'".$in{'curr_authtype'}."arg'";
                   3094:         }
1.174     matthew  3095:     }
1.165     raeburn  3096:     my $argfield = 'null';
1.591     raeburn  3097:     if (defined($in{'mode'})) {
1.165     raeburn  3098:         if ($in{'mode'} eq 'modifycourse')  {
1.591     raeburn  3099:             if (defined($in{'curr_autharg'})) {
                   3100:                 if ($in{'curr_autharg'} ne '') {
1.165     raeburn  3101:                     $argfield = "'$in{'curr_autharg'}'";
                   3102:                 }
                   3103:             }
                   3104:         }
                   3105:     }
                   3106: 
1.32      matthew  3107:     $result.=<<"END";
                   3108: var current = new Object();
1.165     raeburn  3109: current.radiovalue = $radioval;
                   3110: current.argfield = $argfield;
1.32      matthew  3111: 
                   3112: function changed_radio(choice,currentform) {
                   3113:     var choicearg = choice + 'arg';
                   3114:     // If a radio button in changed, we need to change the argfield
                   3115:     if (current.radiovalue != choice) {
                   3116:         current.radiovalue = choice;
                   3117:         if (current.argfield != null) {
                   3118:             currentform.elements[current.argfield].value = '';
                   3119:         }
                   3120:         if (choice == 'nochange') {
                   3121:             current.argfield = null;
                   3122:         } else {
                   3123:             current.argfield = choicearg;
                   3124:             switch(choice) {
                   3125:                 case 'krb': 
                   3126:                     currentform.elements[current.argfield].value = 
                   3127:                         "$in{'kerb_def_dom'}";
                   3128:                 break;
                   3129:               default:
                   3130:                 break;
                   3131:             }
                   3132:         }
                   3133:     }
                   3134:     return;
                   3135: }
1.22      www      3136: 
1.32      matthew  3137: function changed_text(choice,currentform) {
                   3138:     var choicearg = choice + 'arg';
                   3139:     if (currentform.elements[choicearg].value !='') {
1.80      albertel 3140:         $Javascript_toUpperCase
1.32      matthew  3141:         // clear old field
                   3142:         if ((current.argfield != choicearg) && (current.argfield != null)) {
                   3143:             currentform.elements[current.argfield].value = '';
                   3144:         }
                   3145:         current.argfield = choicearg;
                   3146:     }
                   3147:     set_auth_radio_buttons(choice,currentform);
                   3148:     return;
1.20      www      3149: }
1.32      matthew  3150: 
                   3151: function set_auth_radio_buttons(newvalue,currentform) {
1.986     raeburn  3152:     var numauthchoices = currentform.login.length;
                   3153:     if (typeof numauthchoices  == "undefined") {
                   3154:         return;
                   3155:     } 
1.32      matthew  3156:     var i=0;
1.986     raeburn  3157:     while (i < numauthchoices) {
1.32      matthew  3158:         if (currentform.login[i].value == newvalue) { break; }
                   3159:         i++;
                   3160:     }
1.986     raeburn  3161:     if (i == numauthchoices) {
1.32      matthew  3162:         return;
                   3163:     }
                   3164:     current.radiovalue = newvalue;
                   3165:     currentform.login[i].checked = true;
                   3166:     return;
                   3167: }
                   3168: END
                   3169:     return $result;
                   3170: }
                   3171: 
1.1106    raeburn  3172: sub authform_authorwarning {
1.32      matthew  3173:     my $result='';
1.144     matthew  3174:     $result='<i>'.
                   3175:         &mt('As a general rule, only authors or co-authors should be '.
                   3176:             'filesystem authenticated '.
                   3177:             '(which allows access to the server filesystem).')."</i>\n";
1.32      matthew  3178:     return $result;
                   3179: }
                   3180: 
1.1106    raeburn  3181: sub authform_nochange {
1.32      matthew  3182:     my %in = (
                   3183:               formname => 'document.cu',
                   3184:               kerb_def_dom => 'MSU.EDU',
                   3185:               @_,
                   3186:           );
1.1106    raeburn  3187:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.586     raeburn  3188:     my $result;
1.1104    raeburn  3189:     if (!$authnum) {
1.1105    raeburn  3190:         $result = &mt('Under your current role you are not permitted to change login settings for this user');
1.586     raeburn  3191:     } else {
                   3192:         $result = '<label>'.&mt('[_1] Do not change login data',
                   3193:                   '<input type="radio" name="login" value="nochange" '.
                   3194:                   'checked="checked" onclick="'.
1.281     albertel 3195:             "javascript:changed_radio('nochange',$in{'formname'});".'" />').
                   3196: 	    '</label>';
1.586     raeburn  3197:     }
1.32      matthew  3198:     return $result;
                   3199: }
                   3200: 
1.591     raeburn  3201: sub authform_kerberos {
1.32      matthew  3202:     my %in = (
                   3203:               formname => 'document.cu',
                   3204:               kerb_def_dom => 'MSU.EDU',
1.80      albertel 3205:               kerb_def_auth => 'krb4',
1.32      matthew  3206:               @_,
                   3207:               );
1.586     raeburn  3208:     my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
1.1259    raeburn  3209:         $autharg,$jscall,$disabled);
1.1106    raeburn  3210:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.80      albertel 3211:     if ($in{'kerb_def_auth'} eq 'krb5') {
1.772     bisitz   3212:        $check5 = ' checked="checked"';
1.80      albertel 3213:     } else {
1.772     bisitz   3214:        $check4 = ' checked="checked"';
1.80      albertel 3215:     }
1.1259    raeburn  3216:     if ($in{'readonly'}) {
                   3217:         $disabled = ' disabled="disabled"';
                   3218:     }
1.165     raeburn  3219:     $krbarg = $in{'kerb_def_dom'};
1.591     raeburn  3220:     if (defined($in{'curr_authtype'})) {
                   3221:         if ($in{'curr_authtype'} eq 'krb') {
1.772     bisitz   3222:             $krbcheck = ' checked="checked"';
1.623     raeburn  3223:             if (defined($in{'mode'})) {
                   3224:                 if ($in{'mode'} eq 'modifyuser') {
                   3225:                     $krbcheck = '';
                   3226:                 }
                   3227:             }
1.591     raeburn  3228:             if (defined($in{'curr_kerb_ver'})) {
                   3229:                 if ($in{'curr_krb_ver'} eq '5') {
1.772     bisitz   3230:                     $check5 = ' checked="checked"';
1.591     raeburn  3231:                     $check4 = '';
                   3232:                 } else {
1.772     bisitz   3233:                     $check4 = ' checked="checked"';
1.591     raeburn  3234:                     $check5 = '';
                   3235:                 }
1.586     raeburn  3236:             }
1.591     raeburn  3237:             if (defined($in{'curr_autharg'})) {
1.165     raeburn  3238:                 $krbarg = $in{'curr_autharg'};
                   3239:             }
1.586     raeburn  3240:             if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591     raeburn  3241:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  3242:                     $result = 
                   3243:     &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
                   3244:         $in{'curr_autharg'},$krbver);
                   3245:                 } else {
                   3246:                     $result =
                   3247:     &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
                   3248:                 }
                   3249:                 return $result; 
                   3250:             }
                   3251:         }
                   3252:     } else {
                   3253:         if ($authnum == 1) {
1.784     bisitz   3254:             $authtype = '<input type="hidden" name="login" value="krb" />';
1.165     raeburn  3255:         }
                   3256:     }
1.586     raeburn  3257:     if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
                   3258:         return;
1.587     raeburn  3259:     } elsif ($authtype eq '') {
1.591     raeburn  3260:         if (defined($in{'mode'})) {
1.587     raeburn  3261:             if ($in{'mode'} eq 'modifycourse') {
                   3262:                 if ($authnum == 1) {
1.1259    raeburn  3263:                     $authtype = '<input type="radio" name="login" value="krb"'.$disabled.' />';
1.587     raeburn  3264:                 }
                   3265:             }
                   3266:         }
1.586     raeburn  3267:     }
                   3268:     $jscall = "javascript:changed_radio('krb',$in{'formname'});";
                   3269:     if ($authtype eq '') {
                   3270:         $authtype = '<input type="radio" name="login" value="krb" '.
                   3271:                     'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
1.1259    raeburn  3272:                     $krbcheck.$disabled.' />';
1.586     raeburn  3273:     }
                   3274:     if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
1.1106    raeburn  3275:         ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
1.586     raeburn  3276:          $in{'curr_authtype'} eq 'krb5') ||
1.1106    raeburn  3277:         (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
1.586     raeburn  3278:          $in{'curr_authtype'} eq 'krb4')) {
                   3279:         $result .= &mt
1.144     matthew  3280:         ('[_1] Kerberos authenticated with domain [_2] '.
1.281     albertel 3281:          '[_3] Version 4 [_4] Version 5 [_5]',
1.586     raeburn  3282:          '<label>'.$authtype,
1.281     albertel 3283:          '</label><input type="text" size="10" name="krbarg" '.
1.165     raeburn  3284:              'value="'.$krbarg.'" '.
1.1259    raeburn  3285:              'onchange="'.$jscall.'"'.$disabled.' />',
                   3286:          '<label><input type="radio" name="krbver" value="4" '.$check4.$disabled.' />',
                   3287:          '</label><label><input type="radio" name="krbver" value="5" '.$check5.$disabled.' />',
1.281     albertel 3288: 	 '</label>');
1.586     raeburn  3289:     } elsif ($can_assign{'krb4'}) {
                   3290:         $result .= &mt
                   3291:         ('[_1] Kerberos authenticated with domain [_2] '.
                   3292:          '[_3] Version 4 [_4]',
                   3293:          '<label>'.$authtype,
                   3294:          '</label><input type="text" size="10" name="krbarg" '.
                   3295:              'value="'.$krbarg.'" '.
1.1259    raeburn  3296:              'onchange="'.$jscall.'"'.$disabled.' />',
1.586     raeburn  3297:          '<label><input type="hidden" name="krbver" value="4" />',
                   3298:          '</label>');
                   3299:     } elsif ($can_assign{'krb5'}) {
                   3300:         $result .= &mt
                   3301:         ('[_1] Kerberos authenticated with domain [_2] '.
                   3302:          '[_3] Version 5 [_4]',
                   3303:          '<label>'.$authtype,
                   3304:          '</label><input type="text" size="10" name="krbarg" '.
                   3305:              'value="'.$krbarg.'" '.
1.1259    raeburn  3306:              'onchange="'.$jscall.'"'.$disabled.' />',
1.586     raeburn  3307:          '<label><input type="hidden" name="krbver" value="5" />',
                   3308:          '</label>');
                   3309:     }
1.32      matthew  3310:     return $result;
                   3311: }
                   3312: 
1.1106    raeburn  3313: sub authform_internal {
1.586     raeburn  3314:     my %in = (
1.32      matthew  3315:                 formname => 'document.cu',
                   3316:                 kerb_def_dom => 'MSU.EDU',
                   3317:                 @_,
                   3318:                 );
1.1259    raeburn  3319:     my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall,$disabled);
1.1106    raeburn  3320:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1259    raeburn  3321:     if ($in{'readonly'}) {
                   3322:         $disabled = ' disabled="disabled"';
                   3323:     }
1.591     raeburn  3324:     if (defined($in{'curr_authtype'})) {
                   3325:         if ($in{'curr_authtype'} eq 'int') {
1.586     raeburn  3326:             if ($can_assign{'int'}) {
1.772     bisitz   3327:                 $intcheck = 'checked="checked" ';
1.623     raeburn  3328:                 if (defined($in{'mode'})) {
                   3329:                     if ($in{'mode'} eq 'modifyuser') {
                   3330:                         $intcheck = '';
                   3331:                     }
                   3332:                 }
1.591     raeburn  3333:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  3334:                     $intarg = $in{'curr_autharg'};
                   3335:                 }
                   3336:             } else {
                   3337:                 $result = &mt('Currently internally authenticated.');
                   3338:                 return $result;
1.165     raeburn  3339:             }
                   3340:         }
1.586     raeburn  3341:     } else {
                   3342:         if ($authnum == 1) {
1.784     bisitz   3343:             $authtype = '<input type="hidden" name="login" value="int" />';
1.586     raeburn  3344:         }
                   3345:     }
                   3346:     if (!$can_assign{'int'}) {
                   3347:         return;
1.587     raeburn  3348:     } elsif ($authtype eq '') {
1.591     raeburn  3349:         if (defined($in{'mode'})) {
1.587     raeburn  3350:             if ($in{'mode'} eq 'modifycourse') {
                   3351:                 if ($authnum == 1) {
1.1259    raeburn  3352:                     $authtype = '<input type="radio" name="login" value="int"'.$disabled.' />';
1.587     raeburn  3353:                 }
                   3354:             }
                   3355:         }
1.165     raeburn  3356:     }
1.586     raeburn  3357:     $jscall = "javascript:changed_radio('int',$in{'formname'});";
                   3358:     if ($authtype eq '') {
                   3359:         $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
1.1259    raeburn  3360:                     ' onchange="'.$jscall.'" onclick="'.$jscall.'"'.$disabled.' />';
1.586     raeburn  3361:     }
1.605     bisitz   3362:     $autharg = '<input type="password" size="10" name="intarg" value="'.
1.1259    raeburn  3363:                $intarg.'" onchange="'.$jscall.'"'.$disabled.' />';
1.586     raeburn  3364:     $result = &mt
1.144     matthew  3365:         ('[_1] Internally authenticated (with initial password [_2])',
1.586     raeburn  3366:          '<label>'.$authtype,'</label>'.$autharg);
1.1259    raeburn  3367:     $result.='<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.intarg.type='."'text'".' } else { this.form.intarg.type='."'password'".' }"'.$disabled.' />'.&mt('Visible input').'</label>';
1.32      matthew  3368:     return $result;
                   3369: }
                   3370: 
1.1104    raeburn  3371: sub authform_local {
1.32      matthew  3372:     my %in = (
                   3373:               formname => 'document.cu',
                   3374:               kerb_def_dom => 'MSU.EDU',
                   3375:               @_,
                   3376:               );
1.1259    raeburn  3377:     my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall,$disabled);
1.1106    raeburn  3378:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1259    raeburn  3379:     if ($in{'readonly'}) {
                   3380:         $disabled = ' disabled="disabled"';
                   3381:     } 
1.591     raeburn  3382:     if (defined($in{'curr_authtype'})) {
                   3383:         if ($in{'curr_authtype'} eq 'loc') {
1.586     raeburn  3384:             if ($can_assign{'loc'}) {
1.772     bisitz   3385:                 $loccheck = 'checked="checked" ';
1.623     raeburn  3386:                 if (defined($in{'mode'})) {
                   3387:                     if ($in{'mode'} eq 'modifyuser') {
                   3388:                         $loccheck = '';
                   3389:                     }
                   3390:                 }
1.591     raeburn  3391:                 if (defined($in{'curr_autharg'})) {
1.586     raeburn  3392:                     $locarg = $in{'curr_autharg'};
                   3393:                 }
                   3394:             } else {
                   3395:                 $result = &mt('Currently using local (institutional) authentication.');
                   3396:                 return $result;
1.165     raeburn  3397:             }
                   3398:         }
1.586     raeburn  3399:     } else {
                   3400:         if ($authnum == 1) {
1.784     bisitz   3401:             $authtype = '<input type="hidden" name="login" value="loc" />';
1.586     raeburn  3402:         }
                   3403:     }
                   3404:     if (!$can_assign{'loc'}) {
                   3405:         return;
1.587     raeburn  3406:     } elsif ($authtype eq '') {
1.591     raeburn  3407:         if (defined($in{'mode'})) {
1.587     raeburn  3408:             if ($in{'mode'} eq 'modifycourse') {
                   3409:                 if ($authnum == 1) {
1.1259    raeburn  3410:                     $authtype = '<input type="radio" name="login" value="loc"'.$disabled.' />';
1.587     raeburn  3411:                 }
                   3412:             }
                   3413:         }
1.165     raeburn  3414:     }
1.586     raeburn  3415:     $jscall = "javascript:changed_radio('loc',$in{'formname'});";
                   3416:     if ($authtype eq '') {
                   3417:         $authtype = '<input type="radio" name="login" value="loc" '.
                   3418:                     $loccheck.' onchange="'.$jscall.'" onclick="'.
1.1259    raeburn  3419:                     $jscall.'"'.$disabled.' />';
1.586     raeburn  3420:     }
                   3421:     $autharg = '<input type="text" size="10" name="locarg" value="'.
1.1259    raeburn  3422:                $locarg.'" onchange="'.$jscall.'"'.$disabled.' />';
1.586     raeburn  3423:     $result = &mt('[_1] Local Authentication with argument [_2]',
                   3424:                   '<label>'.$authtype,'</label>'.$autharg);
1.32      matthew  3425:     return $result;
                   3426: }
                   3427: 
1.1106    raeburn  3428: sub authform_filesystem {
1.32      matthew  3429:     my %in = (
                   3430:               formname => 'document.cu',
                   3431:               kerb_def_dom => 'MSU.EDU',
                   3432:               @_,
                   3433:               );
1.1259    raeburn  3434:     my ($fsyscheck,$result,$authtype,$autharg,$jscall,$disabled);
1.1106    raeburn  3435:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.1259    raeburn  3436:     if ($in{'readonly'}) {
                   3437:         $disabled = ' disabled="disabled"';
                   3438:     }
1.591     raeburn  3439:     if (defined($in{'curr_authtype'})) {
                   3440:         if ($in{'curr_authtype'} eq 'fsys') {
1.586     raeburn  3441:             if ($can_assign{'fsys'}) {
1.772     bisitz   3442:                 $fsyscheck = 'checked="checked" ';
1.623     raeburn  3443:                 if (defined($in{'mode'})) {
                   3444:                     if ($in{'mode'} eq 'modifyuser') {
                   3445:                         $fsyscheck = '';
                   3446:                     }
                   3447:                 }
1.586     raeburn  3448:             } else {
                   3449:                 $result = &mt('Currently Filesystem Authenticated.');
                   3450:                 return $result;
1.1259    raeburn  3451:             }
1.586     raeburn  3452:         }
                   3453:     } else {
                   3454:         if ($authnum == 1) {
1.784     bisitz   3455:             $authtype = '<input type="hidden" name="login" value="fsys" />';
1.586     raeburn  3456:         }
                   3457:     }
                   3458:     if (!$can_assign{'fsys'}) {
                   3459:         return;
1.587     raeburn  3460:     } elsif ($authtype eq '') {
1.591     raeburn  3461:         if (defined($in{'mode'})) {
1.587     raeburn  3462:             if ($in{'mode'} eq 'modifycourse') {
                   3463:                 if ($authnum == 1) {
1.1259    raeburn  3464:                     $authtype = '<input type="radio" name="login" value="fsys"'.$disabled.' />';
1.587     raeburn  3465:                 }
                   3466:             }
                   3467:         }
1.586     raeburn  3468:     }
                   3469:     $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
                   3470:     if ($authtype eq '') {
                   3471:         $authtype = '<input type="radio" name="login" value="fsys" '.
                   3472:                     $fsyscheck.' onchange="'.$jscall.'" onclick="'.
1.1259    raeburn  3473:                     $jscall.'"'.$disabled.' />';
1.586     raeburn  3474:     }
1.1310    raeburn  3475:     $autharg = '<input type="password" size="10" name="fsysarg" value=""'.
1.1259    raeburn  3476:                ' onchange="'.$jscall.'"'.$disabled.' />';
1.586     raeburn  3477:     $result = &mt
1.144     matthew  3478:         ('[_1] Filesystem Authenticated (with initial password [_2])',
1.1310    raeburn  3479:          '<label>'.$authtype,'</label>'.$autharg);
                   3480:     return $result;
                   3481: }
                   3482: 
                   3483: sub authform_lti {
                   3484:     my %in = (
                   3485:               formname => 'document.cu',
                   3486:               kerb_def_dom => 'MSU.EDU',
                   3487:               @_,
                   3488:               );
                   3489:     my ($lticheck,$result,$authtype,$autharg,$jscall,$disabled);
                   3490:     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
                   3491:     if ($in{'readonly'}) {
                   3492:         $disabled = ' disabled="disabled"';
                   3493:     }
                   3494:     if (defined($in{'curr_authtype'})) {
                   3495:         if ($in{'curr_authtype'} eq 'lti') {
                   3496:             if ($can_assign{'lti'}) {
                   3497:                 $lticheck = 'checked="checked" ';
                   3498:                 if (defined($in{'mode'})) {
                   3499:                     if ($in{'mode'} eq 'modifyuser') {
                   3500:                         $lticheck = '';
                   3501:                     }
                   3502:                 }
                   3503:             } else {
                   3504:                 $result = &mt('Currently LTI Authenticated.');
                   3505:                 return $result;
                   3506:             }
                   3507:         }
                   3508:     } else {
                   3509:         if ($authnum == 1) {
                   3510:             $authtype = '<input type="hidden" name="login" value="lti" />';
                   3511:         }
                   3512:     }
                   3513:     if (!$can_assign{'lti'}) {
                   3514:         return;
                   3515:     } elsif ($authtype eq '') {
                   3516:         if (defined($in{'mode'})) {
                   3517:             if ($in{'mode'} eq 'modifycourse') {
                   3518:                 if ($authnum == 1) {
                   3519:                     $authtype = '<input type="radio" name="login" value="lti"'.$disabled.' />';
                   3520:                 }
                   3521:             }
                   3522:         }
                   3523:     }
                   3524:     $jscall = "javascript:changed_radio('lti',$in{'formname'});";
                   3525:     if (($authtype eq '') && (($in{'mode'} eq 'modifycourse') || ($in{'curr_authtype'} ne 'lti'))) {
                   3526:         $authtype = '<input type="radio" name="login" value="lti" '.
                   3527:                     $lticheck.' onchange="'.$jscall.'" onclick="'.
                   3528:                     $jscall.'"'.$disabled.' />';
                   3529:     }
                   3530:     $autharg = '<input type="hidden" name="ltiarg" value="" />';
                   3531:     if ($authtype) {
                   3532:         $result = &mt('[_1] LTI Authenticated',
                   3533:                       '<label>'.$authtype.'</label>'.$autharg);
                   3534:     } else {
                   3535:         $result = '<b>'.&mt('LTI Authenticated').'</b>'.
                   3536:                   $autharg;
                   3537:     }
1.32      matthew  3538:     return $result;
                   3539: }
                   3540: 
1.586     raeburn  3541: sub get_assignable_auth {
                   3542:     my ($dom) = @_;
                   3543:     if ($dom eq '') {
                   3544:         $dom = $env{'request.role.domain'};
                   3545:     }
                   3546:     my %can_assign = (
                   3547:                           krb4 => 1,
                   3548:                           krb5 => 1,
                   3549:                           int  => 1,
                   3550:                           loc  => 1,
1.1310    raeburn  3551:                           lti  => 1,
1.586     raeburn  3552:                      );
                   3553:     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
                   3554:     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   3555:         if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
                   3556:             my $authhash = $domconfig{'usercreation'}{'authtypes'};
                   3557:             my $context;
                   3558:             if ($env{'request.role'} =~ /^au/) {
                   3559:                 $context = 'author';
1.1259    raeburn  3560:             } elsif ($env{'request.role'} =~ /^(dc|dh)/) {
1.586     raeburn  3561:                 $context = 'domain';
                   3562:             } elsif ($env{'request.course.id'}) {
                   3563:                 $context = 'course';
                   3564:             }
                   3565:             if ($context) {
                   3566:                 if (ref($authhash->{$context}) eq 'HASH') {
                   3567:                    %can_assign = %{$authhash->{$context}}; 
                   3568:                 }
                   3569:             }
                   3570:         }
                   3571:     }
                   3572:     my $authnum = 0;
                   3573:     foreach my $key (keys(%can_assign)) {
                   3574:         if ($can_assign{$key}) {
                   3575:             $authnum ++;
                   3576:         }
                   3577:     }
                   3578:     if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
                   3579:         $authnum --;
                   3580:     }
                   3581:     return ($authnum,%can_assign);
                   3582: }
                   3583: 
1.1331    raeburn  3584: sub check_passwd_rules {
                   3585:     my ($domain,$plainpass) = @_;
                   3586:     my %passwdconf = &Apache::lonnet::get_passwdconf($domain);
                   3587:     my ($min,$max,@chars,@brokerule,$warning);
1.1333    raeburn  3588:     $min = $Apache::lonnet::passwdmin;
1.1331    raeburn  3589:     if (ref($passwdconf{'chars'}) eq 'ARRAY') {
                   3590:         if ($passwdconf{'min'} =~ /^\d+$/) {
1.1333    raeburn  3591:             if ($passwdconf{'min'} > $min) {
                   3592:                 $min = $passwdconf{'min'};
                   3593:             }
1.1331    raeburn  3594:         }
                   3595:         if ($passwdconf{'max'} =~ /^\d+$/) {
                   3596:             $max = $passwdconf{'max'};
                   3597:         }
                   3598:         @chars = @{$passwdconf{'chars'}};
                   3599:     }
                   3600:     if (($min) && (length($plainpass) < $min)) {
                   3601:         push(@brokerule,'min');
                   3602:     }
                   3603:     if (($max) && (length($plainpass) > $max)) {
                   3604:         push(@brokerule,'max');
                   3605:     }
                   3606:     if (@chars) {
                   3607:         my %rules;
                   3608:         map { $rules{$_} = 1; } @chars;
                   3609:         if ($rules{'uc'}) {
                   3610:             unless ($plainpass =~ /[A-Z]/) {
                   3611:                 push(@brokerule,'uc');
                   3612:             }
                   3613:         }
                   3614:         if ($rules{'lc'}) {
1.1332    raeburn  3615:             unless ($plainpass =~ /[a-z]/) {
1.1331    raeburn  3616:                 push(@brokerule,'lc');
                   3617:             }
                   3618:         }
                   3619:         if ($rules{'num'}) {
                   3620:             unless ($plainpass =~ /\d/) {
                   3621:                 push(@brokerule,'num');
                   3622:             }
                   3623:         }
                   3624:         if ($rules{'spec'}) {
                   3625:             unless ($plainpass =~ /[!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/) {
                   3626:                 push(@brokerule,'spec');
                   3627:             }
                   3628:         }
                   3629:     }
                   3630:     if (@brokerule) {
                   3631:         my %rulenames = &Apache::lonlocal::texthash(
                   3632:             uc   => 'At least one upper case letter',
                   3633:             lc   => 'At least one lower case letter',
                   3634:             num  => 'At least one number',
                   3635:             spec => 'At least one non-alphanumeric',
                   3636:         );
                   3637:         $rulenames{'uc'} .= ': ABCDEFGHIJKLMNOPQRSTUVWXYZ';
                   3638:         $rulenames{'lc'} .= ': abcdefghijklmnopqrstuvwxyz';
                   3639:         $rulenames{'num'} .= ': 0123456789';
                   3640:         $rulenames{'spec'} .= ': !&quot;\#$%&amp;\'()*+,-./:;&lt;=&gt;?@[\]^_\`{|}~';
                   3641:         $rulenames{'min'} = &mt('Minimum password length: [_1]',$min);
                   3642:         $rulenames{'max'} = &mt('Maximum password length: [_1]',$max);
                   3643:         $warning = &mt('Password did not satisfy the following:').'<ul>';
1.1336    raeburn  3644:         foreach my $rule ('min','max','uc','lc','num','spec') {
1.1331    raeburn  3645:             if (grep(/^$rule$/,@brokerule)) {
                   3646:                 $warning .= '<li>'.$rulenames{$rule}.'</li>';
                   3647:             }
                   3648:         }
                   3649:         $warning .= '</ul>';
                   3650:     }
1.1332    raeburn  3651:     if (wantarray) {
                   3652:         return @brokerule;
                   3653:     }
1.1331    raeburn  3654:     return $warning;
                   3655: }
                   3656: 
1.1376    raeburn  3657: sub passwd_validation_js {
1.1377    raeburn  3658:     my ($currpasswdval,$domain,$context,$id) = @_;
                   3659:     my (%passwdconf,$alertmsg);
                   3660:     if ($context eq 'linkprot') {
                   3661:         my %domconfig = &Apache::lonnet::get_dom('configuration',['ltisec'],$domain);
                   3662:         if (ref($domconfig{'ltisec'}) eq 'HASH') {
                   3663:             if (ref($domconfig{'ltisec'}{'rules'}) eq 'HASH') {
                   3664:                 %passwdconf = %{$domconfig{'ltisec'}{'rules'}};
                   3665:             }
                   3666:         }
                   3667:         if ($id eq 'add') {
                   3668:             $alertmsg = &mt('Secret for added launcher did not satisfy requirement(s):').'\n\n';
                   3669:         } elsif ($id =~ /^\d+$/) {
                   3670:             my $pos = $id+1;
                   3671:             $alertmsg = &mt('Secret for launcher [_1] did not satisfy requirement(s):','#'.$pos).'\n\n';
                   3672:         } else {
                   3673:             $alertmsg = &mt('A secret did not satisfy requirement(s):').'\n\n';
                   3674:         }
                   3675:     } else {
                   3676:         %passwdconf = &Apache::lonnet::get_passwdconf($domain);
                   3677:         $alertmsg = &mt('Initial password did not satisfy requirement(s):').'\n\n';
                   3678:     }
1.1376    raeburn  3679:     my ($min,$max,@chars,$numrules,$intargjs,%alert);
                   3680:     $numrules = 0;
                   3681:     $min = $Apache::lonnet::passwdmin;
                   3682:     if (ref($passwdconf{'chars'}) eq 'ARRAY') {
                   3683:         if ($passwdconf{'min'} =~ /^\d+$/) {
                   3684:             if ($passwdconf{'min'} > $min) {
                   3685:                 $min = $passwdconf{'min'};
                   3686:             }
                   3687:         }
                   3688:         if ($passwdconf{'max'} =~ /^\d+$/) {
                   3689:             $max = $passwdconf{'max'};
                   3690:             $numrules ++;
                   3691:         }
                   3692:         @chars = @{$passwdconf{'chars'}};
                   3693:         if (@chars) {
                   3694:             $numrules ++;
                   3695:         }
                   3696:     }
                   3697:     if ($min > 0) {
                   3698:         $numrules ++;
                   3699:     }
                   3700:     if (($min > 0) || ($max ne '') || (@chars > 0)) {
                   3701:         if ($min) {
                   3702:             $alert{'min'} = &mt('minimum [quant,_1,character]',$min).'\n';
                   3703:         }
                   3704:         if ($max) {
                   3705:             $alert{'max'} = &mt('maximum [quant,_1,character]',$max).'\n';
                   3706:         }
                   3707:         my (@charalerts,@charrules);
                   3708:         if (@chars) {
                   3709:             if (grep(/^uc$/,@chars)) {
                   3710:                 push(@charalerts,&mt('contain at least one upper case letter'));
                   3711:                 push(@charrules,'uc');
                   3712:             }
                   3713:             if (grep(/^lc$/,@chars)) {
                   3714:                 push(@charalerts,&mt('contain at least one lower case letter'));
                   3715:                 push(@charrules,'lc');
                   3716:             }
                   3717:             if (grep(/^num$/,@chars)) {
                   3718:                 push(@charalerts,&mt('contain at least one number'));
                   3719:                 push(@charrules,'num');
                   3720:             }
                   3721:             if (grep(/^spec$/,@chars)) {
                   3722:                 push(@charalerts,&mt('contain at least one non-alphanumeric'));
                   3723:                 push(@charrules,'spec');
                   3724:             }
                   3725:         }
                   3726:         $intargjs = qq|            var rulesmsg = '';\n|.
                   3727:                     qq|            var currpwval = $currpasswdval;\n|;
                   3728:             if ($min) {
                   3729:                 $intargjs .= qq|
                   3730:             if (currpwval.length < $min) {
                   3731:                 rulesmsg += ' - $alert{min}';
                   3732:             }
                   3733: |;
                   3734:             }
                   3735:             if ($max) {
                   3736:                 $intargjs .= qq|
                   3737:             if (currpwval.length > $max) {
                   3738:                 rulesmsg += ' - $alert{max}';
                   3739:             }
                   3740: |;
                   3741:             }
                   3742:             if (@chars > 0) {
                   3743:                 my $charrulestr = '"'.join('","',@charrules).'"';
                   3744:                 my $charalertstr = '"'.join('","',@charalerts).'"';
                   3745:                 $intargjs .= qq|            var brokerules = new Array();\n|.
                   3746:                              qq|            var charrules = new Array($charrulestr);\n|.
                   3747:                              qq|            var charalerts = new Array($charalertstr);\n|;
                   3748:                 my %rules;
                   3749:                 map { $rules{$_} = 1; } @chars;
                   3750:                 if ($rules{'uc'}) {
                   3751:                     $intargjs .= qq|
                   3752:             var ucRegExp = /[A-Z]/;
                   3753:             if (!ucRegExp.test(currpwval)) {
                   3754:                 brokerules.push('uc');
                   3755:             }
                   3756: |;
                   3757:                 }
                   3758:                 if ($rules{'lc'}) {
                   3759:                     $intargjs .= qq|
                   3760:             var lcRegExp = /[a-z]/;
                   3761:             if (!lcRegExp.test(currpwval)) {
                   3762:                 brokerules.push('lc');
                   3763:             }
                   3764: |;
                   3765:                 }
                   3766:                 if ($rules{'num'}) {
                   3767:                      $intargjs .= qq|
                   3768:             var numRegExp = /[0-9]/;
                   3769:             if (!numRegExp.test(currpwval)) {
                   3770:                 brokerules.push('num');
                   3771:             }
                   3772: |;
                   3773:                 }
                   3774:                 if ($rules{'spec'}) {
                   3775:                      $intargjs .= q|
                   3776:             var specRegExp = /[!"#$%&'()*+,\-.\/:;<=>?@[\\^\]_`{\|}~]/;
                   3777:             if (!specRegExp.test(currpwval)) {
                   3778:                 brokerules.push('spec');
                   3779:             }
                   3780: |;
                   3781:                 }
                   3782:                 $intargjs .= qq|
                   3783:             if (brokerules.length > 0) {
                   3784:                 for (var i=0; i<brokerules.length; i++) {
                   3785:                     for (var j=0; j<charrules.length; j++) {
                   3786:                         if (brokerules[i] == charrules[j]) {
                   3787:                             rulesmsg += ' - '+charalerts[j]+'\\n';
                   3788:                             break;
                   3789:                         }
                   3790:                     }
                   3791:                 }
                   3792:             }
                   3793: |;
                   3794:             }
                   3795:             $intargjs .= qq|
                   3796:             if (rulesmsg != '') {
                   3797:                 rulesmsg = '$alertmsg'+rulesmsg;
                   3798:                 alert(rulesmsg);
                   3799:                 return false;
                   3800:             }
                   3801: |;
                   3802:     }
                   3803:     return ($numrules,$intargjs);
                   3804: }
                   3805: 
1.80      albertel 3806: ###############################################################
                   3807: ##    Get Kerberos Defaults for Domain                 ##
                   3808: ###############################################################
                   3809: ##
                   3810: ## Returns default kerberos version and an associated argument
                   3811: ## as listed in file domain.tab. If not listed, provides
                   3812: ## appropriate default domain and kerberos version.
                   3813: ##
                   3814: #-------------------------------------------
                   3815: 
                   3816: =pod
                   3817: 
1.648     raeburn  3818: =item * &get_kerberos_defaults()
1.80      albertel 3819: 
                   3820: get_kerberos_defaults($target_domain) returns the default kerberos
1.641     raeburn  3821: version and domain. If not found, it defaults to version 4 and the 
                   3822: domain of the server.
1.80      albertel 3823: 
1.648     raeburn  3824: =over 4
                   3825: 
1.80      albertel 3826: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
                   3827: 
1.648     raeburn  3828: =back
                   3829: 
                   3830: =back
                   3831: 
1.80      albertel 3832: =cut
                   3833: 
                   3834: #-------------------------------------------
                   3835: sub get_kerberos_defaults {
                   3836:     my $domain=shift;
1.641     raeburn  3837:     my ($krbdef,$krbdefdom);
                   3838:     my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
                   3839:     if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
                   3840:         $krbdef = $domdefaults{'auth_def'};
                   3841:         $krbdefdom = $domdefaults{'auth_arg_def'};
                   3842:     } else {
1.80      albertel 3843:         $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
                   3844:         my $krbdefdom=$1;
                   3845:         $krbdefdom=~tr/a-z/A-Z/;
                   3846:         $krbdef = "krb4";
                   3847:     }
                   3848:     return ($krbdef,$krbdefdom);
                   3849: }
1.112     bowersj2 3850: 
1.32      matthew  3851: 
1.46      matthew  3852: ###############################################################
                   3853: ##                Thesaurus Functions                        ##
                   3854: ###############################################################
1.20      www      3855: 
1.46      matthew  3856: =pod
1.20      www      3857: 
1.112     bowersj2 3858: =head1 Thesaurus Functions
                   3859: 
                   3860: =over 4
                   3861: 
1.648     raeburn  3862: =item * &initialize_keywords()
1.46      matthew  3863: 
                   3864: Initializes the package variable %Keywords if it is empty.  Uses the
                   3865: package variable $thesaurus_db_file.
                   3866: 
                   3867: =cut
                   3868: 
                   3869: ###################################################
                   3870: 
                   3871: sub initialize_keywords {
                   3872:     return 1 if (scalar keys(%Keywords));
                   3873:     # If we are here, %Keywords is empty, so fill it up
                   3874:     #   Make sure the file we need exists...
                   3875:     if (! -e $thesaurus_db_file) {
                   3876:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
                   3877:                                  " failed because it does not exist");
                   3878:         return 0;
                   3879:     }
                   3880:     #   Set up the hash as a database
                   3881:     my %thesaurus_db;
                   3882:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 3883:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  3884:         &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
                   3885:                                  $thesaurus_db_file);
                   3886:         return 0;
                   3887:     } 
                   3888:     #  Get the average number of appearances of a word.
                   3889:     my $avecount = $thesaurus_db{'average.count'};
                   3890:     #  Put keywords (those that appear > average) into %Keywords
                   3891:     while (my ($word,$data)=each (%thesaurus_db)) {
                   3892:         my ($count,undef) = split /:/,$data;
                   3893:         $Keywords{$word}++ if ($count > $avecount);
                   3894:     }
                   3895:     untie %thesaurus_db;
                   3896:     # Remove special values from %Keywords.
1.356     albertel 3897:     foreach my $value ('total.count','average.count') {
                   3898:         delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586     raeburn  3899:   }
1.46      matthew  3900:     return 1;
                   3901: }
                   3902: 
                   3903: ###################################################
                   3904: 
                   3905: =pod
                   3906: 
1.648     raeburn  3907: =item * &keyword($word)
1.46      matthew  3908: 
                   3909: Returns true if $word is a keyword.  A keyword is a word that appears more 
                   3910: than the average number of times in the thesaurus database.  Calls 
                   3911: &initialize_keywords
                   3912: 
                   3913: =cut
                   3914: 
                   3915: ###################################################
1.20      www      3916: 
                   3917: sub keyword {
1.46      matthew  3918:     return if (!&initialize_keywords());
                   3919:     my $word=lc(shift());
                   3920:     $word=~s/\W//g;
                   3921:     return exists($Keywords{$word});
1.20      www      3922: }
1.46      matthew  3923: 
                   3924: ###############################################################
                   3925: 
                   3926: =pod 
1.20      www      3927: 
1.648     raeburn  3928: =item * &get_related_words()
1.46      matthew  3929: 
1.160     matthew  3930: Look up a word in the thesaurus.  Takes a scalar argument and returns
1.46      matthew  3931: an array of words.  If the keyword is not in the thesaurus, an empty array
                   3932: will be returned.  The order of the words returned is determined by the
                   3933: database which holds them.
                   3934: 
                   3935: Uses global $thesaurus_db_file.
                   3936: 
1.1057    foxr     3937: 
1.46      matthew  3938: =cut
                   3939: 
                   3940: ###############################################################
                   3941: sub get_related_words {
                   3942:     my $keyword = shift;
                   3943:     my %thesaurus_db;
                   3944:     if (! -e $thesaurus_db_file) {
                   3945:         &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
                   3946:                                  "failed because the file does not exist");
                   3947:         return ();
                   3948:     }
                   3949:     if (! tie(%thesaurus_db,'GDBM_File',
1.53      albertel 3950:               $thesaurus_db_file,&GDBM_READER(),0640)){
1.46      matthew  3951:         return ();
                   3952:     } 
                   3953:     my @Words=();
1.429     www      3954:     my $count=0;
1.46      matthew  3955:     if (exists($thesaurus_db{$keyword})) {
1.356     albertel 3956: 	# The first element is the number of times
                   3957: 	# the word appears.  We do not need it now.
1.429     www      3958: 	my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
                   3959: 	my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
                   3960: 	my $threshold=$mostfrequentcount/10;
                   3961:         foreach my $possibleword (@RelatedWords) {
                   3962:             my ($word,$wordcount)=split(/\,/,$possibleword);
                   3963:             if ($wordcount>$threshold) {
                   3964: 		push(@Words,$word);
                   3965:                 $count++;
                   3966:                 if ($count>10) { last; }
                   3967: 	    }
1.20      www      3968:         }
                   3969:     }
1.46      matthew  3970:     untie %thesaurus_db;
                   3971:     return @Words;
1.14      harris41 3972: }
1.1090    foxr     3973: ###############################################################
                   3974: #
                   3975: #  Spell checking
                   3976: #
                   3977: 
                   3978: =pod
                   3979: 
1.1142    raeburn  3980: =back
                   3981: 
1.1090    foxr     3982: =head1 Spell checking
                   3983: 
                   3984: =over 4
                   3985: 
                   3986: =item * &check_spelling($wordlist $language)
                   3987: 
                   3988: Takes a string containing words and feeds it to an external
                   3989: spellcheck program via a pipeline. Returns a string containing
                   3990: them mis-spelled words.
                   3991: 
                   3992: Parameters:
                   3993: 
                   3994: =over 4
                   3995: 
                   3996: =item - $wordlist
                   3997: 
                   3998: String that will be fed into the spellcheck program.
                   3999: 
                   4000: =item - $language
                   4001: 
                   4002: Language string that specifies the language for which the spell
                   4003: check will be performed.
                   4004: 
                   4005: =back
                   4006: 
                   4007: =back
                   4008: 
                   4009: Note: This sub assumes that aspell is installed.
                   4010: 
                   4011: 
                   4012: =cut
                   4013: 
1.46      matthew  4014: 
1.1090    foxr     4015: sub check_spelling {
                   4016:     my ($wordlist, $language) = @_;
1.1091    foxr     4017:     my @misspellings;
                   4018:     
                   4019:     # Generate the speller and set the langauge.
                   4020:     # if explicitly selected:
1.1090    foxr     4021: 
1.1091    foxr     4022:     my $speller = Text::Aspell->new;
1.1090    foxr     4023:     if ($language) {
1.1091    foxr     4024: 	$speller->set_option('lang', $language);
1.1090    foxr     4025:     }
                   4026: 
1.1091    foxr     4027:     # Turn the word list into an array of words by splittingon whitespace
1.1090    foxr     4028: 
1.1091    foxr     4029:     my @words = split(/\s+/, $wordlist);
1.1090    foxr     4030: 
1.1091    foxr     4031:     foreach my $word (@words) {
                   4032: 	if(! $speller->check($word)) {
                   4033: 	    push(@misspellings, $word);
1.1090    foxr     4034: 	}
                   4035:     }
1.1091    foxr     4036:     return join(' ', @misspellings);
                   4037:     
1.1090    foxr     4038: }
                   4039: 
1.61      www      4040: # -------------------------------------------------------------- Plaintext name
1.81      albertel 4041: =pod
                   4042: 
1.112     bowersj2 4043: =head1 User Name Functions
                   4044: 
                   4045: =over 4
                   4046: 
1.648     raeburn  4047: =item * &plainname($uname,$udom,$first)
1.81      albertel 4048: 
1.112     bowersj2 4049: Takes a users logon name and returns it as a string in
1.226     albertel 4050: "first middle last generation" form 
                   4051: if $first is set to 'lastname' then it returns it as
                   4052: 'lastname generation, firstname middlename' if their is a lastname
1.81      albertel 4053: 
                   4054: =cut
1.61      www      4055: 
1.295     www      4056: 
1.81      albertel 4057: ###############################################################
1.61      www      4058: sub plainname {
1.226     albertel 4059:     my ($uname,$udom,$first)=@_;
1.537     albertel 4060:     return if (!defined($uname) || !defined($udom));
1.295     www      4061:     my %names=&getnames($uname,$udom);
1.226     albertel 4062:     my $name=&Apache::lonnet::format_name($names{'firstname'},
                   4063: 					  $names{'middlename'},
                   4064: 					  $names{'lastname'},
                   4065: 					  $names{'generation'},$first);
                   4066:     $name=~s/^\s+//;
1.62      www      4067:     $name=~s/\s+$//;
                   4068:     $name=~s/\s+/ /g;
1.353     albertel 4069:     if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62      www      4070:     return $name;
1.61      www      4071: }
1.66      www      4072: 
                   4073: # -------------------------------------------------------------------- Nickname
1.81      albertel 4074: =pod
                   4075: 
1.648     raeburn  4076: =item * &nickname($uname,$udom)
1.81      albertel 4077: 
                   4078: Gets a users name and returns it as a string as
                   4079: 
                   4080: "&quot;nickname&quot;"
1.66      www      4081: 
1.81      albertel 4082: if the user has a nickname or
                   4083: 
                   4084: "first middle last generation"
                   4085: 
                   4086: if the user does not
                   4087: 
                   4088: =cut
1.66      www      4089: 
                   4090: sub nickname {
                   4091:     my ($uname,$udom)=@_;
1.537     albertel 4092:     return if (!defined($uname) || !defined($udom));
1.295     www      4093:     my %names=&getnames($uname,$udom);
1.68      albertel 4094:     my $name=$names{'nickname'};
1.66      www      4095:     if ($name) {
                   4096:        $name='&quot;'.$name.'&quot;'; 
                   4097:     } else {
                   4098:        $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
                   4099: 	     $names{'lastname'}.' '.$names{'generation'};
                   4100:        $name=~s/\s+$//;
                   4101:        $name=~s/\s+/ /g;
                   4102:     }
                   4103:     return $name;
                   4104: }
                   4105: 
1.295     www      4106: sub getnames {
                   4107:     my ($uname,$udom)=@_;
1.537     albertel 4108:     return if (!defined($uname) || !defined($udom));
1.433     albertel 4109:     if ($udom eq 'public' && $uname eq 'public') {
                   4110: 	return ('lastname' => &mt('Public'));
                   4111:     }
1.295     www      4112:     my $id=$uname.':'.$udom;
                   4113:     my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
                   4114:     if ($cached) {
                   4115: 	return %{$names};
                   4116:     } else {
                   4117: 	my %loadnames=&Apache::lonnet::get('environment',
                   4118:                     ['firstname','middlename','lastname','generation','nickname'],
                   4119: 					 $udom,$uname);
                   4120: 	&Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
                   4121: 	return %loadnames;
                   4122:     }
                   4123: }
1.61      www      4124: 
1.542     raeburn  4125: # -------------------------------------------------------------------- getemails
1.648     raeburn  4126: 
1.542     raeburn  4127: =pod
                   4128: 
1.648     raeburn  4129: =item * &getemails($uname,$udom)
1.542     raeburn  4130: 
                   4131: Gets a user's email information and returns it as a hash with keys:
                   4132: notification, critnotification, permanentemail
                   4133: 
                   4134: For notification and critnotification, values are comma-separated lists 
1.648     raeburn  4135: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542     raeburn  4136:  
1.648     raeburn  4137: 
1.542     raeburn  4138: =cut
                   4139: 
1.648     raeburn  4140: 
1.466     albertel 4141: sub getemails {
                   4142:     my ($uname,$udom)=@_;
                   4143:     if ($udom eq 'public' && $uname eq 'public') {
                   4144: 	return;
                   4145:     }
1.467     www      4146:     if (!$udom) { $udom=$env{'user.domain'}; }
                   4147:     if (!$uname) { $uname=$env{'user.name'}; }
1.466     albertel 4148:     my $id=$uname.':'.$udom;
                   4149:     my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
                   4150:     if ($cached) {
                   4151: 	return %{$names};
                   4152:     } else {
                   4153: 	my %loadnames=&Apache::lonnet::get('environment',
                   4154:                     			   ['notification','critnotification',
                   4155: 					    'permanentemail'],
                   4156: 					   $udom,$uname);
                   4157: 	&Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
                   4158: 	return %loadnames;
                   4159:     }
                   4160: }
                   4161: 
1.551     albertel 4162: sub flush_email_cache {
                   4163:     my ($uname,$udom)=@_;
                   4164:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   4165:     if (!$uname) { $uname=$env{'user.name'};   }
                   4166:     return if ($udom eq 'public' && $uname eq 'public');
                   4167:     my $id=$uname.':'.$udom;
                   4168:     &Apache::lonnet::devalidate_cache_new('emailscache',$id);
                   4169: }
                   4170: 
1.728     raeburn  4171: # -------------------------------------------------------------------- getlangs
                   4172: 
                   4173: =pod
                   4174: 
                   4175: =item * &getlangs($uname,$udom)
                   4176: 
                   4177: Gets a user's language preference and returns it as a hash with key:
                   4178: language.
                   4179: 
                   4180: =cut
                   4181: 
                   4182: 
                   4183: sub getlangs {
                   4184:     my ($uname,$udom) = @_;
                   4185:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   4186:     if (!$uname) { $uname=$env{'user.name'};   }
                   4187:     my $id=$uname.':'.$udom;
                   4188:     my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
                   4189:     if ($cached) {
                   4190:         return %{$langs};
                   4191:     } else {
                   4192:         my %loadlangs=&Apache::lonnet::get('environment',['languages'],
                   4193:                                            $udom,$uname);
                   4194:         &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
                   4195:         return %loadlangs;
                   4196:     }
                   4197: }
                   4198: 
                   4199: sub flush_langs_cache {
                   4200:     my ($uname,$udom)=@_;
                   4201:     if (!$udom)  { $udom =$env{'user.domain'}; }
                   4202:     if (!$uname) { $uname=$env{'user.name'};   }
                   4203:     return if ($udom eq 'public' && $uname eq 'public');
                   4204:     my $id=$uname.':'.$udom;
                   4205:     &Apache::lonnet::devalidate_cache_new('userlangs',$id);
                   4206: }
                   4207: 
1.61      www      4208: # ------------------------------------------------------------------ Screenname
1.81      albertel 4209: 
                   4210: =pod
                   4211: 
1.648     raeburn  4212: =item * &screenname($uname,$udom)
1.81      albertel 4213: 
                   4214: Gets a users screenname and returns it as a string
                   4215: 
                   4216: =cut
1.61      www      4217: 
                   4218: sub screenname {
                   4219:     my ($uname,$udom)=@_;
1.258     albertel 4220:     if ($uname eq $env{'user.name'} &&
                   4221: 	$udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212     albertel 4222:     my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68      albertel 4223:     return $names{'screenname'};
1.62      www      4224: }
                   4225: 
1.212     albertel 4226: 
1.802     bisitz   4227: # ------------------------------------------------------------- Confirm Wrapper
                   4228: =pod
                   4229: 
1.1142    raeburn  4230: =item * &confirmwrapper($message)
1.802     bisitz   4231: 
                   4232: Wrap messages about completion of operation in box
                   4233: 
                   4234: =cut
                   4235: 
                   4236: sub confirmwrapper {
                   4237:     my ($message)=@_;
                   4238:     if ($message) {
                   4239:         return "\n".'<div class="LC_confirm_box">'."\n"
                   4240:                .$message."\n"
                   4241:                .'</div>'."\n";
                   4242:     } else {
                   4243:         return $message;
                   4244:     }
                   4245: }
                   4246: 
1.62      www      4247: # ------------------------------------------------------------- Message Wrapper
                   4248: 
                   4249: sub messagewrapper {
1.369     www      4250:     my ($link,$username,$domain,$subject,$text)=@_;
1.62      www      4251:     return 
1.441     albertel 4252:         '<a href="/adm/email?compose=individual&amp;'.
                   4253:         'recname='.$username.'&amp;recdom='.$domain.
                   4254: 	'&amp;subject='.&escape($subject).'&amp;text='.&escape($text).'" '.
1.200     matthew  4255:         'title="'.&mt('Send message').'">'.$link.'</a>';
1.74      www      4256: }
1.802     bisitz   4257: 
1.74      www      4258: # --------------------------------------------------------------- Notes Wrapper
                   4259: 
                   4260: sub noteswrapper {
                   4261:     my ($link,$un,$do)=@_;
                   4262:     return 
1.896     amueller 4263: "<a href='/adm/email?recordftf=retrieve&amp;recname=$un&amp;recdom=$do'>$link</a>";
1.62      www      4264: }
1.802     bisitz   4265: 
1.62      www      4266: # ------------------------------------------------------------- Aboutme Wrapper
                   4267: 
                   4268: sub aboutmewrapper {
1.1070    raeburn  4269:     my ($link,$username,$domain,$target,$class)=@_;
1.447     raeburn  4270:     if (!defined($username)  && !defined($domain)) {
                   4271:         return;
                   4272:     }
1.1096    raeburn  4273:     return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.1070    raeburn  4274: 	($target?' target="'.$target.'"':'').($class?' class="'.$class.'"':'').' title="'.&mt("View this user's personal information page").'">'.$link.'</a>';
1.62      www      4275: }
                   4276: 
                   4277: # ------------------------------------------------------------ Syllabus Wrapper
                   4278: 
                   4279: sub syllabuswrapper {
1.707     bisitz   4280:     my ($linktext,$coursedir,$domain)=@_;
1.208     matthew  4281:     return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61      www      4282: }
1.14      harris41 4283: 
1.802     bisitz   4284: # -----------------------------------------------------------------------------
                   4285: 
1.208     matthew  4286: sub track_student_link {
1.887     raeburn  4287:     my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
1.268     albertel 4288:     my $link ="/adm/trackstudent?";
1.208     matthew  4289:     my $title = 'View recent activity';
                   4290:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   4291:         defined($sdom)  && $sdom  !~ /^\s*$/) {
1.268     albertel 4292:         $link .= "selected_student=$sname:$sdom";
1.208     matthew  4293:         $title .= ' of this student';
1.268     albertel 4294:     } 
1.208     matthew  4295:     if (defined($target) && $target !~ /^\s*$/) {
                   4296:         $target = qq{target="$target"};
                   4297:     } else {
                   4298:         $target = '';
                   4299:     }
1.268     albertel 4300:     if ($start) { $link.='&amp;start='.$start; }
1.887     raeburn  4301:     if ($only_body) { $link .= '&amp;only_body=1'; }
1.554     albertel 4302:     $title = &mt($title);
                   4303:     $linktext = &mt($linktext);
1.448     albertel 4304:     return qq{<a href="$link" title="$title" $target>$linktext</a>}.
                   4305: 	&help_open_topic('View_recent_activity');
1.208     matthew  4306: }
                   4307: 
1.781     raeburn  4308: sub slot_reservations_link {
                   4309:     my ($linktext,$sname,$sdom,$target) = @_;
                   4310:     my $link ="/adm/slotrequest?command=showresv&amp;origin=aboutme";
                   4311:     my $title = 'View slot reservation history';
                   4312:     if (defined($sname) && $sname !~ /^\s*$/ &&
                   4313:         defined($sdom)  && $sdom  !~ /^\s*$/) {
                   4314:         $link .= "&amp;uname=$sname&amp;udom=$sdom";
                   4315:         $title .= ' of this student';
                   4316:     }
                   4317:     if (defined($target) && $target !~ /^\s*$/) {
                   4318:         $target = qq{target="$target"};
                   4319:     } else {
                   4320:         $target = '';
                   4321:     }
                   4322:     $title = &mt($title);
                   4323:     $linktext = &mt($linktext);
                   4324:     return qq{<a href="$link" title="$title" $target>$linktext</a>};
                   4325: # FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
                   4326: 
                   4327: }
                   4328: 
1.508     www      4329: # ===================================================== Display a student photo
                   4330: 
                   4331: 
1.509     albertel 4332: sub student_image_tag {
1.508     www      4333:     my ($domain,$user)=@_;
                   4334:     my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
                   4335:     if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
                   4336: 	return '<img src="'.$imgsrc.'" align="right" />';
                   4337:     } else {
                   4338: 	return '';
                   4339:     }
                   4340: }
                   4341: 
1.112     bowersj2 4342: =pod
                   4343: 
                   4344: =back
                   4345: 
                   4346: =head1 Access .tab File Data
                   4347: 
                   4348: =over 4
                   4349: 
1.648     raeburn  4350: =item * &languageids() 
1.112     bowersj2 4351: 
                   4352: returns list of all language ids
                   4353: 
                   4354: =cut
                   4355: 
1.14      harris41 4356: sub languageids {
1.16      harris41 4357:     return sort(keys(%language));
1.14      harris41 4358: }
                   4359: 
1.112     bowersj2 4360: =pod
                   4361: 
1.648     raeburn  4362: =item * &languagedescription() 
1.112     bowersj2 4363: 
                   4364: returns description of a specified language id
                   4365: 
                   4366: =cut
                   4367: 
1.14      harris41 4368: sub languagedescription {
1.125     www      4369:     my $code=shift;
                   4370:     return  ($supported_language{$code}?'* ':'').
                   4371:             $language{$code}.
1.126     www      4372: 	    ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145     www      4373: }
                   4374: 
1.1048    foxr     4375: =pod
                   4376: 
                   4377: =item * &plainlanguagedescription
                   4378: 
                   4379: Returns both the plain language description (e.g. 'Creoles and Pidgins, English-based (Other)')
                   4380: and the language character encoding (e.g. ISO) separated by a ' - ' string.
                   4381: 
                   4382: =cut
                   4383: 
1.145     www      4384: sub plainlanguagedescription {
                   4385:     my $code=shift;
                   4386:     return $language{$code};
                   4387: }
                   4388: 
1.1048    foxr     4389: =pod
                   4390: 
                   4391: =item * &supportedlanguagecode
                   4392: 
                   4393: Returns the supported language code (e.g. sptutf maps to pt) given a language
                   4394: code.
                   4395: 
                   4396: =cut
                   4397: 
1.145     www      4398: sub supportedlanguagecode {
                   4399:     my $code=shift;
                   4400:     return $supported_language{$code};
1.97      www      4401: }
                   4402: 
1.112     bowersj2 4403: =pod
                   4404: 
1.1048    foxr     4405: =item * &latexlanguage()
                   4406: 
                   4407: Given a language key code returns the correspondnig language to use
                   4408: to select the correct hyphenation on LaTeX printouts.  This is undef if there
                   4409: is no supported hyphenation for the language code.
                   4410: 
                   4411: =cut
                   4412: 
                   4413: sub latexlanguage {
                   4414:     my $code = shift;
                   4415:     return $latex_language{$code};
                   4416: }
                   4417: 
                   4418: =pod
                   4419: 
                   4420: =item * &latexhyphenation()
                   4421: 
                   4422: Same as above but what's supplied is the language as it might be stored
                   4423: in the metadata.
                   4424: 
                   4425: =cut
                   4426: 
                   4427: sub latexhyphenation {
                   4428:     my $key = shift;
                   4429:     return $latex_language_bykey{$key};
                   4430: }
                   4431: 
                   4432: =pod
                   4433: 
1.648     raeburn  4434: =item * &copyrightids() 
1.112     bowersj2 4435: 
                   4436: returns list of all copyrights
                   4437: 
                   4438: =cut
                   4439: 
                   4440: sub copyrightids {
                   4441:     return sort(keys(%cprtag));
                   4442: }
                   4443: 
                   4444: =pod
                   4445: 
1.648     raeburn  4446: =item * &copyrightdescription() 
1.112     bowersj2 4447: 
                   4448: returns description of a specified copyright id
                   4449: 
                   4450: =cut
                   4451: 
                   4452: sub copyrightdescription {
1.166     www      4453:     return &mt($cprtag{shift(@_)});
1.112     bowersj2 4454: }
1.197     matthew  4455: 
                   4456: =pod
                   4457: 
1.648     raeburn  4458: =item * &source_copyrightids() 
1.192     taceyjo1 4459: 
                   4460: returns list of all source copyrights
                   4461: 
                   4462: =cut
                   4463: 
                   4464: sub source_copyrightids {
                   4465:     return sort(keys(%scprtag));
                   4466: }
                   4467: 
                   4468: =pod
                   4469: 
1.648     raeburn  4470: =item * &source_copyrightdescription() 
1.192     taceyjo1 4471: 
                   4472: returns description of a specified source copyright id
                   4473: 
                   4474: =cut
                   4475: 
                   4476: sub source_copyrightdescription {
                   4477:     return &mt($scprtag{shift(@_)});
                   4478: }
1.112     bowersj2 4479: 
                   4480: =pod
                   4481: 
1.648     raeburn  4482: =item * &filecategories() 
1.112     bowersj2 4483: 
                   4484: returns list of all file categories
                   4485: 
                   4486: =cut
                   4487: 
                   4488: sub filecategories {
                   4489:     return sort(keys(%category_extensions));
                   4490: }
                   4491: 
                   4492: =pod
                   4493: 
1.648     raeburn  4494: =item * &filecategorytypes() 
1.112     bowersj2 4495: 
                   4496: returns list of file types belonging to a given file
                   4497: category
                   4498: 
                   4499: =cut
                   4500: 
                   4501: sub filecategorytypes {
1.356     albertel 4502:     my ($cat) = @_;
1.1248    raeburn  4503:     if (ref($category_extensions{lc($cat)}) eq 'ARRAY') { 
                   4504:         return @{$category_extensions{lc($cat)}};
                   4505:     } else {
                   4506:         return ();
                   4507:     }
1.112     bowersj2 4508: }
                   4509: 
                   4510: =pod
                   4511: 
1.648     raeburn  4512: =item * &fileembstyle() 
1.112     bowersj2 4513: 
                   4514: returns embedding style for a specified file type
                   4515: 
                   4516: =cut
                   4517: 
                   4518: sub fileembstyle {
                   4519:     return $fe{lc(shift(@_))};
1.169     www      4520: }
                   4521: 
1.351     www      4522: sub filemimetype {
                   4523:     return $fm{lc(shift(@_))};
                   4524: }
                   4525: 
1.169     www      4526: 
                   4527: sub filecategoryselect {
                   4528:     my ($name,$value)=@_;
1.189     matthew  4529:     return &select_form($value,$name,
1.970     raeburn  4530:                         {'' => &mt('Any category'), map { $_,$_ } sort(keys(%category_extensions))});
1.112     bowersj2 4531: }
                   4532: 
                   4533: =pod
                   4534: 
1.648     raeburn  4535: =item * &filedescription() 
1.112     bowersj2 4536: 
                   4537: returns description for a specified file type
                   4538: 
                   4539: =cut
                   4540: 
                   4541: sub filedescription {
1.188     matthew  4542:     my $file_description = $fd{lc(shift())};
                   4543:     $file_description =~ s:([\[\]]):~$1:g;
                   4544:     return &mt($file_description);
1.112     bowersj2 4545: }
                   4546: 
                   4547: =pod
                   4548: 
1.648     raeburn  4549: =item * &filedescriptionex() 
1.112     bowersj2 4550: 
                   4551: returns description for a specified file type with
                   4552: extra formatting
                   4553: 
                   4554: =cut
                   4555: 
                   4556: sub filedescriptionex {
                   4557:     my $ex=shift;
1.188     matthew  4558:     my $file_description = $fd{lc($ex)};
                   4559:     $file_description =~ s:([\[\]]):~$1:g;
                   4560:     return '.'.$ex.' '.&mt($file_description);
1.112     bowersj2 4561: }
                   4562: 
                   4563: # End of .tab access
                   4564: =pod
                   4565: 
                   4566: =back
                   4567: 
                   4568: =cut
                   4569: 
                   4570: # ------------------------------------------------------------------ File Types
                   4571: sub fileextensions {
                   4572:     return sort(keys(%fe));
                   4573: }
                   4574: 
1.97      www      4575: # ----------------------------------------------------------- Display Languages
                   4576: # returns a hash with all desired display languages
                   4577: #
                   4578: 
                   4579: sub display_languages {
                   4580:     my %languages=();
1.695     raeburn  4581:     foreach my $lang (&Apache::lonlocal::preferred_languages()) {
1.356     albertel 4582: 	$languages{$lang}=1;
1.97      www      4583:     }
                   4584:     &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258     albertel 4585:     if ($env{'form.displaylanguage'}) {
1.356     albertel 4586: 	foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
                   4587: 	    $languages{$lang}=1;
1.97      www      4588:         }
                   4589:     }
                   4590:     return %languages;
1.14      harris41 4591: }
                   4592: 
1.582     albertel 4593: sub languages {
                   4594:     my ($possible_langs) = @_;
1.695     raeburn  4595:     my @preferred_langs = &Apache::lonlocal::preferred_languages();
1.582     albertel 4596:     if (!ref($possible_langs)) {
                   4597: 	if( wantarray ) {
                   4598: 	    return @preferred_langs;
                   4599: 	} else {
                   4600: 	    return $preferred_langs[0];
                   4601: 	}
                   4602:     }
                   4603:     my %possibilities = map { $_ => 1 } (@$possible_langs);
                   4604:     my @preferred_possibilities;
                   4605:     foreach my $preferred_lang (@preferred_langs) {
                   4606: 	if (exists($possibilities{$preferred_lang})) {
                   4607: 	    push(@preferred_possibilities, $preferred_lang);
                   4608: 	}
                   4609:     }
                   4610:     if( wantarray ) {
                   4611: 	return @preferred_possibilities;
                   4612:     }
                   4613:     return $preferred_possibilities[0];
                   4614: }
                   4615: 
1.742     raeburn  4616: sub user_lang {
                   4617:     my ($touname,$toudom,$fromcid) = @_;
                   4618:     my @userlangs;
                   4619:     if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
                   4620:         @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
                   4621:                     $env{'course.'.$fromcid.'.languages'}));
                   4622:     } else {
                   4623:         my %langhash = &getlangs($touname,$toudom);
                   4624:         if ($langhash{'languages'} ne '') {
                   4625:             @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
                   4626:         } else {
                   4627:             my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
                   4628:             if ($domdefs{'lang_def'} ne '') {
                   4629:                 @userlangs = ($domdefs{'lang_def'});
                   4630:             }
                   4631:         }
                   4632:     }
                   4633:     my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
                   4634:     my $user_lh = Apache::localize->get_handle(@languages);
                   4635:     return $user_lh;
                   4636: }
                   4637: 
                   4638: 
1.112     bowersj2 4639: ###############################################################
                   4640: ##               Student Answer Attempts                     ##
                   4641: ###############################################################
                   4642: 
                   4643: =pod
                   4644: 
                   4645: =head1 Alternate Problem Views
                   4646: 
                   4647: =over 4
                   4648: 
1.648     raeburn  4649: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.1199    raeburn  4650:     $getattempt, $regexp, $gradesub, $usec, $identifier)
1.112     bowersj2 4651: 
                   4652: Return string with previous attempt on problem. Arguments:
                   4653: 
                   4654: =over 4
                   4655: 
                   4656: =item * $symb: Problem, including path
                   4657: 
                   4658: =item * $username: username of the desired student
                   4659: 
                   4660: =item * $domain: domain of the desired student
1.14      harris41 4661: 
1.112     bowersj2 4662: =item * $course: Course ID
1.14      harris41 4663: 
1.112     bowersj2 4664: =item * $getattempt: Leave blank for all attempts, otherwise put
                   4665:     something
1.14      harris41 4666: 
1.112     bowersj2 4667: =item * $regexp: if string matches this regexp, the string will be
                   4668:     sent to $gradesub
1.14      harris41 4669: 
1.112     bowersj2 4670: =item * $gradesub: routine that processes the string if it matches $regexp
1.14      harris41 4671: 
1.1199    raeburn  4672: =item * $usec: section of the desired student
                   4673: 
                   4674: =item * $identifier: counter for student (multiple students one problem) or 
                   4675:     problem (one student; whole sequence).
                   4676: 
1.112     bowersj2 4677: =back
1.14      harris41 4678: 
1.112     bowersj2 4679: The output string is a table containing all desired attempts, if any.
1.16      harris41 4680: 
1.112     bowersj2 4681: =cut
1.1       albertel 4682: 
                   4683: sub get_previous_attempt {
1.1199    raeburn  4684:   my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub,$usec,$identifier)=@_;
1.1       albertel 4685:   my $prevattempts='';
1.43      ng       4686:   no strict 'refs';
1.1       albertel 4687:   if ($symb) {
1.3       albertel 4688:     my (%returnhash)=
                   4689:       &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1       albertel 4690:     if ($returnhash{'version'}) {
                   4691:       my %lasthash=();
                   4692:       my $version;
                   4693:       for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1212    raeburn  4694:         foreach my $key (reverse(sort(split(/\:/,$returnhash{$version.':keys'})))) {
                   4695:             if ($key =~ /\.rawrndseed$/) {
                   4696:                 my ($id) = ($key =~ /^(.+)\.rawrndseed$/);
                   4697:                 $lasthash{$id.'.rndseed'} = $returnhash{$version.':'.$key};
                   4698:             } else {
                   4699:                 $lasthash{$key}=$returnhash{$version.':'.$key};
                   4700:             }
1.19      harris41 4701:         }
1.1       albertel 4702:       }
1.596     albertel 4703:       $prevattempts=&start_data_table().&start_data_table_header_row();
                   4704:       $prevattempts.='<th>'.&mt('History').'</th>';
1.1199    raeburn  4705:       my (%typeparts,%lasthidden,%regraded,%hidestatus);
1.945     raeburn  4706:       my $showsurv=&Apache::lonnet::allowed('vas',$env{'request.course.id'});
1.356     albertel 4707:       foreach my $key (sort(keys(%lasthash))) {
                   4708: 	my ($ign,@parts) = split(/\./,$key);
1.41      ng       4709: 	if ($#parts > 0) {
1.31      albertel 4710: 	  my $data=$parts[-1];
1.989     raeburn  4711:           next if ($data eq 'foilorder');
1.31      albertel 4712: 	  pop(@parts);
1.1010    www      4713:           $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.'&nbsp;</th>';
1.945     raeburn  4714:           if ($data eq 'type') {
                   4715:               unless ($showsurv) {
                   4716:                   my $id = join(',',@parts);
                   4717:                   $typeparts{$ign.'.'.$id} = $lasthash{$key};
1.978     raeburn  4718:                   if (($lasthash{$key} eq 'anonsurvey') || ($lasthash{$key} eq 'anonsurveycred')) {
                   4719:                       $lasthidden{$ign.'.'.$id} = 1;
                   4720:                   }
1.945     raeburn  4721:               }
1.1199    raeburn  4722:               if ($identifier ne '') {
                   4723:                   my $id = join(',',@parts);
                   4724:                   if (&Apache::lonnet::EXT("resource.$id.problemstatus",$symb,
                   4725:                                                $domain,$username,$usec,undef,$course) =~ /^no/) {
                   4726:                       $hidestatus{$ign.'.'.$id} = 1;
                   4727:                   }
                   4728:               }
                   4729:           } elsif ($data eq 'regrader') {
                   4730:               if (($identifier ne '') && (@parts)) {
1.1200    raeburn  4731:                   my $id = join(',',@parts);
                   4732:                   $regraded{$ign.'.'.$id} = 1;
1.1199    raeburn  4733:               }
1.1010    www      4734:           } 
1.31      albertel 4735: 	} else {
1.41      ng       4736: 	  if ($#parts == 0) {
                   4737: 	    $prevattempts.='<th>'.$parts[0].'</th>';
                   4738: 	  } else {
                   4739: 	    $prevattempts.='<th>'.$ign.'</th>';
                   4740: 	  }
1.31      albertel 4741: 	}
1.16      harris41 4742:       }
1.596     albertel 4743:       $prevattempts.=&end_data_table_header_row();
1.40      ng       4744:       if ($getattempt eq '') {
1.1199    raeburn  4745:         my (%solved,%resets,%probstatus);
1.1200    raeburn  4746:         if (($identifier ne '') && (keys(%regraded) > 0)) {
                   4747:             for ($version=1;$version<=$returnhash{'version'};$version++) {
                   4748:                 foreach my $id (keys(%regraded)) {
                   4749:                     if (($returnhash{$version.':'.$id.'.regrader'}) &&
                   4750:                         ($returnhash{$version.':'.$id.'.tries'} eq '') &&
                   4751:                         ($returnhash{$version.':'.$id.'.award'} eq '')) {
                   4752:                         push(@{$resets{$id}},$version);
1.1199    raeburn  4753:                     }
                   4754:                 }
                   4755:             }
1.1200    raeburn  4756:         }
                   4757: 	for ($version=1;$version<=$returnhash{'version'};$version++) {
1.1199    raeburn  4758:             my (@hidden,@unsolved);
1.945     raeburn  4759:             if (%typeparts) {
                   4760:                 foreach my $id (keys(%typeparts)) {
1.1199    raeburn  4761:                     if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') || 
                   4762:                         ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {
1.945     raeburn  4763:                         push(@hidden,$id);
1.1199    raeburn  4764:                     } elsif ($identifier ne '') {
                   4765:                         unless (($returnhash{$version.':'.$id.'.type'} eq 'survey') ||
                   4766:                                 ($returnhash{$version.':'.$id.'.type'} eq 'surveycred') ||
                   4767:                                 ($hidestatus{$id})) {
1.1200    raeburn  4768:                             next if ((ref($resets{$id}) eq 'ARRAY') && grep(/^\Q$version\E$/,@{$resets{$id}}));
1.1199    raeburn  4769:                             if ($returnhash{$version.':'.$id.'.solved'} eq 'correct_by_student') {
                   4770:                                 push(@{$solved{$id}},$version);
                   4771:                             } elsif (($returnhash{$version.':'.$id.'.solved'} ne '') &&
                   4772:                                      (ref($solved{$id}) eq 'ARRAY')) {
                   4773:                                 my $skip;
                   4774:                                 if (ref($resets{$id}) eq 'ARRAY') {
                   4775:                                     foreach my $reset (@{$resets{$id}}) {
                   4776:                                         if ($reset > $solved{$id}[-1]) {
                   4777:                                             $skip=1;
                   4778:                                             last;
                   4779:                                         }
                   4780:                                     }
                   4781:                                 }
                   4782:                                 unless ($skip) {
                   4783:                                     my ($ign,$partslist) = split(/\./,$id,2);
                   4784:                                     push(@unsolved,$partslist);
                   4785:                                 }
                   4786:                             }
                   4787:                         }
1.945     raeburn  4788:                     }
                   4789:                 }
                   4790:             }
                   4791:             $prevattempts.=&start_data_table_row().
1.1199    raeburn  4792:                            '<td>'.&mt('Transaction [_1]',$version);
                   4793:             if (@unsolved) {
                   4794:                 $prevattempts .= '<span class="LC_nobreak"><label>'.
                   4795:                                  '<input type="checkbox" name="HIDE'.$identifier.'" value="'.$version.':'.join('_',@unsolved).'" />'.
                   4796:                                  &mt('Hide').'</label></span>';
                   4797:             }
                   4798:             $prevattempts .= '</td>';
1.945     raeburn  4799:             if (@hidden) {
                   4800:                 foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4801:                     next if ($key =~ /\.foilorder$/);
1.945     raeburn  4802:                     my $hide;
                   4803:                     foreach my $id (@hidden) {
                   4804:                         if ($key =~ /^\Q$id\E/) {
                   4805:                             $hide = 1;
                   4806:                             last;
                   4807:                         }
                   4808:                     }
                   4809:                     if ($hide) {
                   4810:                         my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
                   4811:                         if (($data eq 'award') || ($data eq 'awarddetail')) {
                   4812:                             my $value = &format_previous_attempt_value($key,
                   4813:                                              $returnhash{$version.':'.$key});
1.1173    kruse    4814:                             $prevattempts.='<td>'.$value.'&nbsp;</td>';
1.945     raeburn  4815:                         } else {
                   4816:                             $prevattempts.='<td>&nbsp;</td>';
                   4817:                         }
                   4818:                     } else {
                   4819:                         if ($key =~ /\./) {
1.1212    raeburn  4820:                             my $value = $returnhash{$version.':'.$key};
                   4821:                             if ($key =~ /\.rndseed$/) {
                   4822:                                 my ($id) = ($key =~ /^(.+)\.[^.]+$/);
                   4823:                                 if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
                   4824:                                     $value = $returnhash{$version.':'.$id.'.rawrndseed'};
                   4825:                                 }
                   4826:                             }
                   4827:                             $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
                   4828:                                            '&nbsp;</td>';
1.945     raeburn  4829:                         } else {
                   4830:                             $prevattempts.='<td>&nbsp;</td>';
                   4831:                         }
                   4832:                     }
                   4833:                 }
                   4834:             } else {
                   4835: 	        foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4836:                     next if ($key =~ /\.foilorder$/);
1.1212    raeburn  4837:                     my $value = $returnhash{$version.':'.$key};
                   4838:                     if ($key =~ /\.rndseed$/) {
                   4839:                         my ($id) = ($key =~ /^(.+)\.[^.]+$/);
                   4840:                         if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
                   4841:                             $value = $returnhash{$version.':'.$id.'.rawrndseed'};
                   4842:                         }
                   4843:                     }
                   4844:                     $prevattempts.='<td>'.&format_previous_attempt_value($key,$value).
                   4845:                                    '&nbsp;</td>';
1.945     raeburn  4846: 	        }
                   4847:             }
                   4848: 	    $prevattempts.=&end_data_table_row();
1.40      ng       4849: 	 }
1.1       albertel 4850:       }
1.945     raeburn  4851:       my @currhidden = keys(%lasthidden);
1.596     albertel 4852:       $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356     albertel 4853:       foreach my $key (sort(keys(%lasthash))) {
1.989     raeburn  4854:           next if ($key =~ /\.foilorder$/);
1.945     raeburn  4855:           if (%typeparts) {
                   4856:               my $hidden;
                   4857:               foreach my $id (@currhidden) {
                   4858:                   if ($key =~ /^\Q$id\E/) {
                   4859:                       $hidden = 1;
                   4860:                       last;
                   4861:                   }
                   4862:               }
                   4863:               if ($hidden) {
                   4864:                   my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
                   4865:                   if (($data eq 'award') || ($data eq 'awarddetail')) {
                   4866:                       my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4867:                       if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4868:                           $value = &$gradesub($value);
                   4869:                       }
1.1173    kruse    4870:                       $prevattempts.='<td>'. $value.'&nbsp;</td>';
1.945     raeburn  4871:                   } else {
                   4872:                       $prevattempts.='<td>&nbsp;</td>';
                   4873:                   }
                   4874:               } else {
                   4875:                   my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4876:                   if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4877:                       $value = &$gradesub($value);
                   4878:                   }
1.1173    kruse    4879:                   $prevattempts.='<td>'.$value.'&nbsp;</td>';
1.945     raeburn  4880:               }
                   4881:           } else {
                   4882: 	      my $value = &format_previous_attempt_value($key,$lasthash{$key});
                   4883: 	      if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   4884:                   $value = &$gradesub($value);
                   4885:               }
1.1173    kruse    4886: 	     $prevattempts.='<td>'.$value.'&nbsp;</td>';
1.945     raeburn  4887:           }
1.16      harris41 4888:       }
1.596     albertel 4889:       $prevattempts.= &end_data_table_row().&end_data_table();
1.1       albertel 4890:     } else {
1.1305    raeburn  4891:       my $msg;
                   4892:       if ($symb =~ /ext\.tool$/) {
                   4893:           $msg = &mt('No grade passed back.');
                   4894:       } else {
                   4895:           $msg = &mt('Nothing submitted - no attempts.');
                   4896:       }
1.596     albertel 4897:       $prevattempts=
                   4898: 	  &start_data_table().&start_data_table_row().
1.1305    raeburn  4899: 	  '<td>'.$msg.'</td>'.
1.596     albertel 4900: 	  &end_data_table_row().&end_data_table();
1.1       albertel 4901:     }
                   4902:   } else {
1.596     albertel 4903:     $prevattempts=
                   4904: 	  &start_data_table().&start_data_table_row().
                   4905: 	  '<td>'.&mt('No data.').'</td>'.
                   4906: 	  &end_data_table_row().&end_data_table();
1.1       albertel 4907:   }
1.10      albertel 4908: }
                   4909: 
1.581     albertel 4910: sub format_previous_attempt_value {
                   4911:     my ($key,$value) = @_;
1.1011    www      4912:     if (($key =~ /timestamp/) || ($key=~/duedate/)) {
1.1173    kruse    4913:         $value = &Apache::lonlocal::locallocaltime($value);
1.581     albertel 4914:     } elsif (ref($value) eq 'ARRAY') {
1.1173    kruse    4915:         $value = &HTML::Entities::encode('('.join(', ', @{ $value }).')','"<>&');
1.988     raeburn  4916:     } elsif ($key =~ /answerstring$/) {
                   4917:         my %answers = &Apache::lonnet::str2hash($value);
1.1173    kruse    4918:         my @answer = %answers;
                   4919:         %answers = map {&HTML::Entities::encode($_, '"<>&')} @answer;
1.988     raeburn  4920:         my @anskeys = sort(keys(%answers));
                   4921:         if (@anskeys == 1) {
                   4922:             my $answer = $answers{$anskeys[0]};
1.1001    raeburn  4923:             if ($answer =~ m{\0}) {
                   4924:                 $answer =~ s{\0}{,}g;
1.988     raeburn  4925:             }
                   4926:             my $tag_internal_answer_name = 'INTERNAL';
                   4927:             if ($anskeys[0] eq $tag_internal_answer_name) {
                   4928:                 $value = $answer; 
                   4929:             } else {
                   4930:                 $value = $anskeys[0].'='.$answer;
                   4931:             }
                   4932:         } else {
                   4933:             foreach my $ans (@anskeys) {
                   4934:                 my $answer = $answers{$ans};
1.1001    raeburn  4935:                 if ($answer =~ m{\0}) {
                   4936:                     $answer =~ s{\0}{,}g;
1.988     raeburn  4937:                 }
                   4938:                 $value .=  $ans.'='.$answer.'<br />';;
                   4939:             } 
                   4940:         }
1.581     albertel 4941:     } else {
1.1173    kruse    4942:         $value = &HTML::Entities::encode(&unescape($value), '"<>&');
1.581     albertel 4943:     }
                   4944:     return $value;
                   4945: }
                   4946: 
                   4947: 
1.107     albertel 4948: sub relative_to_absolute {
                   4949:     my ($url,$output)=@_;
                   4950:     my $parser=HTML::TokeParser->new(\$output);
                   4951:     my $token;
                   4952:     my $thisdir=$url;
                   4953:     my @rlinks=();
                   4954:     while ($token=$parser->get_token) {
                   4955: 	if ($token->[0] eq 'S') {
                   4956: 	    if ($token->[1] eq 'a') {
                   4957: 		if ($token->[2]->{'href'}) {
                   4958: 		    $rlinks[$#rlinks+1]=$token->[2]->{'href'};
                   4959: 		}
                   4960: 	    } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
                   4961: 		$rlinks[$#rlinks+1]=$token->[2]->{'src'};
                   4962: 	    } elsif ($token->[1] eq 'base') {
                   4963: 		$thisdir=$token->[2]->{'href'};
                   4964: 	    }
                   4965: 	}
                   4966:     }
                   4967:     $thisdir=~s-/[^/]*$--;
1.356     albertel 4968:     foreach my $link (@rlinks) {
1.726     raeburn  4969: 	unless (($link=~/^https?\:\/\//i) ||
1.356     albertel 4970: 		($link=~/^\//) ||
                   4971: 		($link=~/^javascript:/i) ||
                   4972: 		($link=~/^mailto:/i) ||
                   4973: 		($link=~/^\#/)) {
                   4974: 	    my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
                   4975: 	    $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107     albertel 4976: 	}
                   4977:     }
                   4978: # -------------------------------------------------- Deal with Applet codebases
                   4979:     $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
                   4980:     return $output;
                   4981: }
                   4982: 
1.112     bowersj2 4983: =pod
                   4984: 
1.648     raeburn  4985: =item * &get_student_view()
1.112     bowersj2 4986: 
                   4987: show a snapshot of what student was looking at
                   4988: 
                   4989: =cut
                   4990: 
1.10      albertel 4991: sub get_student_view {
1.186     albertel 4992:   my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114     www      4993:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 4994:   my (%form);
1.10      albertel 4995:   my @elements=('symb','courseid','domain','username');
                   4996:   foreach my $element (@elements) {
1.186     albertel 4997:       $form{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 4998:   }
1.186     albertel 4999:   if (defined($moreenv)) {
                   5000:       %form=(%form,%{$moreenv});
                   5001:   }
1.236     albertel 5002:   if (defined($target)) { $form{'grade_target'} = $target; }
1.107     albertel 5003:   $feedurl=&Apache::lonnet::clutter($feedurl);
1.1306    raeburn  5004:   if (($feedurl =~ /ext\.tool$/) && ($target eq 'tex')) {
                   5005:       $feedurl =~ s{^/adm/wrapper}{};
                   5006:   }
1.650     www      5007:   my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11      albertel 5008:   $userview=~s/\<body[^\>]*\>//gi;
                   5009:   $userview=~s/\<\/body\>//gi;
                   5010:   $userview=~s/\<html\>//gi;
                   5011:   $userview=~s/\<\/html\>//gi;
                   5012:   $userview=~s/\<head\>//gi;
                   5013:   $userview=~s/\<\/head\>//gi;
                   5014:   $userview=~s/action\s*\=/would_be_action\=/gi;
1.107     albertel 5015:   $userview=&relative_to_absolute($feedurl,$userview);
1.650     www      5016:   if (wantarray) {
                   5017:      return ($userview,$response);
                   5018:   } else {
                   5019:      return $userview;
                   5020:   }
                   5021: }
                   5022: 
                   5023: sub get_student_view_with_retries {
                   5024:   my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
                   5025: 
                   5026:     my $ok = 0;                 # True if we got a good response.
                   5027:     my $content;
                   5028:     my $response;
                   5029: 
                   5030:     # Try to get the student_view done. within the retries count:
                   5031:     
                   5032:     do {
                   5033:          ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
                   5034:          $ok      = $response->is_success;
                   5035:          if (!$ok) {
                   5036:             &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
                   5037:          }
                   5038:          $retries--;
                   5039:     } while (!$ok && ($retries > 0));
                   5040:     
                   5041:     if (!$ok) {
                   5042:        $content = '';          # On error return an empty content.
                   5043:     }
1.651     www      5044:     if (wantarray) {
                   5045:        return ($content, $response);
                   5046:     } else {
                   5047:        return $content;
                   5048:     }
1.11      albertel 5049: }
                   5050: 
1.1349    raeburn  5051: sub css_links {
                   5052:     my ($currsymb,$level) = @_;
                   5053:     my ($links,@symbs,%cssrefs,%httpref);
                   5054:     if ($level eq 'map') {
                   5055:         my $navmap = Apache::lonnavmaps::navmap->new();
                   5056:         if (ref($navmap)) {
                   5057:             my ($map,undef,$url)=&Apache::lonnet::decode_symb($currsymb);
                   5058:             my @resources = $navmap->retrieveResources($map,sub { $_[0]->is_problem() },0,0);
                   5059:             foreach my $res (@resources) {
                   5060:                 if (ref($res) && $res->symb()) {
                   5061:                     push(@symbs,$res->symb());
                   5062:                 }
                   5063:             }
                   5064:         }
                   5065:     } else {
                   5066:         @symbs = ($currsymb);
                   5067:     }
                   5068:     foreach my $symb (@symbs) {
                   5069:         my $css_href = &Apache::lonnet::EXT('resource.0.cssfile',$symb);
                   5070:         if ($css_href =~ /\S/) {
                   5071:             unless ($css_href =~ m{https?://}) {
                   5072:                 my $url = (&Apache::lonnet::decode_symb($symb))[-1];
                   5073:                 my $proburl =  &Apache::lonnet::clutter($url);
                   5074:                 my ($probdir) = ($proburl =~ m{(.+)/[^/]+$});
                   5075:                 unless ($css_href =~ m{^/}) {
                   5076:                     $css_href = &Apache::lonnet::hreflocation($probdir,$css_href);
                   5077:                 }
                   5078:                 if ($css_href =~ m{^/(res|uploaded)/}) {
                   5079:                     unless (($httpref{'httpref.'.$css_href}) ||
                   5080:                             (&Apache::lonnet::is_on_map($css_href))) {
                   5081:                         my $thisurl = $proburl;
                   5082:                         if ($env{'httpref.'.$proburl}) {
                   5083:                             $thisurl = $env{'httpref.'.$proburl};
                   5084:                         }
                   5085:                         $httpref{'httpref.'.$css_href} = $thisurl;
                   5086:                     }
                   5087:                 }
                   5088:             }
                   5089:             $cssrefs{$css_href} = 1;
                   5090:         }
                   5091:     }
                   5092:     if (keys(%httpref)) {
                   5093:         &Apache::lonnet::appenv(\%httpref);
                   5094:     }
                   5095:     if (keys(%cssrefs)) {
                   5096:         foreach my $css_href (keys(%cssrefs)) {
                   5097:             next unless ($css_href =~ m{^(/res/|/uploaded/|https?://)});
                   5098:             $links .= '<link rel="stylesheet" type="text/css" href="'.$css_href.'" />'."\n";
                   5099:         }
                   5100:     }
                   5101:     return $links;
                   5102: }
                   5103: 
1.112     bowersj2 5104: =pod
                   5105: 
1.648     raeburn  5106: =item * &get_student_answers() 
1.112     bowersj2 5107: 
                   5108: show a snapshot of how student was answering problem
                   5109: 
                   5110: =cut
                   5111: 
1.11      albertel 5112: sub get_student_answers {
1.100     sakharuk 5113:   my ($symb,$username,$domain,$courseid,%form) = @_;
1.114     www      5114:   my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186     albertel 5115:   my (%moreenv);
1.11      albertel 5116:   my @elements=('symb','courseid','domain','username');
                   5117:   foreach my $element (@elements) {
1.186     albertel 5118:     $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10      albertel 5119:   }
1.186     albertel 5120:   $moreenv{'grade_target'}='answer';
                   5121:   %moreenv=(%form,%moreenv);
1.497     raeburn  5122:   $feedurl = &Apache::lonnet::clutter($feedurl);
                   5123:   my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10      albertel 5124:   return $userview;
1.1       albertel 5125: }
1.116     albertel 5126: 
                   5127: =pod
                   5128: 
                   5129: =item * &submlink()
                   5130: 
1.242     albertel 5131: Inputs: $text $uname $udom $symb $target
1.116     albertel 5132: 
                   5133: Returns: A link to grades.pm such as to see the SUBM view of a student
                   5134: 
                   5135: =cut
                   5136: 
                   5137: ###############################################
                   5138: sub submlink {
1.242     albertel 5139:     my ($text,$uname,$udom,$symb,$target)=@_;
1.116     albertel 5140:     if (!($uname && $udom)) {
                   5141: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 5142: 	    &Apache::lonnet::whichuser($symb);
1.116     albertel 5143: 	if (!$symb) { $symb=$cursymb; }
                   5144:     }
1.254     matthew  5145:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      5146:     $symb=&escape($symb);
1.960     bisitz   5147:     if ($target) { $target=" target=\"$target\""; }
                   5148:     return
                   5149:         '<a href="/adm/grades?command=submission'.
                   5150:         '&amp;symb='.$symb.
                   5151:         '&amp;student='.$uname.
                   5152:         '&amp;userdom='.$udom.'"'.
                   5153:         $target.'>'.$text.'</a>';
1.242     albertel 5154: }
                   5155: ##############################################
                   5156: 
                   5157: =pod
                   5158: 
                   5159: =item * &pgrdlink()
                   5160: 
                   5161: Inputs: $text $uname $udom $symb $target
                   5162: 
                   5163: Returns: A link to grades.pm such as to see the PGRD view of a student
                   5164: 
                   5165: =cut
                   5166: 
                   5167: ###############################################
                   5168: sub pgrdlink {
                   5169:     my $link=&submlink(@_);
                   5170:     $link=~s/(&command=submission)/$1&showgrading=yes/;
                   5171:     return $link;
                   5172: }
                   5173: ##############################################
                   5174: 
                   5175: =pod
                   5176: 
                   5177: =item * &pprmlink()
                   5178: 
                   5179: Inputs: $text $uname $udom $symb $target
                   5180: 
                   5181: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283     albertel 5182: student and a specific resource
1.242     albertel 5183: 
                   5184: =cut
                   5185: 
                   5186: ###############################################
                   5187: sub pprmlink {
                   5188:     my ($text,$uname,$udom,$symb,$target)=@_;
                   5189:     if (!($uname && $udom)) {
                   5190: 	(my $cursymb, my $courseid,$udom,$uname)=
1.463     albertel 5191: 	    &Apache::lonnet::whichuser($symb);
1.242     albertel 5192: 	if (!$symb) { $symb=$cursymb; }
                   5193:     }
1.254     matthew  5194:     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369     www      5195:     $symb=&escape($symb);
1.242     albertel 5196:     if ($target) { $target="target=\"$target\""; }
1.595     albertel 5197:     return '<a href="/adm/parmset?command=set&amp;'.
                   5198: 	'symb='.$symb.'&amp;uname='.$uname.
                   5199: 	'&amp;udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116     albertel 5200: }
                   5201: ##############################################
1.37      matthew  5202: 
1.112     bowersj2 5203: =pod
                   5204: 
                   5205: =back
                   5206: 
                   5207: =cut
                   5208: 
1.37      matthew  5209: ###############################################
1.51      www      5210: 
                   5211: 
                   5212: sub timehash {
1.687     raeburn  5213:     my ($thistime) = @_;
                   5214:     my $timezone = &Apache::lonlocal::gettimezone();
                   5215:     my $dt = DateTime->from_epoch(epoch => $thistime)
                   5216:                      ->set_time_zone($timezone);
                   5217:     my $wday = $dt->day_of_week();
                   5218:     if ($wday == 7) { $wday = 0; }
                   5219:     return ( 'second' => $dt->second(),
                   5220:              'minute' => $dt->minute(),
                   5221:              'hour'   => $dt->hour(),
                   5222:              'day'     => $dt->day_of_month(),
                   5223:              'month'   => $dt->month(),
                   5224:              'year'    => $dt->year(),
                   5225:              'weekday' => $wday,
                   5226:              'dayyear' => $dt->day_of_year(),
                   5227:              'dlsav'   => $dt->is_dst() );
1.51      www      5228: }
                   5229: 
1.370     www      5230: sub utc_string {
                   5231:     my ($date)=@_;
1.371     www      5232:     return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370     www      5233: }
                   5234: 
1.51      www      5235: sub maketime {
                   5236:     my %th=@_;
1.687     raeburn  5237:     my ($epoch_time,$timezone,$dt);
                   5238:     $timezone = &Apache::lonlocal::gettimezone();
                   5239:     eval {
                   5240:         $dt = DateTime->new( year   => $th{'year'},
                   5241:                              month  => $th{'month'},
                   5242:                              day    => $th{'day'},
                   5243:                              hour   => $th{'hour'},
                   5244:                              minute => $th{'minute'},
                   5245:                              second => $th{'second'},
                   5246:                              time_zone => $timezone,
                   5247:                          );
                   5248:     };
                   5249:     if (!$@) {
                   5250:         $epoch_time = $dt->epoch;
                   5251:         if ($epoch_time) {
                   5252:             return $epoch_time;
                   5253:         }
                   5254:     }
1.51      www      5255:     return POSIX::mktime(
                   5256:         ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210     www      5257:          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70      www      5258: }
                   5259: 
                   5260: #########################################
1.51      www      5261: 
                   5262: sub findallcourses {
1.482     raeburn  5263:     my ($roles,$uname,$udom) = @_;
1.355     albertel 5264:     my %roles;
                   5265:     if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348     albertel 5266:     my %courses;
1.51      www      5267:     my $now=time;
1.482     raeburn  5268:     if (!defined($uname)) {
                   5269:         $uname = $env{'user.name'};
                   5270:     }
                   5271:     if (!defined($udom)) {
                   5272:         $udom = $env{'user.domain'};
                   5273:     }
                   5274:     if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
1.1073    raeburn  5275:         my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
1.482     raeburn  5276:         if (!%roles) {
                   5277:             %roles = (
                   5278:                        cc => 1,
1.907     raeburn  5279:                        co => 1,
1.482     raeburn  5280:                        in => 1,
                   5281:                        ep => 1,
                   5282:                        ta => 1,
                   5283:                        cr => 1,
                   5284:                        st => 1,
                   5285:              );
                   5286:         }
                   5287:         foreach my $entry (keys(%roleshash)) {
                   5288:             my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
                   5289:             if ($trole =~ /^cr/) { 
                   5290:                 next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
                   5291:             } else {
                   5292:                 next if (!exists($roles{$trole}));
                   5293:             }
                   5294:             if ($tend) {
                   5295:                 next if ($tend < $now);
                   5296:             }
                   5297:             if ($tstart) {
                   5298:                 next if ($tstart > $now);
                   5299:             }
1.1058    raeburn  5300:             my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role);
1.482     raeburn  5301:             (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
1.1058    raeburn  5302:             my $value = $trole.'/'.$cdom.'/';
1.482     raeburn  5303:             if ($secpart eq '') {
                   5304:                 ($cnum,$role) = split(/_/,$cnumpart); 
                   5305:                 $sec = 'none';
1.1058    raeburn  5306:                 $value .= $cnum.'/';
1.482     raeburn  5307:             } else {
                   5308:                 $cnum = $cnumpart;
                   5309:                 ($sec,$role) = split(/_/,$secpart);
1.1058    raeburn  5310:                 $value .= $cnum.'/'.$sec;
                   5311:             }
                   5312:             if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
                   5313:                 unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
                   5314:                     push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
                   5315:                 }
                   5316:             } else {
                   5317:                 @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.490     raeburn  5318:             }
1.482     raeburn  5319:         }
                   5320:     } else {
                   5321:         foreach my $key (keys(%env)) {
1.483     albertel 5322: 	    if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
                   5323:                  $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482     raeburn  5324: 	        my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
                   5325: 	        next if ($role eq 'ca' || $role eq 'aa');
                   5326: 	        next if (%roles && !exists($roles{$role}));
                   5327: 	        my ($starttime,$endtime)=split(/\./,$env{$key});
                   5328:                 my $active=1;
                   5329:                 if ($starttime) {
                   5330: 		    if ($now<$starttime) { $active=0; }
                   5331:                 }
                   5332:                 if ($endtime) {
                   5333:                     if ($now>$endtime) { $active=0; }
                   5334:                 }
                   5335:                 if ($active) {
1.1058    raeburn  5336:                     my $value = $role.'/'.$cdom.'/'.$cnum.'/';
1.482     raeburn  5337:                     if ($sec eq '') {
                   5338:                         $sec = 'none';
1.1058    raeburn  5339:                     } else {
                   5340:                         $value .= $sec;
                   5341:                     }
                   5342:                     if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
                   5343:                         unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
                   5344:                             push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
                   5345:                         }
                   5346:                     } else {
                   5347:                         @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.482     raeburn  5348:                     }
1.474     raeburn  5349:                 }
                   5350:             }
1.51      www      5351:         }
                   5352:     }
1.474     raeburn  5353:     return %courses;
1.51      www      5354: }
1.37      matthew  5355: 
1.54      www      5356: ###############################################
1.474     raeburn  5357: 
                   5358: sub blockcheck {
1.1372    raeburn  5359:     my ($setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_;
                   5360:     unless (($activity eq 'docs') || ($activity eq 'reinit') || ($activity eq 'alert')) {
                   5361:         my ($has_evb,$check_ipaccess);
                   5362:         my $dom = $env{'user.domain'};
                   5363:         if ($env{'request.course.id'}) {
                   5364:             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   5365:             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   5366:             my $checkrole = "cm./$cdom/$cnum";
                   5367:             my $sec = $env{'request.course.sec'};
                   5368:             if ($sec ne '') {
                   5369:                 $checkrole .= "/$sec";
                   5370:             }
                   5371:             if ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
                   5372:                 ($env{'request.role'} !~ /^st/)) {
                   5373:                 $has_evb = 1;
                   5374:             }
                   5375:             unless ($has_evb) {
                   5376:                 if (($activity eq 'printout') || ($activity eq 'grades') || ($activity eq 'search') ||
                   5377:                     ($activity eq 'boards') || ($activity eq 'groups') || ($activity eq 'chat')) {
                   5378:                     if ($udom eq $cdom) {
                   5379:                         $check_ipaccess = 1;
                   5380:                     }
                   5381:                 }
                   5382:             }
1.1375    raeburn  5383:         } elsif (($activity eq 'com') || ($activity eq 'port') || ($activity eq 'blogs') ||
                   5384:                 ($activity eq 'about') || ($activity eq 'wishlist') || ($activity eq 'passwd')) {
                   5385:             my $checkrole;
                   5386:             if ($env{'request.role.domain'} eq '') {
                   5387:                 $checkrole = "cm./$env{'user.domain'}/";
                   5388:             } else {
                   5389:                 $checkrole = "cm./$env{'request.role.domain'}/";
                   5390:             }
                   5391:             if (($checkrole) && (&Apache::lonnet::allowed('evb',undef,undef,$checkrole))) {
                   5392:                 $has_evb = 1;
                   5393:             }
1.1372    raeburn  5394:         }
                   5395:         unless ($has_evb || $check_ipaccess) {
                   5396:             my @machinedoms = &Apache::lonnet::current_machine_domains();
                   5397:             if (($dom eq 'public') && ($activity eq 'port')) {
                   5398:                 $dom = $udom;
                   5399:             }
                   5400:             if (($dom ne '') && (grep(/^\Q$dom\E$/,@machinedoms))) {
                   5401:                 $check_ipaccess = 1;
                   5402:             } else {
                   5403:                 my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
                   5404:                 my $internet_names = &Apache::lonnet::get_internet_names($lonhost);
                   5405:                 my $prim = &Apache::lonnet::domain($dom,'primary');
                   5406:                 my $intdom = &Apache::lonnet::internet_dom($prim);
                   5407:                 if (($intdom ne '') && (ref($internet_names) eq 'ARRAY')) {
                   5408:                     if (grep(/^\Q$intdom\E$/,@{$internet_names})) {
                   5409:                         $check_ipaccess = 1;
                   5410:                     }
                   5411:                 }
                   5412:             }
                   5413:         }
                   5414:         if ($check_ipaccess) {
                   5415:             my ($ipaccessref,$cached)=&Apache::lonnet::is_cached_new('ipaccess',$dom);
                   5416:             unless (defined($cached)) {
                   5417:                 my %domconfig =
                   5418:                     &Apache::lonnet::get_dom('configuration',['ipaccess'],$dom);
                   5419:                 $ipaccessref = &Apache::lonnet::do_cache_new('ipaccess',$dom,$domconfig{'ipaccess'},1800);
                   5420:             }
                   5421:             if ((ref($ipaccessref) eq 'HASH') && ($clientip)) {
                   5422:                 foreach my $id (keys(%{$ipaccessref})) {
                   5423:                     if (ref($ipaccessref->{$id}) eq 'HASH') {
                   5424:                         my $range = $ipaccessref->{$id}->{'ip'};
                   5425:                         if ($range) {
                   5426:                             if (&Apache::lonnet::ip_match($clientip,$range)) {
                   5427:                                 if (ref($ipaccessref->{$id}->{'commblocks'}) eq 'HASH') {
                   5428:                                     if ($ipaccessref->{$id}->{'commblocks'}->{$activity} eq 'on') {
                   5429:                                         return ('','','',$id,$dom);
                   5430:                                         last;
                   5431:                                     }
                   5432:                                 }
                   5433:                             }
                   5434:                         }
                   5435:                     }
                   5436:                 }
                   5437:             }
                   5438:         }
1.1373    raeburn  5439:         if (($activity eq 'wishlist') || ($activity eq 'annotate')) {
                   5440:             return ();
                   5441:         }
1.1372    raeburn  5442:     }
1.1189    raeburn  5443:     if (defined($udom) && defined($uname)) {
                   5444:         # If uname and udom are for a course, check for blocks in the course.
                   5445:         if (($is_course) || (&Apache::lonnet::is_course($udom,$uname))) {
                   5446:             my ($startblock,$endblock,$triggerblock) =
1.1347    raeburn  5447:                 &get_blocks($setters,$activity,$udom,$uname,$url,$symb,$caller);
1.1189    raeburn  5448:             return ($startblock,$endblock,$triggerblock);
                   5449:         }
                   5450:     } else {
1.490     raeburn  5451:         $udom = $env{'user.domain'};
                   5452:         $uname = $env{'user.name'};
                   5453:     }
                   5454: 
1.502     raeburn  5455:     my $startblock = 0;
                   5456:     my $endblock = 0;
1.1062    raeburn  5457:     my $triggerblock = '';
1.1373    raeburn  5458:     my %live_courses;
                   5459:     unless (($activity eq 'wishlist') || ($activity eq 'annotate')) {
                   5460:         %live_courses = &findallcourses(undef,$uname,$udom);
                   5461:     }
1.474     raeburn  5462: 
1.490     raeburn  5463:     # If uname is for a user, and activity is course-specific, i.e.,
                   5464:     # boards, chat or groups, check for blocking in current course only.
1.474     raeburn  5465: 
1.490     raeburn  5466:     if (($activity eq 'boards' || $activity eq 'chat' ||
1.1282    raeburn  5467:          $activity eq 'groups' || $activity eq 'printout' ||
1.1346    raeburn  5468:          $activity eq 'search' || $activity eq 'reinit' ||
                   5469:          $activity eq 'alert') &&
1.1189    raeburn  5470:         ($env{'request.course.id'})) {
1.490     raeburn  5471:         foreach my $key (keys(%live_courses)) {
                   5472:             if ($key ne $env{'request.course.id'}) {
                   5473:                 delete($live_courses{$key});
                   5474:             }
                   5475:         }
                   5476:     }
                   5477: 
                   5478:     my $otheruser = 0;
                   5479:     my %own_courses;
                   5480:     if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
                   5481:         # Resource belongs to user other than current user.
                   5482:         $otheruser = 1;
                   5483:         # Gather courses for current user
                   5484:         %own_courses = 
                   5485:             &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
                   5486:     }
                   5487: 
                   5488:     # Gather active course roles - course coordinator, instructor, 
                   5489:     # exam proctor, ta, student, or custom role.
1.474     raeburn  5490: 
                   5491:     foreach my $course (keys(%live_courses)) {
1.482     raeburn  5492:         my ($cdom,$cnum);
                   5493:         if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
                   5494:             $cdom = $env{'course.'.$course.'.domain'};
                   5495:             $cnum = $env{'course.'.$course.'.num'};
                   5496:         } else {
1.490     raeburn  5497:             ($cdom,$cnum) = split(/_/,$course); 
1.482     raeburn  5498:         }
                   5499:         my $no_ownblock = 0;
                   5500:         my $no_userblock = 0;
1.533     raeburn  5501:         if ($otheruser && $activity ne 'com') {
1.490     raeburn  5502:             # Check if current user has 'evb' priv for this
                   5503:             if (defined($own_courses{$course})) {
                   5504:                 foreach my $sec (keys(%{$own_courses{$course}})) {
                   5505:                     my $checkrole = 'cm./'.$cdom.'/'.$cnum;
                   5506:                     if ($sec ne 'none') {
                   5507:                         $checkrole .= '/'.$sec;
                   5508:                     }
                   5509:                     if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   5510:                         $no_ownblock = 1;
                   5511:                         last;
                   5512:                     }
                   5513:                 }
                   5514:             }
                   5515:             # if they have 'evb' priv and are currently not playing student
                   5516:             next if (($no_ownblock) &&
                   5517:                  ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
                   5518:         }
1.474     raeburn  5519:         foreach my $sec (keys(%{$live_courses{$course}})) {
1.482     raeburn  5520:             my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474     raeburn  5521:             if ($sec ne 'none') {
1.482     raeburn  5522:                 $checkrole .= '/'.$sec;
1.474     raeburn  5523:             }
1.490     raeburn  5524:             if ($otheruser) {
                   5525:                 # Resource belongs to user other than current user.
                   5526:                 # Assemble privs for that user, and check for 'evb' priv.
1.1058    raeburn  5527:                 my (%allroles,%userroles);
                   5528:                 if (ref($live_courses{$course}{$sec}) eq 'ARRAY') {
                   5529:                     foreach my $entry (@{$live_courses{$course}{$sec}}) { 
                   5530:                         my ($trole,$tdom,$tnum,$tsec);
                   5531:                         if ($entry =~ /^cr/) {
                   5532:                             ($trole,$tdom,$tnum,$tsec) = 
                   5533:                                 ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
                   5534:                         } else {
                   5535:                            ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
                   5536:                         }
                   5537:                         my ($spec,$area,$trest);
                   5538:                         $area = '/'.$tdom.'/'.$tnum;
                   5539:                         $trest = $tnum;
                   5540:                         if ($tsec ne '') {
                   5541:                             $area .= '/'.$tsec;
                   5542:                             $trest .= '/'.$tsec;
                   5543:                         }
                   5544:                         $spec = $trole.'.'.$area;
                   5545:                         if ($trole =~ /^cr/) {
                   5546:                             &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
                   5547:                                                               $tdom,$spec,$trest,$area);
                   5548:                         } else {
                   5549:                             &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
                   5550:                                                                 $tdom,$spec,$trest,$area);
                   5551:                         }
                   5552:                     }
1.1276    raeburn  5553:                     my ($author,$adv,$rar) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
1.1058    raeburn  5554:                     if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
                   5555:                         if ($1) {
                   5556:                             $no_userblock = 1;
                   5557:                             last;
                   5558:                         }
1.486     raeburn  5559:                     }
                   5560:                 }
1.490     raeburn  5561:             } else {
                   5562:                 # Resource belongs to current user
                   5563:                 # Check for 'evb' priv via lonnet::allowed().
1.482     raeburn  5564:                 if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
                   5565:                     $no_ownblock = 1;
                   5566:                     last;
                   5567:                 }
1.474     raeburn  5568:             }
                   5569:         }
                   5570:         # if they have the evb priv and are currently not playing student
1.482     raeburn  5571:         next if (($no_ownblock) &&
1.491     albertel 5572:                  ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482     raeburn  5573:         next if ($no_userblock);
1.474     raeburn  5574: 
1.1303    raeburn  5575:         # Retrieve blocking times and identity of blocker for course
1.490     raeburn  5576:         # of specified user, unless user has 'evb' privilege.
1.1284    raeburn  5577: 
1.1062    raeburn  5578:         my ($start,$end,$trigger) = 
1.1347    raeburn  5579:             &get_blocks($setters,$activity,$cdom,$cnum,$url,$symb,$caller);
1.502     raeburn  5580:         if (($start != 0) && 
                   5581:             (($startblock == 0) || ($startblock > $start))) {
                   5582:             $startblock = $start;
1.1062    raeburn  5583:             if ($trigger ne '') {
                   5584:                 $triggerblock = $trigger;
                   5585:             }
1.502     raeburn  5586:         }
                   5587:         if (($end != 0)  &&
                   5588:             (($endblock == 0) || ($endblock < $end))) {
                   5589:             $endblock = $end;
1.1062    raeburn  5590:             if ($trigger ne '') {
                   5591:                 $triggerblock = $trigger;
                   5592:             }
1.502     raeburn  5593:         }
1.490     raeburn  5594:     }
1.1062    raeburn  5595:     return ($startblock,$endblock,$triggerblock);
1.490     raeburn  5596: }
                   5597: 
                   5598: sub get_blocks {
1.1347    raeburn  5599:     my ($setters,$activity,$cdom,$cnum,$url,$symb,$caller) = @_;
1.490     raeburn  5600:     my $startblock = 0;
                   5601:     my $endblock = 0;
1.1062    raeburn  5602:     my $triggerblock = '';
1.490     raeburn  5603:     my $course = $cdom.'_'.$cnum;
                   5604:     $setters->{$course} = {};
                   5605:     $setters->{$course}{'staff'} = [];
                   5606:     $setters->{$course}{'times'} = [];
1.1062    raeburn  5607:     $setters->{$course}{'triggers'} = [];
                   5608:     my (@blockers,%triggered);
                   5609:     my $now = time;
                   5610:     my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum);
                   5611:     if ($activity eq 'docs') {
1.1348    raeburn  5612:         my ($blocked,$nosymbcache,$noenccheck);
1.1347    raeburn  5613:         if (($caller eq 'blockedaccess') || ($caller eq 'blockingstatus')) {
                   5614:             $blocked = 1;
                   5615:             $nosymbcache = 1;
1.1348    raeburn  5616:             $noenccheck = 1;
1.1347    raeburn  5617:         }
1.1348    raeburn  5618:         @blockers = &Apache::lonnet::has_comm_blocking('bre',$symb,$url,$nosymbcache,$noenccheck,$blocked,\%commblocks);
1.1062    raeburn  5619:         foreach my $block (@blockers) {
                   5620:             if ($block =~ /^firstaccess____(.+)$/) {
                   5621:                 my $item = $1;
                   5622:                 my $type = 'map';
                   5623:                 my $timersymb = $item;
                   5624:                 if ($item eq 'course') {
                   5625:                     $type = 'course';
                   5626:                 } elsif ($item =~ /___\d+___/) {
                   5627:                     $type = 'resource';
                   5628:                 } else {
                   5629:                     $timersymb = &Apache::lonnet::symbread($item);
                   5630:                 }
                   5631:                 my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
                   5632:                 my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
                   5633:                 $triggered{$block} = {
                   5634:                                        start => $start,
                   5635:                                        end   => $end,
                   5636:                                        type  => $type,
                   5637:                                      };
                   5638:             }
                   5639:         }
                   5640:     } else {
                   5641:         foreach my $block (keys(%commblocks)) {
                   5642:             if ($block =~ m/^(\d+)____(\d+)$/) { 
                   5643:                 my ($start,$end) = ($1,$2);
                   5644:                 if ($start <= time && $end >= time) {
                   5645:                     if (ref($commblocks{$block}) eq 'HASH') {
                   5646:                         if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
                   5647:                             if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
                   5648:                                 unless(grep(/^\Q$block\E$/,@blockers)) {
                   5649:                                     push(@blockers,$block);
                   5650:                                 }
                   5651:                             }
                   5652:                         }
                   5653:                     }
                   5654:                 }
                   5655:             } elsif ($block =~ /^firstaccess____(.+)$/) {
                   5656:                 my $item = $1;
                   5657:                 my $timersymb = $item; 
                   5658:                 my $type = 'map';
                   5659:                 if ($item eq 'course') {
                   5660:                     $type = 'course';
                   5661:                 } elsif ($item =~ /___\d+___/) {
                   5662:                     $type = 'resource';
                   5663:                 } else {
                   5664:                     $timersymb = &Apache::lonnet::symbread($item);
                   5665:                 }
                   5666:                 my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
                   5667:                 my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb}; 
                   5668:                 if ($start && $end) {
                   5669:                     if (($start <= time) && ($end >= time)) {
1.1281    raeburn  5670:                         if (ref($commblocks{$block}) eq 'HASH') {
                   5671:                             if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
                   5672:                                 if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
                   5673:                                     unless(grep(/^\Q$block\E$/,@blockers)) {
                   5674:                                         push(@blockers,$block);
                   5675:                                         $triggered{$block} = {
                   5676:                                                                start => $start,
                   5677:                                                                end   => $end,
                   5678:                                                                type  => $type,
                   5679:                                                              };
                   5680:                                     }
                   5681:                                 }
                   5682:                             }
1.1062    raeburn  5683:                         }
                   5684:                     }
1.490     raeburn  5685:                 }
1.1062    raeburn  5686:             }
                   5687:         }
                   5688:     }
                   5689:     foreach my $blocker (@blockers) {
                   5690:         my ($staff_name,$staff_dom,$title,$blocks) =
                   5691:             &parse_block_record($commblocks{$blocker});
                   5692:         push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
                   5693:         my ($start,$end,$triggertype);
                   5694:         if ($blocker =~ m/^(\d+)____(\d+)$/) {
                   5695:             ($start,$end) = ($1,$2);
                   5696:         } elsif (ref($triggered{$blocker}) eq 'HASH') {
                   5697:             $start = $triggered{$blocker}{'start'};
                   5698:             $end = $triggered{$blocker}{'end'};
                   5699:             $triggertype = $triggered{$blocker}{'type'};
                   5700:         }
                   5701:         if ($start) {
                   5702:             push(@{$$setters{$course}{'times'}}, [$start,$end]);
                   5703:             if ($triggertype) {
                   5704:                 push(@{$$setters{$course}{'triggers'}},$triggertype);
                   5705:             } else {
                   5706:                 push(@{$$setters{$course}{'triggers'}},0);
                   5707:             }
                   5708:             if ( ($startblock == 0) || ($startblock > $start) ) {
                   5709:                 $startblock = $start;
                   5710:                 if ($triggertype) {
                   5711:                     $triggerblock = $blocker;
1.474     raeburn  5712:                 }
                   5713:             }
1.1062    raeburn  5714:             if ( ($endblock == 0) || ($endblock < $end) ) {
                   5715:                $endblock = $end;
                   5716:                if ($triggertype) {
                   5717:                    $triggerblock = $blocker;
                   5718:                }
                   5719:             }
1.474     raeburn  5720:         }
                   5721:     }
1.1062    raeburn  5722:     return ($startblock,$endblock,$triggerblock);
1.474     raeburn  5723: }
                   5724: 
                   5725: sub parse_block_record {
                   5726:     my ($record) = @_;
                   5727:     my ($setuname,$setudom,$title,$blocks);
                   5728:     if (ref($record) eq 'HASH') {
                   5729:         ($setuname,$setudom) = split(/:/,$record->{'setter'});
                   5730:         $title = &unescape($record->{'event'});
                   5731:         $blocks = $record->{'blocks'};
                   5732:     } else {
                   5733:         my @data = split(/:/,$record,3);
                   5734:         if (scalar(@data) eq 2) {
                   5735:             $title = $data[1];
                   5736:             ($setuname,$setudom) = split(/@/,$data[0]);
                   5737:         } else {
                   5738:             ($setuname,$setudom,$title) = @data;
                   5739:         }
                   5740:         $blocks = { 'com' => 'on' };
                   5741:     }
                   5742:     return ($setuname,$setudom,$title,$blocks);
                   5743: }
                   5744: 
1.854     kalberla 5745: sub blocking_status {
1.1372    raeburn  5746:     my ($activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_;
1.1061    raeburn  5747:     my %setters;
1.890     droeschl 5748: 
1.1061    raeburn  5749: # check for active blocking
1.1372    raeburn  5750:     if ($clientip eq '') {
                   5751:         $clientip = &Apache::lonnet::get_requestor_ip();
                   5752:     }
                   5753:     my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = 
                   5754:         &blockcheck(\%setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller);
1.1062    raeburn  5755:     my $blocked = 0;
1.1372    raeburn  5756:     if (($startblock && $endblock) || ($by_ip)) {
1.1062    raeburn  5757:         $blocked = 1;
                   5758:     }
1.890     droeschl 5759: 
1.1061    raeburn  5760: # caller just wants to know whether a block is active
                   5761:     if (!wantarray) { return $blocked; }
                   5762: 
                   5763: # build a link to a popup window containing the details
                   5764:     my $querystring  = "?activity=$activity";
1.1351    raeburn  5765: # $uname and $udom decide whose portfolio (or information page) the user is trying to look at
                   5766:     if (($activity eq 'port') || ($activity eq 'about') || ($activity eq 'passwd')) {
1.1232    raeburn  5767:         $querystring .= "&amp;udom=$udom"      if ($udom =~ /^$match_domain$/); 
                   5768:         $querystring .= "&amp;uname=$uname"    if ($uname =~ /^$match_username$/);
1.1062    raeburn  5769:     } elsif ($activity eq 'docs') {
1.1347    raeburn  5770:         my $showurl = &Apache::lonenc::check_encrypt($url);
                   5771:         $querystring .= '&amp;url='.&HTML::Entities::encode($showurl,'\'&"<>');
                   5772:         if ($symb) {
                   5773:             my $showsymb = &Apache::lonenc::check_encrypt($symb);
                   5774:             $querystring .= '&amp;symb='.&HTML::Entities::encode($showsymb,'\'&"<>');
                   5775:         }
1.1062    raeburn  5776:     }
1.1061    raeburn  5777: 
                   5778:     my $output .= <<'END_MYBLOCK';
                   5779: function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
                   5780:     var options = "width=" + w + ",height=" + h + ",";
                   5781:     options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
                   5782:     options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
                   5783:     var newWin = window.open(url, wdwName, options);
                   5784:     newWin.focus();
                   5785: }
1.890     droeschl 5786: END_MYBLOCK
1.854     kalberla 5787: 
1.1061    raeburn  5788:     $output = Apache::lonhtmlcommon::scripttag($output);
1.890     droeschl 5789:   
1.1061    raeburn  5790:     my $popupUrl = "/adm/blockingstatus/$querystring";
1.1062    raeburn  5791:     my $text = &mt('Communication Blocked');
1.1217    raeburn  5792:     my $class = 'LC_comblock';
1.1062    raeburn  5793:     if ($activity eq 'docs') {
                   5794:         $text = &mt('Content Access Blocked');
1.1217    raeburn  5795:         $class = '';
1.1063    raeburn  5796:     } elsif ($activity eq 'printout') {
                   5797:         $text = &mt('Printing Blocked');
1.1232    raeburn  5798:     } elsif ($activity eq 'passwd') {
                   5799:         $text = &mt('Password Changing Blocked');
1.1345    raeburn  5800:     } elsif ($activity eq 'grades') {
                   5801:         $text = &mt('Gradebook Blocked');
1.1346    raeburn  5802:     } elsif ($activity eq 'search') {
                   5803:         $text = &mt('Search Blocked');
1.1282    raeburn  5804:     } elsif ($activity eq 'alert') {
                   5805:         $text = &mt('Checking Critical Messages Blocked');
                   5806:     } elsif ($activity eq 'reinit') {
                   5807:         $text = &mt('Checking Course Update Blocked');
1.1351    raeburn  5808:     } elsif ($activity eq 'about') {
                   5809:         $text = &mt('Access to User Information Pages Blocked');
1.1373    raeburn  5810:     } elsif ($activity eq 'wishlist') {
                   5811:         $text = &mt('Access to Stored Links Blocked');
                   5812:     } elsif ($activity eq 'annotate') {
                   5813:         $text = &mt('Access to Annotations Blocked');
1.1062    raeburn  5814:     }
1.1061    raeburn  5815:     $output .= <<"END_BLOCK";
1.1217    raeburn  5816: <div class='$class'>
1.869     kalberla 5817:   <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890     droeschl 5818:   title='$text'>
                   5819:   <img class='LC_noBorder LC_middle' title='$text' src='/res/adm/pages/comblock.png' alt='$text'/></a>
1.869     kalberla 5820:   <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring' 
1.890     droeschl 5821:   title='$text'>$text</a>
1.867     kalberla 5822: </div>
                   5823: 
                   5824: END_BLOCK
1.474     raeburn  5825: 
1.1061    raeburn  5826:     return ($blocked, $output);
1.854     kalberla 5827: }
1.490     raeburn  5828: 
1.60      matthew  5829: ###############################################
                   5830: 
1.682     raeburn  5831: sub check_ip_acc {
1.1201    raeburn  5832:     my ($acc,$clientip)=@_;
1.682     raeburn  5833:     &Apache::lonxml::debug("acc is $acc");
                   5834:     if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
                   5835:         return 1;
                   5836:     }
1.1339    raeburn  5837:     my ($ip,$allowed);
                   5838:     if (($ENV{'REMOTE_ADDR'} eq '127.0.0.1') ||
                   5839:         ($ENV{'REMOTE_ADDR'} eq &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}))) {
                   5840:         $ip = $env{'request.host'} || $ENV{'REMOTE_ADDR'} || $clientip;
                   5841:     } else {
1.1350    raeburn  5842:         my $remote_ip = &Apache::lonnet::get_requestor_ip();
                   5843:         $ip = $remote_ip || $env{'request.host'} || $clientip;
1.1339    raeburn  5844:     }
1.682     raeburn  5845: 
                   5846:     my $name;
1.1219    raeburn  5847:     my %access = (
                   5848:                      allowfrom => 1,
                   5849:                      denyfrom  => 0,
                   5850:                  );
                   5851:     my @allows;
                   5852:     my @denies;
                   5853:     foreach my $item (split(',',$acc)) {
                   5854:         $item =~ s/^\s*//;
                   5855:         $item =~ s/\s*$//;
                   5856:         my $pattern;
                   5857:         if ($item =~ /^\!(.+)$/) {
                   5858:             push(@denies,$1);
                   5859:         } else {
                   5860:             push(@allows,$item);
                   5861:         }
                   5862:    }
                   5863:    my $numdenies = scalar(@denies);
                   5864:    my $numallows = scalar(@allows);
                   5865:    my $count = 0;
                   5866:    foreach my $pattern (@denies,@allows) {
                   5867:         $count ++; 
                   5868:         my $acctype = 'allowfrom';
                   5869:         if ($count <= $numdenies) {
                   5870:             $acctype = 'denyfrom';
                   5871:         }
1.682     raeburn  5872:         if ($pattern =~ /\*$/) {
                   5873:             #35.8.*
                   5874:             $pattern=~s/\*//;
1.1219    raeburn  5875:             if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682     raeburn  5876:         } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
                   5877:             #35.8.3.[34-56]
                   5878:             my $low=$2;
                   5879:             my $high=$3;
                   5880:             $pattern=$1;
                   5881:             if ($ip =~ /^\Q$pattern\E/) {
                   5882:                 my $last=(split(/\./,$ip))[3];
1.1219    raeburn  5883:                 if ($last <=$high && $last >=$low) { $allowed=$access{$acctype}; }
1.682     raeburn  5884:             }
                   5885:         } elsif ($pattern =~ /^\*/) {
                   5886:             #*.msu.edu
                   5887:             $pattern=~s/\*//;
                   5888:             if (!defined($name)) {
                   5889:                 use Socket;
                   5890:                 my $netaddr=inet_aton($ip);
                   5891:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   5892:             }
1.1219    raeburn  5893:             if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
1.682     raeburn  5894:         } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
                   5895:             #127.0.0.1
1.1219    raeburn  5896:             if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; }
1.682     raeburn  5897:         } else {
                   5898:             #some.name.com
                   5899:             if (!defined($name)) {
                   5900:                 use Socket;
                   5901:                 my $netaddr=inet_aton($ip);
                   5902:                 ($name)=gethostbyaddr($netaddr,AF_INET);
                   5903:             }
1.1219    raeburn  5904:             if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; }
                   5905:         }
                   5906:         if ($allowed =~ /^(0|1)$/) { last; }
                   5907:     }
                   5908:     if ($allowed eq '') {
                   5909:         if ($numdenies && !$numallows) {
                   5910:             $allowed = 1;
                   5911:         } else {
                   5912:             $allowed = 0;
1.682     raeburn  5913:         }
                   5914:     }
                   5915:     return $allowed;
                   5916: }
                   5917: 
                   5918: ###############################################
                   5919: 
1.60      matthew  5920: =pod
                   5921: 
1.112     bowersj2 5922: =head1 Domain Template Functions
                   5923: 
                   5924: =over 4
                   5925: 
                   5926: =item * &determinedomain()
1.60      matthew  5927: 
                   5928: Inputs: $domain (usually will be undef)
                   5929: 
1.63      www      5930: Returns: Determines which domain should be used for designs
1.60      matthew  5931: 
                   5932: =cut
1.54      www      5933: 
1.60      matthew  5934: ###############################################
1.63      www      5935: sub determinedomain {
                   5936:     my $domain=shift;
1.531     albertel 5937:     if (! $domain) {
1.60      matthew  5938:         # Determine domain if we have not been given one
1.893     raeburn  5939:         $domain = &Apache::lonnet::default_login_domain();
1.258     albertel 5940:         if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
                   5941:         if ($env{'request.role.domain'}) { 
                   5942:             $domain=$env{'request.role.domain'}; 
1.60      matthew  5943:         }
                   5944:     }
1.63      www      5945:     return $domain;
                   5946: }
                   5947: ###############################################
1.517     raeburn  5948: 
1.518     albertel 5949: sub devalidate_domconfig_cache {
                   5950:     my ($udom)=@_;
                   5951:     &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
                   5952: }
                   5953: 
                   5954: # ---------------------- Get domain configuration for a domain
                   5955: sub get_domainconf {
                   5956:     my ($udom) = @_;
                   5957:     my $cachetime=1800;
                   5958:     my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
                   5959:     if (defined($cached)) { return %{$result}; }
                   5960: 
                   5961:     my %domconfig = &Apache::lonnet::get_dom('configuration',
1.948     raeburn  5962: 					     ['login','rolecolors','autoenroll'],$udom);
1.632     raeburn  5963:     my (%designhash,%legacy);
1.518     albertel 5964:     if (keys(%domconfig) > 0) {
                   5965:         if (ref($domconfig{'login'}) eq 'HASH') {
1.632     raeburn  5966:             if (keys(%{$domconfig{'login'}})) {
                   5967:                 foreach my $key (keys(%{$domconfig{'login'}})) {
1.699     raeburn  5968:                     if (ref($domconfig{'login'}{$key}) eq 'HASH') {
1.1208    raeburn  5969:                         if (($key eq 'loginvia') || ($key eq 'headtag')) {
                   5970:                             if (ref($domconfig{'login'}{$key}) eq 'HASH') {
                   5971:                                 foreach my $hostname (keys(%{$domconfig{'login'}{$key}})) {
                   5972:                                     if (ref($domconfig{'login'}{$key}{$hostname}) eq 'HASH') {
                   5973:                                         if ($key eq 'loginvia') {
                   5974:                                             if ($domconfig{'login'}{'loginvia'}{$hostname}{'server'}) {
                   5975:                                                 my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
                   5976:                                                 $designhash{$udom.'.login.loginvia'} = $server;
                   5977:                                                 if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {
                   5978: 
                   5979:                                                     $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
                   5980:                                                 } else {
                   5981:                                                     $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
                   5982:                                                 }
1.948     raeburn  5983:                                             }
1.1208    raeburn  5984:                                         } elsif ($key eq 'headtag') {
                   5985:                                             if ($domconfig{'login'}{'headtag'}{$hostname}{'url'}) {
                   5986:                                                 $designhash{$udom.'.login.headtag_'.$hostname} = $domconfig{'login'}{'headtag'}{$hostname}{'url'};
1.948     raeburn  5987:                                             }
1.946     raeburn  5988:                                         }
1.1208    raeburn  5989:                                         if ($domconfig{'login'}{$key}{$hostname}{'exempt'}) {
                   5990:                                             $designhash{$udom.'.login.'.$key.'_exempt_'.$hostname} = $domconfig{'login'}{$key}{$hostname}{'exempt'};
                   5991:                                         }
1.946     raeburn  5992:                                     }
                   5993:                                 }
                   5994:                             }
1.1366    raeburn  5995:                         } elsif ($key eq 'saml') {
                   5996:                             if (ref($domconfig{'login'}{$key}) eq 'HASH') {
                   5997:                                 foreach my $host (keys(%{$domconfig{'login'}{$key}})) {
                   5998:                                     if (ref($domconfig{'login'}{$key}{$host}) eq 'HASH') {
                   5999:                                         $designhash{$udom.'.login.'.$key.'_'.$host} = 1;
                   6000:                                         foreach my $item ('text','img','alt','url','title','notsso') {
                   6001:                                             $designhash{$udom.'.login.'.$key.'_'.$item.'_'.$host} = $domconfig{'login'}{$key}{$host}{$item};
                   6002:                                         }
                   6003:                                     }
                   6004:                                 }
                   6005:                             }
1.946     raeburn  6006:                         } else {
                   6007:                             foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
                   6008:                                 $designhash{$udom.'.login.'.$key.'_'.$img} = 
                   6009:                                     $domconfig{'login'}{$key}{$img};
                   6010:                             }
1.699     raeburn  6011:                         }
                   6012:                     } else {
                   6013:                         $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
                   6014:                     }
1.632     raeburn  6015:                 }
                   6016:             } else {
                   6017:                 $legacy{'login'} = 1;
1.518     albertel 6018:             }
1.632     raeburn  6019:         } else {
                   6020:             $legacy{'login'} = 1;
1.518     albertel 6021:         }
                   6022:         if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632     raeburn  6023:             if (keys(%{$domconfig{'rolecolors'}})) {
                   6024:                 foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
                   6025:                     if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
                   6026:                         foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
                   6027:                             $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
                   6028:                         }
1.518     albertel 6029:                     }
                   6030:                 }
1.632     raeburn  6031:             } else {
                   6032:                 $legacy{'rolecolors'} = 1;
1.518     albertel 6033:             }
1.632     raeburn  6034:         } else {
                   6035:             $legacy{'rolecolors'} = 1;
1.518     albertel 6036:         }
1.948     raeburn  6037:         if (ref($domconfig{'autoenroll'}) eq 'HASH') {
                   6038:             if ($domconfig{'autoenroll'}{'co-owners'}) {
                   6039:                 $designhash{$udom.'.autoassign.co-owners'}=$domconfig{'autoenroll'}{'co-owners'};
                   6040:             }
                   6041:         }
1.632     raeburn  6042:         if (keys(%legacy) > 0) {
                   6043:             my %legacyhash = &get_legacy_domconf($udom);
                   6044:             foreach my $item (keys(%legacyhash)) {
                   6045:                 if ($item =~ /^\Q$udom\E\.login/) {
                   6046:                     if ($legacy{'login'}) { 
                   6047:                         $designhash{$item} = $legacyhash{$item};
                   6048:                     }
                   6049:                 } else {
                   6050:                     if ($legacy{'rolecolors'}) {
                   6051:                         $designhash{$item} = $legacyhash{$item};
                   6052:                     }
1.518     albertel 6053:                 }
                   6054:             }
                   6055:         }
1.632     raeburn  6056:     } else {
                   6057:         %designhash = &get_legacy_domconf($udom); 
1.518     albertel 6058:     }
                   6059:     &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
                   6060: 				  $cachetime);
                   6061:     return %designhash;
                   6062: }
                   6063: 
1.632     raeburn  6064: sub get_legacy_domconf {
                   6065:     my ($udom) = @_;
                   6066:     my %legacyhash;
                   6067:     my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
                   6068:     my $designfile =  $designdir.'/'.$udom.'.tab';
                   6069:     if (-e $designfile) {
1.1317    raeburn  6070:         if ( open (my $fh,'<',$designfile) ) {
1.632     raeburn  6071:             while (my $line = <$fh>) {
                   6072:                 next if ($line =~ /^\#/);
                   6073:                 chomp($line);
                   6074:                 my ($key,$val)=(split(/\=/,$line));
                   6075:                 if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
                   6076:             }
                   6077:             close($fh);
                   6078:         }
                   6079:     }
1.1026    raeburn  6080:     if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/adm/lonDomLogos/'.$udom.'.gif') {
1.632     raeburn  6081:         $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
                   6082:     }
                   6083:     return %legacyhash;
                   6084: }
                   6085: 
1.63      www      6086: =pod
                   6087: 
1.112     bowersj2 6088: =item * &domainlogo()
1.63      www      6089: 
                   6090: Inputs: $domain (usually will be undef)
                   6091: 
                   6092: Returns: A link to a domain logo, if the domain logo exists.
                   6093: If the domain logo does not exist, a description of the domain.
                   6094: 
                   6095: =cut
1.112     bowersj2 6096: 
1.63      www      6097: ###############################################
                   6098: sub domainlogo {
1.517     raeburn  6099:     my $domain = &determinedomain(shift);
1.518     albertel 6100:     my %designhash = &get_domainconf($domain);    
1.517     raeburn  6101:     # See if there is a logo
                   6102:     if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519     raeburn  6103:         my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538     albertel 6104:         if ($imgsrc =~ m{^/(adm|res)/}) {
                   6105: 	    if ($imgsrc =~ m{^/res/}) {
                   6106: 		my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
                   6107: 		&Apache::lonnet::repcopy($local_name);
                   6108: 	    }
                   6109: 	   $imgsrc = &lonhttpdurl($imgsrc);
1.1374    raeburn  6110:         }
                   6111:         my $alttext = $domain;
                   6112:         if ($designhash{$domain.'.login.alttext_domlogo'} ne '') {
                   6113:             $alttext = $designhash{$domain.'.login.alttext_domlogo'};
                   6114:         }
                   6115:         return '<img src="'.$imgsrc.'" alt="'.$alttext.'" id="lclogindomlogo" />';
1.514     albertel 6116:     } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
                   6117:         return &Apache::lonnet::domain($domain,'description');
1.59      www      6118:     } else {
1.60      matthew  6119:         return '';
1.59      www      6120:     }
                   6121: }
1.63      www      6122: ##############################################
                   6123: 
                   6124: =pod
                   6125: 
1.112     bowersj2 6126: =item * &designparm()
1.63      www      6127: 
                   6128: Inputs: $which parameter; $domain (usually will be undef)
                   6129: 
                   6130: Returns: value of designparamter $which
                   6131: 
                   6132: =cut
1.112     bowersj2 6133: 
1.397     albertel 6134: 
1.400     albertel 6135: ##############################################
1.397     albertel 6136: sub designparm {
                   6137:     my ($which,$domain)=@_;
                   6138:     if (exists($env{'environment.color.'.$which})) {
1.817     bisitz   6139:         return $env{'environment.color.'.$which};
1.96      www      6140:     }
1.63      www      6141:     $domain=&determinedomain($domain);
1.1016    raeburn  6142:     my %domdesign;
                   6143:     unless ($domain eq 'public') {
                   6144:         %domdesign = &get_domainconf($domain);
                   6145:     }
1.520     raeburn  6146:     my $output;
1.517     raeburn  6147:     if ($domdesign{$domain.'.'.$which} ne '') {
1.817     bisitz   6148:         $output = $domdesign{$domain.'.'.$which};
1.63      www      6149:     } else {
1.520     raeburn  6150:         $output = $defaultdesign{$which};
                   6151:     }
                   6152:     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635     raeburn  6153:         ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538     albertel 6154:         if ($output =~ m{^/(adm|res)/}) {
1.817     bisitz   6155:             if ($output =~ m{^/res/}) {
                   6156:                 my $local_name = &Apache::lonnet::filelocation('',$output);
                   6157:                 &Apache::lonnet::repcopy($local_name);
                   6158:             }
1.520     raeburn  6159:             $output = &lonhttpdurl($output);
                   6160:         }
1.63      www      6161:     }
1.520     raeburn  6162:     return $output;
1.63      www      6163: }
1.59      www      6164: 
1.822     bisitz   6165: ##############################################
                   6166: =pod
                   6167: 
1.832     bisitz   6168: =item * &authorspace()
                   6169: 
1.1028    raeburn  6170: Inputs: $url (usually will be undef).
1.832     bisitz   6171: 
1.1132    raeburn  6172: Returns: Path to Authoring Space containing the resource or 
1.1028    raeburn  6173:          directory being viewed (or for which action is being taken). 
                   6174:          If $url is provided, and begins /priv/<domain>/<uname>
                   6175:          the path will be that portion of the $context argument.
                   6176:          Otherwise the path will be for the author space of the current
                   6177:          user when the current role is author, or for that of the 
                   6178:          co-author/assistant co-author space when the current role 
                   6179:          is co-author or assistant co-author.
1.832     bisitz   6180: 
                   6181: =cut
                   6182: 
                   6183: sub authorspace {
1.1028    raeburn  6184:     my ($url) = @_;
                   6185:     if ($url ne '') {
                   6186:         if ($url =~ m{^(/priv/$match_domain/$match_username/)}) {
                   6187:            return $1;
                   6188:         }
                   6189:     }
1.832     bisitz   6190:     my $caname = '';
1.1024    www      6191:     my $cadom = '';
1.1028    raeburn  6192:     if ($env{'request.role'} =~ /^(?:ca|aa)/) {
1.1024    www      6193:         ($cadom,$caname) =
1.832     bisitz   6194:             ($env{'request.role'}=~/($match_domain)\/($match_username)$/);
1.1028    raeburn  6195:     } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
1.832     bisitz   6196:         $caname = $env{'user.name'};
1.1024    www      6197:         $cadom = $env{'user.domain'};
1.832     bisitz   6198:     }
1.1028    raeburn  6199:     if (($caname ne '') && ($cadom ne '')) {
                   6200:         return "/priv/$cadom/$caname/";
                   6201:     }
                   6202:     return;
1.832     bisitz   6203: }
                   6204: 
                   6205: ##############################################
                   6206: =pod
                   6207: 
1.822     bisitz   6208: =item * &head_subbox()
                   6209: 
                   6210: Inputs: $content (contains HTML code with page functions, etc.)
                   6211: 
                   6212: Returns: HTML div with $content
                   6213:          To be included in page header
                   6214: 
                   6215: =cut
                   6216: 
                   6217: sub head_subbox {
                   6218:     my ($content)=@_;
                   6219:     my $output =
1.993     raeburn  6220:         '<div class="LC_head_subbox">'
1.822     bisitz   6221:        .$content
                   6222:        .'</div>'
                   6223: }
                   6224: 
                   6225: ##############################################
                   6226: =pod
                   6227: 
                   6228: =item * &CSTR_pageheader()
                   6229: 
1.1026    raeburn  6230: Input: (optional) filename from which breadcrumb trail is built.
                   6231:        In most cases no input as needed, as $env{'request.filename'}
                   6232:        is appropriate for use in building the breadcrumb trail.
1.822     bisitz   6233: 
                   6234: Returns: HTML div with CSTR path and recent box
1.1132    raeburn  6235:          To be included on Authoring Space pages
1.822     bisitz   6236: 
                   6237: =cut
                   6238: 
                   6239: sub CSTR_pageheader {
1.1026    raeburn  6240:     my ($trailfile) = @_;
                   6241:     if ($trailfile eq '') {
                   6242:         $trailfile = $env{'request.filename'};
                   6243:     }
                   6244: 
                   6245: # this is for resources; directories have customtitle, and crumbs
                   6246: # and select recent are created in lonpubdir.pm
                   6247: 
                   6248:     my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
1.1022    www      6249:     my ($udom,$uname,$thisdisfn)=
1.1113    raeburn  6250:         ($trailfile =~ m{^\Q$londocroot\E/priv/([^/]+)/([^/]+)(?:|/(.*))$});
1.1026    raeburn  6251:     my $formaction = "/priv/$udom/$uname/$thisdisfn";
                   6252:     $formaction =~ s{/+}{/}g;
1.822     bisitz   6253: 
                   6254:     my $parentpath = '';
                   6255:     my $lastitem = '';
                   6256:     if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
                   6257:         $parentpath = $1;
                   6258:         $lastitem = $2;
                   6259:     } else {
                   6260:         $lastitem = $thisdisfn;
                   6261:     }
1.921     bisitz   6262: 
1.1246    raeburn  6263:     my ($crsauthor,$title);
                   6264:     if (($env{'request.course.id'}) &&
                   6265:         ($env{'course.'.$env{'request.course.id'}.'.num'} eq $uname) &&
1.1247    raeburn  6266:         ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $udom)) {
1.1246    raeburn  6267:         $crsauthor = 1;
                   6268:         $title = &mt('Course Authoring Space');
                   6269:     } else {
                   6270:         $title = &mt('Authoring Space');
                   6271:     }
                   6272: 
1.1314    raeburn  6273:     my ($target,$crumbtarget) = (' target="_top"','_top'); #FIXME lonpubdir: target="_parent"
1.1313    raeburn  6274:     if (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
1.1314    raeburn  6275:         $target = '';
                   6276:         $crumbtarget = '';
1.1313    raeburn  6277:     }
1.1378  ! raeburn  6278:     if (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'})) {
        !          6279:         $target = ' target="'.$env{'request.deeplink.target'}.'"';
        !          6280:         $crumbtarget = $env{'request.deeplink.target'};
        !          6281:     }
1.1313    raeburn  6282: 
1.921     bisitz   6283:     my $output =
1.822     bisitz   6284:          '<div>'
                   6285:         .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
1.1246    raeburn  6286:         .'<b>'.$title.'</b> '
1.1314    raeburn  6287:         .'<form name="dirs" method="post" action="'.$formaction.'"'.$target.'>'
                   6288:         .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,$crumbtarget,'/priv/'.$udom,undef,undef);
1.921     bisitz   6289: 
                   6290:     if ($lastitem) {
                   6291:         $output .=
                   6292:              '<span class="LC_filename">'
                   6293:             .$lastitem
                   6294:             .'</span>';
                   6295:     }
1.1245    raeburn  6296: 
1.1246    raeburn  6297:     if ($crsauthor) {
                   6298:         $output .= '</form>'.&Apache::lonmenu::constspaceform();
                   6299:     } else {
                   6300:         $output .=
                   6301:              '<br />'
1.1314    raeburn  6302:             #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/',$crumbtarget,'/priv','','+1',1)."</b></tt><br />"
1.1246    raeburn  6303:             .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
                   6304:             .'</form>'
                   6305:             .&Apache::lonmenu::constspaceform();
                   6306:     }
                   6307:     $output .= '</div>';
1.921     bisitz   6308: 
                   6309:     return $output;
1.822     bisitz   6310: }
                   6311: 
1.60      matthew  6312: ###############################################
                   6313: ###############################################
                   6314: 
                   6315: =pod
                   6316: 
1.112     bowersj2 6317: =back
                   6318: 
1.549     albertel 6319: =head1 HTML Helpers
1.112     bowersj2 6320: 
                   6321: =over 4
                   6322: 
                   6323: =item * &bodytag()
1.60      matthew  6324: 
                   6325: Returns a uniform header for LON-CAPA web pages.
                   6326: 
                   6327: Inputs: 
                   6328: 
1.112     bowersj2 6329: =over 4
                   6330: 
                   6331: =item * $title, A title to be displayed on the page.
                   6332: 
                   6333: =item * $function, the current role (can be undef).
                   6334: 
                   6335: =item * $addentries, extra parameters for the <body> tag.
                   6336: 
                   6337: =item * $bodyonly, if defined, only return the <body> tag.
                   6338: 
                   6339: =item * $domain, if defined, force a given domain.
                   6340: 
                   6341: =item * $forcereg, if page should register as content page (relevant for 
1.86      www      6342:             text interface only)
1.60      matthew  6343: 
1.814     bisitz   6344: =item * $no_nav_bar, if true, keep the 'what is this' info but remove the
                   6345:                      navigational links
1.317     albertel 6346: 
1.338     albertel 6347: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
                   6348: 
1.460     albertel 6349: =item * $args, optional argument valid values are
                   6350:             no_auto_mt_title -> prevents &mt()ing the title arg
1.1274    raeburn  6351:             use_absolute     -> for external resource or syllabus, this will
                   6352:                                 contain https://<hostname> if server uses
                   6353:                                 https (as per hosts.tab), but request is for http
                   6354:             hostname         -> hostname, from $r->hostname().
1.460     albertel 6355: 
1.1096    raeburn  6356: =item * $advtoolsref, optional argument, ref to an array containing
                   6357:             inlineremote items to be added in "Functions" menu below
                   6358:             breadcrumbs.
                   6359: 
1.1316    raeburn  6360: =item * $ltiscope, optional argument, will be one of: resource, map or
                   6361:             course, if LON-CAPA is in LTI Provider context. Value is
                   6362:             the scope of use, i.e., launch was for access to a single, a map
                   6363:             or the entire course.
                   6364: 
                   6365: =item * $ltiuri, optional argument, if LON-CAPA is in LTI Provider
                   6366:             context, this will contain the URL for the landing item in
                   6367:             the course, after launch from an LTI Consumer
                   6368: 
1.1318    raeburn  6369: =item * $ltimenu, optional argument, if LON-CAPA is in LTI Provider
                   6370:             context, this will contain a reference to hash of items
                   6371:             to be included in the page header and/or inline menu.
                   6372: 
1.112     bowersj2 6373: =back
                   6374: 
1.60      matthew  6375: Returns: A uniform header for LON-CAPA web pages.  
                   6376: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
                   6377: If $bodyonly is undef or zero, an html string containing a <body> tag and 
                   6378: other decorations will be returned.
                   6379: 
                   6380: =cut
                   6381: 
1.54      www      6382: sub bodytag {
1.831     bisitz   6383:     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
1.1359    raeburn  6384:         $no_nav_bar,$bgcolor,$args,$advtoolsref,$ltiscope,$ltiuri,
                   6385:         $ltimenu,$menucoll,$menuref)=@_;
1.339     albertel 6386: 
1.954     raeburn  6387:     my $public;
                   6388:     if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
                   6389:         || ($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
                   6390:         $public = 1;
                   6391:     }
1.460     albertel 6392:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.1154    raeburn  6393:     my $httphost = $args->{'use_absolute'};
1.1274    raeburn  6394:     my $hostname = $args->{'hostname'};
1.339     albertel 6395: 
1.183     matthew  6396:     $function = &get_users_function() if (!$function);
1.339     albertel 6397:     my $img =    &designparm($function.'.img',$domain);
                   6398:     my $font =   &designparm($function.'.font',$domain);
                   6399:     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
                   6400: 
1.803     bisitz   6401:     my %design = ( 'style'   => 'margin-top: 0',
1.535     albertel 6402: 		   'bgcolor' => $pgbg,
1.339     albertel 6403: 		   'text'    => $font,
                   6404:                    'alink'   => &designparm($function.'.alink',$domain),
                   6405: 		   'vlink'   => &designparm($function.'.vlink',$domain),
                   6406: 		   'link'    => &designparm($function.'.link',$domain),);
1.438     albertel 6407:     @design{keys(%$addentries)} = @$addentries{keys(%$addentries)}; 
1.339     albertel 6408: 
1.63      www      6409:  # role and realm
1.1178    raeburn  6410:     my ($role,$realm) = split(m{\./},$env{'request.role'},2);
                   6411:     if ($realm) {
                   6412:         $realm = '/'.$realm;
                   6413:     }
1.1357    raeburn  6414:     if ($role eq 'ca') {
1.479     albertel 6415:         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500     albertel 6416:         $realm = &plainname($rname,$rdom);
1.378     raeburn  6417:     } 
1.55      www      6418: # realm
1.1357    raeburn  6419:     my ($cid,$sec);
1.258     albertel 6420:     if ($env{'request.course.id'}) {
1.1357    raeburn  6421:         $cid = $env{'request.course.id'};
                   6422:         if ($env{'request.course.sec'}) {
                   6423:             $sec = $env{'request.course.sec'};
                   6424:         }
                   6425:     } elsif ($realm =~ m{^/($match_domain)/($match_courseid)(?:|/(\w+))$}) {
                   6426:         if (&Apache::lonnet::is_course($1,$2)) {
                   6427:             $cid = $1.'_'.$2;
                   6428:             $sec = $3;
                   6429:         }
                   6430:     }
                   6431:     if ($cid) {
1.378     raeburn  6432:         if ($env{'request.role'} !~ /^cr/) {
                   6433:             $role = &Apache::lonnet::plaintext($role,&course_type());
1.1257    raeburn  6434:         } elsif ($role =~ m{^cr/($match_domain)/\1-domainconfig/(\w+)$}) {
1.1269    raeburn  6435:             if ($env{'request.role.desc'}) {
                   6436:                 $role = $env{'request.role.desc'};
                   6437:             } else {
                   6438:                 $role = &mt('Helpdesk[_1]','&nbsp;'.$2);
                   6439:             }
1.1257    raeburn  6440:         } else {
                   6441:             $role = (split(/\//,$role,4))[-1]; 
1.378     raeburn  6442:         }
1.1357    raeburn  6443:         if ($sec) {
                   6444:             $role .= ('&nbsp;'x2).'-&nbsp;'.&mt('section:').'&nbsp;'.$sec;
1.898     raeburn  6445:         }   
1.1357    raeburn  6446: 	$realm = $env{'course.'.$cid.'.description'};
1.378     raeburn  6447:     } else {
                   6448:         $role = &Apache::lonnet::plaintext($role);
1.54      www      6449:     }
1.433     albertel 6450: 
1.359     albertel 6451:     if (!$realm) { $realm='&nbsp;'; }
1.330     albertel 6452: 
1.438     albertel 6453:     my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329     albertel 6454: 
1.101     www      6455: # construct main body tag
1.359     albertel 6456:     my $bodytag = "<body $extra_body_attr>".
1.1235    raeburn  6457: 	&Apache::lontexconvert::init_math_support();
1.252     albertel 6458: 
1.1131    raeburn  6459:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   6460: 
1.1130    raeburn  6461:     if (($bodyonly) || ($no_nav_bar) || ($env{'form.inhibitmenu'} eq 'yes')) {
1.60      matthew  6462:         return $bodytag;
1.1130    raeburn  6463:     }
1.359     albertel 6464: 
1.954     raeburn  6465:     if ($public) {
1.433     albertel 6466: 	undef($role);
                   6467:     }
1.1318    raeburn  6468: 
1.1359    raeburn  6469:     my $showcrstitle = 1;
1.1357    raeburn  6470:     if (($cid) && ($env{'request.lti.login'})) {
1.1318    raeburn  6471:         if (ref($ltimenu) eq 'HASH') {
                   6472:             unless ($ltimenu->{'role'}) {
                   6473:                 undef($role);
                   6474:             }
                   6475:             unless ($ltimenu->{'coursetitle'}) {
                   6476:                 $realm='&nbsp;';
1.1359    raeburn  6477:                 $showcrstitle = 0;
                   6478:             }
                   6479:         }
                   6480:     } elsif (($cid) && ($menucoll)) {
                   6481:         if (ref($menuref) eq 'HASH') {
                   6482:             unless ($menuref->{'role'}) {
                   6483:                 undef($role);
                   6484:             }
                   6485:             unless ($menuref->{'crs'}) {
                   6486:                 $realm='&nbsp;';
                   6487:                 $showcrstitle = 0;
1.1318    raeburn  6488:             }
                   6489:         }
                   6490:     }
                   6491: 
1.762     bisitz   6492:     my $titleinfo = '<h1>'.$title.'</h1>';
1.359     albertel 6493:     #
                   6494:     # Extra info if you are the DC
                   6495:     my $dc_info = '';
1.1359    raeburn  6496:     if (($env{'user.adv'}) && ($env{'request.course.id'}) && $showcrstitle &&
1.1357    raeburn  6497:         (exists($env{'user.role.dc./'.$env{'course.'.$cid.'.domain'}.'/'}))) {
1.917     raeburn  6498:         $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380     www      6499:         $dc_info =~ s/\s+$//;
1.359     albertel 6500:     }
                   6501: 
1.1237    raeburn  6502:     my $crstype;
1.1357    raeburn  6503:     if ($cid) {
                   6504:         $crstype = $env{'course.'.$cid.'.type'};
1.1237    raeburn  6505:     } elsif ($args->{'crstype'}) {
                   6506:         $crstype = $args->{'crstype'};
                   6507:     }
                   6508:     if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) {
                   6509:         undef($role);
                   6510:     } else {
1.1242    raeburn  6511:         $role = '<span class="LC_nobreak">('.$role.')</span>' if ($role && !$env{'browser.mobile'});
1.1237    raeburn  6512:     }
1.853     droeschl 6513: 
1.903     droeschl 6514:         if ($env{'request.state'} eq 'construct') { $forcereg=1; }
                   6515: 
                   6516:         #    if ($env{'request.state'} eq 'construct') {
                   6517:         #        $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
                   6518:         #    }
                   6519: 
1.1130    raeburn  6520:         $bodytag .= Apache::lonhtmlcommon::scripttag(
1.1154    raeburn  6521:             Apache::lonmenu::utilityfunctions($httphost), 'start');
1.359     albertel 6522: 
1.1318    raeburn  6523:         unless ($args->{'no_primary_menu'}) {
1.1369    raeburn  6524:             my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu,$menucoll,$menuref,
                   6525:                                                               $args->{'links_disabled'});
1.359     albertel 6526: 
1.1318    raeburn  6527:             if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
                   6528:                 if ($dc_info) {
                   6529:                     $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
                   6530:                 }
                   6531:                 $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
                   6532:                                <em>$realm</em> $dc_info</div>|;
                   6533:                 return $bodytag;
                   6534:             }
1.894     droeschl 6535: 
1.1318    raeburn  6536:             unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
                   6537:                 $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
                   6538:             }
1.916     droeschl 6539: 
1.1318    raeburn  6540:             $bodytag .= $right;
1.852     droeschl 6541: 
1.1318    raeburn  6542:             if ($dc_info) {
                   6543:                 $dc_info = &dc_courseid_toggle($dc_info);
                   6544:             }
                   6545:             $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
1.917     raeburn  6546:         }
1.916     droeschl 6547: 
1.1169    raeburn  6548:         #if directed to not display the secondary menu, don't.  
1.1168    raeburn  6549:         if ($args->{'no_secondary_menu'}) {
                   6550:             return $bodytag;
                   6551:         }
1.1169    raeburn  6552:         #don't show menus for public users
1.954     raeburn  6553:         if (!$public){
1.1318    raeburn  6554:             unless ($args->{'no_inline_menu'}) {
                   6555:                 $bodytag .= Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu,
1.1359    raeburn  6556:                                                             $args->{'no_primary_menu'},
1.1369    raeburn  6557:                                                             $menucoll,$menuref,
                   6558:                                                             $args->{'links_disabled'});
1.1318    raeburn  6559:             }
1.903     droeschl 6560:             $bodytag .= Apache::lonmenu::serverform();
1.920     raeburn  6561:             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
                   6562:             if ($env{'request.state'} eq 'construct') {
1.962     droeschl 6563:                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,
1.1316    raeburn  6564:                                 $args->{'bread_crumbs'},'','',$hostname,$ltiscope,$ltiuri);
1.1096    raeburn  6565:             } elsif ($forcereg) {
                   6566:                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
1.1258    raeburn  6567:                                                             $args->{'group'},
1.1274    raeburn  6568:                                                             $args->{'hide_buttons'},
1.1316    raeburn  6569:                                                             $hostname,$ltiscope,$ltiuri);
1.1096    raeburn  6570:             } else {
                   6571:                 $bodytag .= 
                   6572:                     &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
                   6573:                                                         $forcereg,$args->{'group'},
                   6574:                                                         $args->{'bread_crumbs'},
1.1274    raeburn  6575:                                                         $advtoolsref,'',$hostname);
1.920     raeburn  6576:             }
1.903     droeschl 6577:         }else{
                   6578:             # this is to seperate menu from content when there's no secondary
                   6579:             # menu. Especially needed for public accessible ressources.
                   6580:             $bodytag .= '<hr style="clear:both" />';
                   6581:             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); 
1.235     raeburn  6582:         }
1.903     droeschl 6583: 
1.235     raeburn  6584:         return $bodytag;
1.182     matthew  6585: }
                   6586: 
1.917     raeburn  6587: sub dc_courseid_toggle {
                   6588:     my ($dc_info) = @_;
1.980     raeburn  6589:     return ' <span id="dccidtext" class="LC_cusr_subheading LC_nobreak">'.
1.1069    raeburn  6590:            '<a href="javascript:showCourseID();" class="LC_menubuttons_link">'.
1.917     raeburn  6591:            &mt('(More ...)').'</a></span>'.
                   6592:            '<div id="dccid" class="LC_dccid">'.$dc_info.'</div>';
                   6593: }
                   6594: 
1.330     albertel 6595: sub make_attr_string {
                   6596:     my ($register,$attr_ref) = @_;
                   6597: 
                   6598:     if ($attr_ref && !ref($attr_ref)) {
                   6599: 	die("addentries Must be a hash ref ".
                   6600: 	    join(':',caller(1))." ".
                   6601: 	    join(':',caller(0))." ");
                   6602:     }
                   6603: 
                   6604:     if ($register) {
1.339     albertel 6605: 	my ($on_load,$on_unload);
                   6606: 	foreach my $key (keys(%{$attr_ref})) {
                   6607: 	    if      (lc($key) eq 'onload') {
                   6608: 		$on_load.=$attr_ref->{$key}.';';
                   6609: 		delete($attr_ref->{$key});
                   6610: 
                   6611: 	    } elsif (lc($key) eq 'onunload') {
                   6612: 		$on_unload.=$attr_ref->{$key}.';';
                   6613: 		delete($attr_ref->{$key});
                   6614: 	    }
                   6615: 	}
1.953     droeschl 6616: 	$attr_ref->{'onload'}  = $on_load;
                   6617: 	$attr_ref->{'onunload'}= $on_unload;
1.330     albertel 6618:     }
1.339     albertel 6619: 
1.330     albertel 6620:     my $attr_string;
1.1159    raeburn  6621:     foreach my $attr (sort(keys(%$attr_ref))) {
1.330     albertel 6622: 	$attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
                   6623:     }
                   6624:     return $attr_string;
                   6625: }
                   6626: 
                   6627: 
1.182     matthew  6628: ###############################################
1.251     albertel 6629: ###############################################
                   6630: 
                   6631: =pod
                   6632: 
                   6633: =item * &endbodytag()
                   6634: 
                   6635: Returns a uniform footer for LON-CAPA web pages.
                   6636: 
1.635     raeburn  6637: Inputs: 1 - optional reference to an args hash
                   6638: If in the hash, key for noredirectlink has a value which evaluates to true,
                   6639: a 'Continue' link is not displayed if the page contains an
                   6640: internal redirect in the <head></head> section,
                   6641: i.e., $env{'internal.head.redirect'} exists   
1.251     albertel 6642: 
                   6643: =cut
                   6644: 
                   6645: sub endbodytag {
1.635     raeburn  6646:     my ($args) = @_;
1.1080    raeburn  6647:     my $endbodytag;
                   6648:     unless ((ref($args) eq 'HASH') && ($args->{'notbody'})) {
                   6649:         $endbodytag='</body>';
                   6650:     }
1.315     albertel 6651:     if ( exists( $env{'internal.head.redirect'} ) ) {
1.635     raeburn  6652:         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
                   6653: 	    $endbodytag=
                   6654: 	        "<br /><a href=\"$env{'internal.head.redirect'}\">".
                   6655: 	        &mt('Continue').'</a>'.
                   6656: 	        $endbodytag;
                   6657:         }
1.315     albertel 6658:     }
1.251     albertel 6659:     return $endbodytag;
                   6660: }
                   6661: 
1.352     albertel 6662: =pod
                   6663: 
                   6664: =item * &standard_css()
                   6665: 
                   6666: Returns a style sheet
                   6667: 
                   6668: Inputs: (all optional)
                   6669:             domain         -> force to color decorate a page for a specific
                   6670:                                domain
                   6671:             function       -> force usage of a specific rolish color scheme
                   6672:             bgcolor        -> override the default page bgcolor
                   6673: 
                   6674: =cut
                   6675: 
1.343     albertel 6676: sub standard_css {
1.345     albertel 6677:     my ($function,$domain,$bgcolor) = @_;
1.352     albertel 6678:     $function  = &get_users_function() if (!$function);
                   6679:     my $img    = &designparm($function.'.img',   $domain);
                   6680:     my $tabbg  = &designparm($function.'.tabbg', $domain);
                   6681:     my $font   = &designparm($function.'.font',  $domain);
1.801     tempelho 6682:     my $fontmenu = &designparm($function.'.fontmenu', $domain);
1.791     tempelho 6683: #second colour for later usage
1.345     albertel 6684:     my $sidebg = &designparm($function.'.sidebg',$domain);
1.382     albertel 6685:     my $pgbg_or_bgcolor =
                   6686: 	         $bgcolor ||
1.352     albertel 6687: 	         &designparm($function.'.pgbg',  $domain);
1.382     albertel 6688:     my $pgbg   = &designparm($function.'.pgbg',  $domain);
1.352     albertel 6689:     my $alink  = &designparm($function.'.alink', $domain);
                   6690:     my $vlink  = &designparm($function.'.vlink', $domain);
                   6691:     my $link   = &designparm($function.'.link',  $domain);
                   6692: 
1.602     albertel 6693:     my $sans                 = 'Verdana,Arial,Helvetica,sans-serif';
1.395     albertel 6694:     my $mono                 = 'monospace';
1.850     bisitz   6695:     my $data_table_head      = $sidebg;
                   6696:     my $data_table_light     = '#FAFAFA';
1.1060    bisitz   6697:     my $data_table_dark      = '#E0E0E0';
1.470     banghart 6698:     my $data_table_darker    = '#CCCCCC';
1.349     albertel 6699:     my $data_table_highlight = '#FFFF00';
1.352     albertel 6700:     my $mail_new             = '#FFBB77';
                   6701:     my $mail_new_hover       = '#DD9955';
                   6702:     my $mail_read            = '#BBBB77';
                   6703:     my $mail_read_hover      = '#999944';
                   6704:     my $mail_replied         = '#AAAA88';
                   6705:     my $mail_replied_hover   = '#888855';
                   6706:     my $mail_other           = '#99BBBB';
                   6707:     my $mail_other_hover     = '#669999';
1.391     albertel 6708:     my $table_header         = '#DDDDDD';
1.489     raeburn  6709:     my $feedback_link_bg     = '#BBBBBB';
1.911     bisitz   6710:     my $lg_border_color      = '#C8C8C8';
1.952     onken    6711:     my $button_hover         = '#BF2317';
1.392     albertel 6712: 
1.608     albertel 6713:     my $border = ($env{'browser.type'} eq 'explorer' ||
1.911     bisitz   6714:       $env{'browser.type'} eq 'safari'     ) ? '0 2px 0 2px'
                   6715:                                              : '0 3px 0 4px';
1.448     albertel 6716: 
1.523     albertel 6717: 
1.343     albertel 6718:     return <<END;
1.947     droeschl 6719: 
                   6720: /* needed for iframe to allow 100% height in FF */
                   6721: body, html { 
                   6722:     margin: 0;
                   6723:     padding: 0 0.5%;
                   6724:     height: 99%; /* to avoid scrollbars */
                   6725: }
                   6726: 
1.795     www      6727: body {
1.911     bisitz   6728:   font-family: $sans;
                   6729:   line-height:130%;
                   6730:   font-size:0.83em;
                   6731:   color:$font;
1.795     www      6732: }
                   6733: 
1.959     onken    6734: a:focus,
                   6735: a:focus img {
1.795     www      6736:   color: red;
                   6737: }
1.698     harmsja  6738: 
1.911     bisitz   6739: form, .inline {
                   6740:   display: inline;
1.795     www      6741: }
1.721     harmsja  6742: 
1.795     www      6743: .LC_right {
1.911     bisitz   6744:   text-align:right;
1.795     www      6745: }
                   6746: 
                   6747: .LC_middle {
1.911     bisitz   6748:   vertical-align:middle;
1.795     www      6749: }
1.721     harmsja  6750: 
1.1130    raeburn  6751: .LC_floatleft {
                   6752:   float: left;
                   6753: }
                   6754: 
                   6755: .LC_floatright {
                   6756:   float: right;
                   6757: }
                   6758: 
1.911     bisitz   6759: .LC_400Box {
                   6760:   width:400px;
                   6761: }
1.721     harmsja  6762: 
1.947     droeschl 6763: .LC_iframecontainer {
                   6764:     width: 98%;
                   6765:     margin: 0;
                   6766:     position: fixed;
                   6767:     top: 8.5em;
                   6768:     bottom: 0;
                   6769: }
                   6770: 
                   6771: .LC_iframecontainer iframe{
                   6772:     border: none;
                   6773:     width: 100%;
                   6774:     height: 100%;
                   6775: }
                   6776: 
1.778     bisitz   6777: .LC_filename {
                   6778:   font-family: $mono;
                   6779:   white-space:pre;
1.921     bisitz   6780:   font-size: 120%;
1.778     bisitz   6781: }
                   6782: 
                   6783: .LC_fileicon {
                   6784:   border: none;
                   6785:   height: 1.3em;
                   6786:   vertical-align: text-bottom;
                   6787:   margin-right: 0.3em;
                   6788:   text-decoration:none;
                   6789: }
                   6790: 
1.1008    www      6791: .LC_setting {
                   6792:   text-decoration:underline;
                   6793: }
                   6794: 
1.350     albertel 6795: .LC_error {
                   6796:   color: red;
                   6797: }
1.795     www      6798: 
1.1097    bisitz   6799: .LC_warning {
                   6800:   color: darkorange;
                   6801: }
                   6802: 
1.457     albertel 6803: .LC_diff_removed {
1.733     bisitz   6804:   color: red;
1.394     albertel 6805: }
1.532     albertel 6806: 
                   6807: .LC_info,
1.457     albertel 6808: .LC_success,
                   6809: .LC_diff_added {
1.350     albertel 6810:   color: green;
                   6811: }
1.795     www      6812: 
1.802     bisitz   6813: div.LC_confirm_box {
                   6814:   background-color: #FAFAFA;
                   6815:   border: 1px solid $lg_border_color;
                   6816:   margin-right: 0;
                   6817:   padding: 5px;
                   6818: }
                   6819: 
                   6820: div.LC_confirm_box .LC_error img,
                   6821: div.LC_confirm_box .LC_success img {
                   6822:   vertical-align: middle;
                   6823: }
                   6824: 
1.1242    raeburn  6825: .LC_maxwidth {
                   6826:   max-width: 100%;
                   6827:   height: auto;
                   6828: }
                   6829: 
1.1243    raeburn  6830: .LC_textsize_mobile {
                   6831:   \@media only screen and (max-device-width: 480px) {
                   6832:       -webkit-text-size-adjust:100%; -moz-text-size-adjust:100%; -ms-text-size-adjust:100%;
                   6833:   }
                   6834: }
                   6835: 
1.440     albertel 6836: .LC_icon {
1.771     droeschl 6837:   border: none;
1.790     droeschl 6838:   vertical-align: middle;
1.771     droeschl 6839: }
                   6840: 
1.543     albertel 6841: .LC_docs_spacer {
                   6842:   width: 25px;
                   6843:   height: 1px;
1.771     droeschl 6844:   border: none;
1.543     albertel 6845: }
1.346     albertel 6846: 
1.532     albertel 6847: .LC_internal_info {
1.735     bisitz   6848:   color: #999999;
1.532     albertel 6849: }
                   6850: 
1.794     www      6851: .LC_discussion {
1.1050    www      6852:   background: $data_table_dark;
1.911     bisitz   6853:   border: 1px solid black;
                   6854:   margin: 2px;
1.794     www      6855: }
                   6856: 
                   6857: .LC_disc_action_left {
1.1050    www      6858:   background: $sidebg;
1.911     bisitz   6859:   text-align: left;
1.1050    www      6860:   padding: 4px;
                   6861:   margin: 2px;
1.794     www      6862: }
                   6863: 
                   6864: .LC_disc_action_right {
1.1050    www      6865:   background: $sidebg;
1.911     bisitz   6866:   text-align: right;
1.1050    www      6867:   padding: 4px;
                   6868:   margin: 2px;
1.794     www      6869: }
                   6870: 
                   6871: .LC_disc_new_item {
1.911     bisitz   6872:   background: white;
                   6873:   border: 2px solid red;
1.1050    www      6874:   margin: 4px;
                   6875:   padding: 4px;
1.794     www      6876: }
                   6877: 
                   6878: .LC_disc_old_item {
1.911     bisitz   6879:   background: white;
1.1050    www      6880:   margin: 4px;
                   6881:   padding: 4px;
1.794     www      6882: }
                   6883: 
1.458     albertel 6884: table.LC_pastsubmission {
                   6885:   border: 1px solid black;
                   6886:   margin: 2px;
                   6887: }
                   6888: 
1.924     bisitz   6889: table#LC_menubuttons {
1.345     albertel 6890:   width: 100%;
                   6891:   background: $pgbg;
1.392     albertel 6892:   border: 2px;
1.402     albertel 6893:   border-collapse: separate;
1.803     bisitz   6894:   padding: 0;
1.345     albertel 6895: }
1.392     albertel 6896: 
1.801     tempelho 6897: table#LC_title_bar a {
                   6898:   color: $fontmenu;
                   6899: }
1.836     bisitz   6900: 
1.807     droeschl 6901: table#LC_title_bar {
1.819     tempelho 6902:   clear: both;
1.836     bisitz   6903:   display: none;
1.807     droeschl 6904: }
                   6905: 
1.795     www      6906: table#LC_title_bar,
1.933     droeschl 6907: table.LC_breadcrumbs, /* obsolete? */
1.393     albertel 6908: table#LC_title_bar.LC_with_remote {
1.359     albertel 6909:   width: 100%;
1.392     albertel 6910:   border-color: $pgbg;
                   6911:   border-style: solid;
                   6912:   border-width: $border;
1.379     albertel 6913:   background: $pgbg;
1.801     tempelho 6914:   color: $fontmenu;
1.392     albertel 6915:   border-collapse: collapse;
1.803     bisitz   6916:   padding: 0;
1.819     tempelho 6917:   margin: 0;
1.359     albertel 6918: }
1.795     www      6919: 
1.933     droeschl 6920: ul.LC_breadcrumb_tools_outerlist {
1.913     droeschl 6921:     margin: 0;
                   6922:     padding: 0;
1.933     droeschl 6923:     position: relative;
                   6924:     list-style: none;
1.913     droeschl 6925: }
1.933     droeschl 6926: ul.LC_breadcrumb_tools_outerlist li {
1.913     droeschl 6927:     display: inline;
                   6928: }
1.933     droeschl 6929: 
                   6930: .LC_breadcrumb_tools_navigation {
1.913     droeschl 6931:     padding: 0;
1.933     droeschl 6932:     margin: 0;
                   6933:     float: left;
1.913     droeschl 6934: }
1.933     droeschl 6935: .LC_breadcrumb_tools_tools {
                   6936:     padding: 0;
                   6937:     margin: 0;
1.913     droeschl 6938:     float: right;
                   6939: }
                   6940: 
1.1240    raeburn  6941: .LC_placement_prog {
                   6942:     padding-right: 20px;
                   6943:     font-weight: bold;
                   6944:     font-size: 90%;
                   6945: }
                   6946: 
1.359     albertel 6947: table#LC_title_bar td {
                   6948:   background: $tabbg;
                   6949: }
1.795     www      6950: 
1.911     bisitz   6951: table#LC_menubuttons img {
1.803     bisitz   6952:   border: none;
1.346     albertel 6953: }
1.795     www      6954: 
1.842     droeschl 6955: .LC_breadcrumbs_component {
1.911     bisitz   6956:   float: right;
                   6957:   margin: 0 1em;
1.357     albertel 6958: }
1.842     droeschl 6959: .LC_breadcrumbs_component img {
1.911     bisitz   6960:   vertical-align: middle;
1.777     tempelho 6961: }
1.795     www      6962: 
1.1243    raeburn  6963: .LC_breadcrumbs_hoverable {
                   6964:   background: $sidebg;
                   6965: }
                   6966: 
1.383     albertel 6967: td.LC_table_cell_checkbox {
                   6968:   text-align: center;
                   6969: }
1.795     www      6970: 
                   6971: .LC_fontsize_small {
1.911     bisitz   6972:   font-size: 70%;
1.705     tempelho 6973: }
                   6974: 
1.844     bisitz   6975: #LC_breadcrumbs {
1.911     bisitz   6976:   clear:both;
                   6977:   background: $sidebg;
                   6978:   border-bottom: 1px solid $lg_border_color;
                   6979:   line-height: 2.5em;
1.933     droeschl 6980:   overflow: hidden;
1.911     bisitz   6981:   margin: 0;
                   6982:   padding: 0;
1.995     raeburn  6983:   text-align: left;
1.819     tempelho 6984: }
1.862     bisitz   6985: 
1.1098    bisitz   6986: .LC_head_subbox, .LC_actionbox {
1.911     bisitz   6987:   clear:both;
                   6988:   background: #F8F8F8; /* $sidebg; */
1.915     droeschl 6989:   border: 1px solid $sidebg;
1.1098    bisitz   6990:   margin: 0 0 10px 0;
1.966     bisitz   6991:   padding: 3px;
1.995     raeburn  6992:   text-align: left;
1.822     bisitz   6993: }
                   6994: 
1.795     www      6995: .LC_fontsize_medium {
1.911     bisitz   6996:   font-size: 85%;
1.705     tempelho 6997: }
                   6998: 
1.795     www      6999: .LC_fontsize_large {
1.911     bisitz   7000:   font-size: 120%;
1.705     tempelho 7001: }
                   7002: 
1.346     albertel 7003: .LC_menubuttons_inline_text {
                   7004:   color: $font;
1.698     harmsja  7005:   font-size: 90%;
1.701     harmsja  7006:   padding-left:3px;
1.346     albertel 7007: }
                   7008: 
1.934     droeschl 7009: .LC_menubuttons_inline_text img{
                   7010:   vertical-align: middle;
                   7011: }
                   7012: 
1.1051    www      7013: li.LC_menubuttons_inline_text img {
1.951     onken    7014:   cursor:pointer;
1.1002    droeschl 7015:   text-decoration: none;
1.951     onken    7016: }
                   7017: 
1.526     www      7018: .LC_menubuttons_link {
                   7019:   text-decoration: none;
                   7020: }
1.795     www      7021: 
1.522     albertel 7022: .LC_menubuttons_category {
1.521     www      7023:   color: $font;
1.526     www      7024:   background: $pgbg;
1.521     www      7025:   font-size: larger;
                   7026:   font-weight: bold;
                   7027: }
                   7028: 
1.346     albertel 7029: td.LC_menubuttons_text {
1.911     bisitz   7030:   color: $font;
1.346     albertel 7031: }
1.706     harmsja  7032: 
1.346     albertel 7033: .LC_current_location {
                   7034:   background: $tabbg;
                   7035: }
1.795     www      7036: 
1.1286    raeburn  7037: td.LC_zero_height {
                   7038:   line-height: 0; 
                   7039:   cellpadding: 0;
                   7040: }
                   7041: 
1.938     bisitz   7042: table.LC_data_table {
1.347     albertel 7043:   border: 1px solid #000000;
1.402     albertel 7044:   border-collapse: separate;
1.426     albertel 7045:   border-spacing: 1px;
1.610     albertel 7046:   background: $pgbg;
1.347     albertel 7047: }
1.795     www      7048: 
1.422     albertel 7049: .LC_data_table_dense {
                   7050:   font-size: small;
                   7051: }
1.795     www      7052: 
1.507     raeburn  7053: table.LC_nested_outer {
                   7054:   border: 1px solid #000000;
1.589     raeburn  7055:   border-collapse: collapse;
1.803     bisitz   7056:   border-spacing: 0;
1.507     raeburn  7057:   width: 100%;
                   7058: }
1.795     www      7059: 
1.879     raeburn  7060: table.LC_innerpickbox,
1.507     raeburn  7061: table.LC_nested {
1.803     bisitz   7062:   border: none;
1.589     raeburn  7063:   border-collapse: collapse;
1.803     bisitz   7064:   border-spacing: 0;
1.507     raeburn  7065:   width: 100%;
                   7066: }
1.795     www      7067: 
1.911     bisitz   7068: table.LC_data_table tr th,
                   7069: table.LC_calendar tr th,
1.879     raeburn  7070: table.LC_prior_tries tr th,
                   7071: table.LC_innerpickbox tr th {
1.349     albertel 7072:   font-weight: bold;
                   7073:   background-color: $data_table_head;
1.801     tempelho 7074:   color:$fontmenu;
1.701     harmsja  7075:   font-size:90%;
1.347     albertel 7076: }
1.795     www      7077: 
1.879     raeburn  7078: table.LC_innerpickbox tr th,
                   7079: table.LC_innerpickbox tr td {
                   7080:   vertical-align: top;
                   7081: }
                   7082: 
1.711     raeburn  7083: table.LC_data_table tr.LC_info_row > td {
1.735     bisitz   7084:   background-color: #CCCCCC;
1.711     raeburn  7085:   font-weight: bold;
                   7086:   text-align: left;
                   7087: }
1.795     www      7088: 
1.912     bisitz   7089: table.LC_data_table tr.LC_odd_row > td {
                   7090:   background-color: $data_table_light;
                   7091:   padding: 2px;
                   7092:   vertical-align: top;
                   7093: }
                   7094: 
1.809     bisitz   7095: table.LC_pick_box tr > td.LC_odd_row {
1.349     albertel 7096:   background-color: $data_table_light;
1.912     bisitz   7097:   vertical-align: top;
                   7098: }
                   7099: 
                   7100: table.LC_data_table tr.LC_even_row > td {
                   7101:   background-color: $data_table_dark;
1.425     albertel 7102:   padding: 2px;
1.900     bisitz   7103:   vertical-align: top;
1.347     albertel 7104: }
1.795     www      7105: 
1.809     bisitz   7106: table.LC_pick_box tr > td.LC_even_row {
1.349     albertel 7107:   background-color: $data_table_dark;
1.900     bisitz   7108:   vertical-align: top;
1.347     albertel 7109: }
1.795     www      7110: 
1.425     albertel 7111: table.LC_data_table tr.LC_data_table_highlight td {
                   7112:   background-color: $data_table_darker;
                   7113: }
1.795     www      7114: 
1.639     raeburn  7115: table.LC_data_table tr td.LC_leftcol_header {
                   7116:   background-color: $data_table_head;
                   7117:   font-weight: bold;
                   7118: }
1.795     www      7119: 
1.451     albertel 7120: table.LC_data_table tr.LC_empty_row td,
1.507     raeburn  7121: table.LC_nested tr.LC_empty_row td {
1.421     albertel 7122:   font-weight: bold;
                   7123:   font-style: italic;
                   7124:   text-align: center;
                   7125:   padding: 8px;
1.347     albertel 7126: }
1.795     www      7127: 
1.1114    raeburn  7128: table.LC_data_table tr.LC_empty_row td,
                   7129: table.LC_data_table tr.LC_footer_row td {
1.940     bisitz   7130:   background-color: $sidebg;
                   7131: }
                   7132: 
                   7133: table.LC_nested tr.LC_empty_row td {
                   7134:   background-color: #FFFFFF;
                   7135: }
                   7136: 
1.890     droeschl 7137: table.LC_caption {
                   7138: }
                   7139: 
1.507     raeburn  7140: table.LC_nested tr.LC_empty_row td {
1.465     albertel 7141:   padding: 4ex
                   7142: }
1.795     www      7143: 
1.507     raeburn  7144: table.LC_nested_outer tr th {
                   7145:   font-weight: bold;
1.801     tempelho 7146:   color:$fontmenu;
1.507     raeburn  7147:   background-color: $data_table_head;
1.701     harmsja  7148:   font-size: small;
1.507     raeburn  7149:   border-bottom: 1px solid #000000;
                   7150: }
1.795     www      7151: 
1.507     raeburn  7152: table.LC_nested_outer tr td.LC_subheader {
                   7153:   background-color: $data_table_head;
                   7154:   font-weight: bold;
                   7155:   font-size: small;
                   7156:   border-bottom: 1px solid #000000;
                   7157:   text-align: right;
1.451     albertel 7158: }
1.795     www      7159: 
1.507     raeburn  7160: table.LC_nested tr.LC_info_row td {
1.735     bisitz   7161:   background-color: #CCCCCC;
1.451     albertel 7162:   font-weight: bold;
                   7163:   font-size: small;
1.507     raeburn  7164:   text-align: center;
                   7165: }
1.795     www      7166: 
1.589     raeburn  7167: table.LC_nested tr.LC_info_row td.LC_left_item,
                   7168: table.LC_nested_outer tr th.LC_left_item {
1.507     raeburn  7169:   text-align: left;
1.451     albertel 7170: }
1.795     www      7171: 
1.507     raeburn  7172: table.LC_nested td {
1.735     bisitz   7173:   background-color: #FFFFFF;
1.451     albertel 7174:   font-size: small;
1.507     raeburn  7175: }
1.795     www      7176: 
1.507     raeburn  7177: table.LC_nested_outer tr th.LC_right_item,
                   7178: table.LC_nested tr.LC_info_row td.LC_right_item,
                   7179: table.LC_nested tr.LC_odd_row td.LC_right_item,
                   7180: table.LC_nested tr td.LC_right_item {
1.451     albertel 7181:   text-align: right;
                   7182: }
                   7183: 
1.507     raeburn  7184: table.LC_nested tr.LC_odd_row td {
1.735     bisitz   7185:   background-color: #EEEEEE;
1.451     albertel 7186: }
                   7187: 
1.473     raeburn  7188: table.LC_createuser {
                   7189: }
                   7190: 
                   7191: table.LC_createuser tr.LC_section_row td {
1.701     harmsja  7192:   font-size: small;
1.473     raeburn  7193: }
                   7194: 
                   7195: table.LC_createuser tr.LC_info_row td  {
1.735     bisitz   7196:   background-color: #CCCCCC;
1.473     raeburn  7197:   font-weight: bold;
                   7198:   text-align: center;
                   7199: }
                   7200: 
1.349     albertel 7201: table.LC_calendar {
                   7202:   border: 1px solid #000000;
                   7203:   border-collapse: collapse;
1.917     raeburn  7204:   width: 98%;
1.349     albertel 7205: }
1.795     www      7206: 
1.349     albertel 7207: table.LC_calendar_pickdate {
                   7208:   font-size: xx-small;
                   7209: }
1.795     www      7210: 
1.349     albertel 7211: table.LC_calendar tr td {
                   7212:   border: 1px solid #000000;
                   7213:   vertical-align: top;
1.917     raeburn  7214:   width: 14%;
1.349     albertel 7215: }
1.795     www      7216: 
1.349     albertel 7217: table.LC_calendar tr td.LC_calendar_day_empty {
                   7218:   background-color: $data_table_dark;
                   7219: }
1.795     www      7220: 
1.779     bisitz   7221: table.LC_calendar tr td.LC_calendar_day_current {
                   7222:   background-color: $data_table_highlight;
1.777     tempelho 7223: }
1.795     www      7224: 
1.938     bisitz   7225: table.LC_data_table tr td.LC_mail_new {
1.349     albertel 7226:   background-color: $mail_new;
                   7227: }
1.795     www      7228: 
1.938     bisitz   7229: table.LC_data_table tr.LC_mail_new:hover {
1.349     albertel 7230:   background-color: $mail_new_hover;
                   7231: }
1.795     www      7232: 
1.938     bisitz   7233: table.LC_data_table tr td.LC_mail_read {
1.349     albertel 7234:   background-color: $mail_read;
                   7235: }
1.795     www      7236: 
1.938     bisitz   7237: /*
                   7238: table.LC_data_table tr.LC_mail_read:hover {
1.349     albertel 7239:   background-color: $mail_read_hover;
                   7240: }
1.938     bisitz   7241: */
1.795     www      7242: 
1.938     bisitz   7243: table.LC_data_table tr td.LC_mail_replied {
1.349     albertel 7244:   background-color: $mail_replied;
                   7245: }
1.795     www      7246: 
1.938     bisitz   7247: /*
                   7248: table.LC_data_table tr.LC_mail_replied:hover {
1.349     albertel 7249:   background-color: $mail_replied_hover;
                   7250: }
1.938     bisitz   7251: */
1.795     www      7252: 
1.938     bisitz   7253: table.LC_data_table tr td.LC_mail_other {
1.349     albertel 7254:   background-color: $mail_other;
                   7255: }
1.795     www      7256: 
1.938     bisitz   7257: /*
                   7258: table.LC_data_table tr.LC_mail_other:hover {
1.349     albertel 7259:   background-color: $mail_other_hover;
                   7260: }
1.938     bisitz   7261: */
1.494     raeburn  7262: 
1.777     tempelho 7263: table.LC_data_table tr > td.LC_browser_file,
                   7264: table.LC_data_table tr > td.LC_browser_file_published {
1.899     bisitz   7265:   background: #AAEE77;
1.389     albertel 7266: }
1.795     www      7267: 
1.777     tempelho 7268: table.LC_data_table tr > td.LC_browser_file_locked,
                   7269: table.LC_data_table tr > td.LC_browser_file_unpublished {
1.389     albertel 7270:   background: #FFAA99;
1.387     albertel 7271: }
1.795     www      7272: 
1.777     tempelho 7273: table.LC_data_table tr > td.LC_browser_file_obsolete {
1.899     bisitz   7274:   background: #888888;
1.779     bisitz   7275: }
1.795     www      7276: 
1.777     tempelho 7277: table.LC_data_table tr > td.LC_browser_file_modified,
1.779     bisitz   7278: table.LC_data_table tr > td.LC_browser_file_metamodified {
1.899     bisitz   7279:   background: #F8F866;
1.777     tempelho 7280: }
1.795     www      7281: 
1.696     bisitz   7282: table.LC_data_table tr.LC_browser_folder > td {
1.899     bisitz   7283:   background: #E0E8FF;
1.387     albertel 7284: }
1.696     bisitz   7285: 
1.707     bisitz   7286: table.LC_data_table tr > td.LC_roles_is {
1.911     bisitz   7287:   /* background: #77FF77; */
1.707     bisitz   7288: }
1.795     www      7289: 
1.707     bisitz   7290: table.LC_data_table tr > td.LC_roles_future {
1.939     bisitz   7291:   border-right: 8px solid #FFFF77;
1.707     bisitz   7292: }
1.795     www      7293: 
1.707     bisitz   7294: table.LC_data_table tr > td.LC_roles_will {
1.939     bisitz   7295:   border-right: 8px solid #FFAA77;
1.707     bisitz   7296: }
1.795     www      7297: 
1.707     bisitz   7298: table.LC_data_table tr > td.LC_roles_expired {
1.939     bisitz   7299:   border-right: 8px solid #FF7777;
1.707     bisitz   7300: }
1.795     www      7301: 
1.707     bisitz   7302: table.LC_data_table tr > td.LC_roles_will_not {
1.939     bisitz   7303:   border-right: 8px solid #AAFF77;
1.707     bisitz   7304: }
1.795     www      7305: 
1.707     bisitz   7306: table.LC_data_table tr > td.LC_roles_selected {
1.939     bisitz   7307:   border-right: 8px solid #11CC55;
1.707     bisitz   7308: }
                   7309: 
1.388     albertel 7310: span.LC_current_location {
1.701     harmsja  7311:   font-size:larger;
1.388     albertel 7312:   background: $pgbg;
                   7313: }
1.387     albertel 7314: 
1.1029    www      7315: span.LC_current_nav_location {
                   7316:   font-weight:bold;
                   7317:   background: $sidebg;
                   7318: }
                   7319: 
1.395     albertel 7320: span.LC_parm_menu_item {
                   7321:   font-size: larger;
                   7322: }
1.795     www      7323: 
1.395     albertel 7324: span.LC_parm_scope_all {
                   7325:   color: red;
                   7326: }
1.795     www      7327: 
1.395     albertel 7328: span.LC_parm_scope_folder {
                   7329:   color: green;
                   7330: }
1.795     www      7331: 
1.395     albertel 7332: span.LC_parm_scope_resource {
                   7333:   color: orange;
                   7334: }
1.795     www      7335: 
1.395     albertel 7336: span.LC_parm_part {
                   7337:   color: blue;
                   7338: }
1.795     www      7339: 
1.911     bisitz   7340: span.LC_parm_folder,
                   7341: span.LC_parm_symb {
1.395     albertel 7342:   font-size: x-small;
                   7343:   font-family: $mono;
                   7344:   color: #AAAAAA;
                   7345: }
                   7346: 
1.977     bisitz   7347: ul.LC_parm_parmlist li {
                   7348:   display: inline-block;
                   7349:   padding: 0.3em 0.8em;
                   7350:   vertical-align: top;
                   7351:   width: 150px;
                   7352:   border-top:1px solid $lg_border_color;
                   7353: }
                   7354: 
1.795     www      7355: td.LC_parm_overview_level_menu,
                   7356: td.LC_parm_overview_map_menu,
                   7357: td.LC_parm_overview_parm_selectors,
                   7358: td.LC_parm_overview_restrictions  {
1.396     albertel 7359:   border: 1px solid black;
                   7360:   border-collapse: collapse;
                   7361: }
1.795     www      7362: 
1.1285    raeburn  7363: span.LC_parm_recursive,
                   7364: td.LC_parm_recursive {
                   7365:   font-weight: bold;
                   7366:   font-size: smaller;
                   7367: }
                   7368: 
1.396     albertel 7369: table.LC_parm_overview_restrictions td {
                   7370:   border-width: 1px 4px 1px 4px;
                   7371:   border-style: solid;
                   7372:   border-color: $pgbg;
                   7373:   text-align: center;
                   7374: }
1.795     www      7375: 
1.396     albertel 7376: table.LC_parm_overview_restrictions th {
                   7377:   background: $tabbg;
                   7378:   border-width: 1px 4px 1px 4px;
                   7379:   border-style: solid;
                   7380:   border-color: $pgbg;
                   7381: }
1.795     www      7382: 
1.398     albertel 7383: table#LC_helpmenu {
1.803     bisitz   7384:   border: none;
1.398     albertel 7385:   height: 55px;
1.803     bisitz   7386:   border-spacing: 0;
1.398     albertel 7387: }
                   7388: 
                   7389: table#LC_helpmenu fieldset legend {
                   7390:   font-size: larger;
                   7391: }
1.795     www      7392: 
1.397     albertel 7393: table#LC_helpmenu_links {
                   7394:   width: 100%;
                   7395:   border: 1px solid black;
                   7396:   background: $pgbg;
1.803     bisitz   7397:   padding: 0;
1.397     albertel 7398:   border-spacing: 1px;
                   7399: }
1.795     www      7400: 
1.397     albertel 7401: table#LC_helpmenu_links tr td {
                   7402:   padding: 1px;
                   7403:   background: $tabbg;
1.399     albertel 7404:   text-align: center;
                   7405:   font-weight: bold;
1.397     albertel 7406: }
1.396     albertel 7407: 
1.795     www      7408: table#LC_helpmenu_links a:link,
                   7409: table#LC_helpmenu_links a:visited,
1.397     albertel 7410: table#LC_helpmenu_links a:active {
                   7411:   text-decoration: none;
                   7412:   color: $font;
                   7413: }
1.795     www      7414: 
1.397     albertel 7415: table#LC_helpmenu_links a:hover {
                   7416:   text-decoration: underline;
                   7417:   color: $vlink;
                   7418: }
1.396     albertel 7419: 
1.417     albertel 7420: .LC_chrt_popup_exists {
                   7421:   border: 1px solid #339933;
                   7422:   margin: -1px;
                   7423: }
1.795     www      7424: 
1.417     albertel 7425: .LC_chrt_popup_up {
                   7426:   border: 1px solid yellow;
                   7427:   margin: -1px;
                   7428: }
1.795     www      7429: 
1.417     albertel 7430: .LC_chrt_popup {
                   7431:   border: 1px solid #8888FF;
                   7432:   background: #CCCCFF;
                   7433: }
1.795     www      7434: 
1.421     albertel 7435: table.LC_pick_box {
                   7436:   border-collapse: separate;
                   7437:   background: white;
                   7438:   border: 1px solid black;
                   7439:   border-spacing: 1px;
                   7440: }
1.795     www      7441: 
1.421     albertel 7442: table.LC_pick_box td.LC_pick_box_title {
1.850     bisitz   7443:   background: $sidebg;
1.421     albertel 7444:   font-weight: bold;
1.900     bisitz   7445:   text-align: left;
1.740     bisitz   7446:   vertical-align: top;
1.421     albertel 7447:   width: 184px;
                   7448:   padding: 8px;
                   7449: }
1.795     www      7450: 
1.579     raeburn  7451: table.LC_pick_box td.LC_pick_box_value {
                   7452:   text-align: left;
                   7453:   padding: 8px;
                   7454: }
1.795     www      7455: 
1.579     raeburn  7456: table.LC_pick_box td.LC_pick_box_select {
                   7457:   text-align: left;
                   7458:   padding: 8px;
                   7459: }
1.795     www      7460: 
1.424     albertel 7461: table.LC_pick_box td.LC_pick_box_separator {
1.803     bisitz   7462:   padding: 0;
1.421     albertel 7463:   height: 1px;
                   7464:   background: black;
                   7465: }
1.795     www      7466: 
1.421     albertel 7467: table.LC_pick_box td.LC_pick_box_submit {
                   7468:   text-align: right;
                   7469: }
1.795     www      7470: 
1.579     raeburn  7471: table.LC_pick_box td.LC_evenrow_value {
                   7472:   text-align: left;
                   7473:   padding: 8px;
                   7474:   background-color: $data_table_light;
                   7475: }
1.795     www      7476: 
1.579     raeburn  7477: table.LC_pick_box td.LC_oddrow_value {
                   7478:   text-align: left;
                   7479:   padding: 8px;
                   7480:   background-color: $data_table_light;
                   7481: }
1.795     www      7482: 
1.579     raeburn  7483: span.LC_helpform_receipt_cat {
                   7484:   font-weight: bold;
                   7485: }
1.795     www      7486: 
1.424     albertel 7487: table.LC_group_priv_box {
                   7488:   background: white;
                   7489:   border: 1px solid black;
                   7490:   border-spacing: 1px;
                   7491: }
1.795     www      7492: 
1.424     albertel 7493: table.LC_group_priv_box td.LC_pick_box_title {
                   7494:   background: $tabbg;
                   7495:   font-weight: bold;
                   7496:   text-align: right;
                   7497:   width: 184px;
                   7498: }
1.795     www      7499: 
1.424     albertel 7500: table.LC_group_priv_box td.LC_groups_fixed {
                   7501:   background: $data_table_light;
                   7502:   text-align: center;
                   7503: }
1.795     www      7504: 
1.424     albertel 7505: table.LC_group_priv_box td.LC_groups_optional {
                   7506:   background: $data_table_dark;
                   7507:   text-align: center;
                   7508: }
1.795     www      7509: 
1.424     albertel 7510: table.LC_group_priv_box td.LC_groups_functionality {
                   7511:   background: $data_table_darker;
                   7512:   text-align: center;
                   7513:   font-weight: bold;
                   7514: }
1.795     www      7515: 
1.424     albertel 7516: table.LC_group_priv td {
                   7517:   text-align: left;
1.803     bisitz   7518:   padding: 0;
1.424     albertel 7519: }
                   7520: 
                   7521: .LC_navbuttons {
                   7522:   margin: 2ex 0ex 2ex 0ex;
                   7523: }
1.795     www      7524: 
1.423     albertel 7525: .LC_topic_bar {
                   7526:   font-weight: bold;
                   7527:   background: $tabbg;
1.918     wenzelju 7528:   margin: 1em 0em 1em 2em;
1.805     bisitz   7529:   padding: 3px;
1.918     wenzelju 7530:   font-size: 1.2em;
1.423     albertel 7531: }
1.795     www      7532: 
1.423     albertel 7533: .LC_topic_bar span {
1.918     wenzelju 7534:   left: 0.5em;
                   7535:   position: absolute;
1.423     albertel 7536:   vertical-align: middle;
1.918     wenzelju 7537:   font-size: 1.2em;
1.423     albertel 7538: }
1.795     www      7539: 
1.423     albertel 7540: table.LC_course_group_status {
                   7541:   margin: 20px;
                   7542: }
1.795     www      7543: 
1.423     albertel 7544: table.LC_status_selector td {
                   7545:   vertical-align: top;
                   7546:   text-align: center;
1.424     albertel 7547:   padding: 4px;
                   7548: }
1.795     www      7549: 
1.599     albertel 7550: div.LC_feedback_link {
1.616     albertel 7551:   clear: both;
1.829     kalberla 7552:   background: $sidebg;
1.779     bisitz   7553:   width: 100%;
1.829     kalberla 7554:   padding-bottom: 10px;
                   7555:   border: 1px $tabbg solid;
1.833     kalberla 7556:   height: 22px;
                   7557:   line-height: 22px;
                   7558:   padding-top: 5px;
                   7559: }
                   7560: 
                   7561: div.LC_feedback_link img {
                   7562:   height: 22px;
1.867     kalberla 7563:   vertical-align:middle;
1.829     kalberla 7564: }
                   7565: 
1.911     bisitz   7566: div.LC_feedback_link a {
1.829     kalberla 7567:   text-decoration: none;
1.489     raeburn  7568: }
1.795     www      7569: 
1.867     kalberla 7570: div.LC_comblock {
1.911     bisitz   7571:   display:inline;
1.867     kalberla 7572:   color:$font;
                   7573:   font-size:90%;
                   7574: }
                   7575: 
                   7576: div.LC_feedback_link div.LC_comblock {
                   7577:   padding-left:5px;
                   7578: }
                   7579: 
                   7580: div.LC_feedback_link div.LC_comblock a {
                   7581:   color:$font;
                   7582: }
                   7583: 
1.489     raeburn  7584: span.LC_feedback_link {
1.858     bisitz   7585:   /* background: $feedback_link_bg; */
1.599     albertel 7586:   font-size: larger;
                   7587: }
1.795     www      7588: 
1.599     albertel 7589: span.LC_message_link {
1.858     bisitz   7590:   /* background: $feedback_link_bg; */
1.599     albertel 7591:   font-size: larger;
                   7592:   position: absolute;
                   7593:   right: 1em;
1.489     raeburn  7594: }
1.421     albertel 7595: 
1.515     albertel 7596: table.LC_prior_tries {
1.524     albertel 7597:   border: 1px solid #000000;
                   7598:   border-collapse: separate;
                   7599:   border-spacing: 1px;
1.515     albertel 7600: }
1.523     albertel 7601: 
1.515     albertel 7602: table.LC_prior_tries td {
1.524     albertel 7603:   padding: 2px;
1.515     albertel 7604: }
1.523     albertel 7605: 
                   7606: .LC_answer_correct {
1.795     www      7607:   background: lightgreen;
                   7608:   color: darkgreen;
                   7609:   padding: 6px;
1.523     albertel 7610: }
1.795     www      7611: 
1.523     albertel 7612: .LC_answer_charged_try {
1.797     www      7613:   background: #FFAAAA;
1.795     www      7614:   color: darkred;
                   7615:   padding: 6px;
1.523     albertel 7616: }
1.795     www      7617: 
1.779     bisitz   7618: .LC_answer_not_charged_try,
1.523     albertel 7619: .LC_answer_no_grade,
                   7620: .LC_answer_late {
1.795     www      7621:   background: lightyellow;
1.523     albertel 7622:   color: black;
1.795     www      7623:   padding: 6px;
1.523     albertel 7624: }
1.795     www      7625: 
1.523     albertel 7626: .LC_answer_previous {
1.795     www      7627:   background: lightblue;
                   7628:   color: darkblue;
                   7629:   padding: 6px;
1.523     albertel 7630: }
1.795     www      7631: 
1.779     bisitz   7632: .LC_answer_no_message {
1.777     tempelho 7633:   background: #FFFFFF;
                   7634:   color: black;
1.795     www      7635:   padding: 6px;
1.779     bisitz   7636: }
1.795     www      7637: 
1.1334    raeburn  7638: .LC_answer_unknown,
                   7639: .LC_answer_warning {
1.779     bisitz   7640:   background: orange;
                   7641:   color: black;
1.795     www      7642:   padding: 6px;
1.777     tempelho 7643: }
1.795     www      7644: 
1.529     albertel 7645: span.LC_prior_numerical,
                   7646: span.LC_prior_string,
                   7647: span.LC_prior_custom,
                   7648: span.LC_prior_reaction,
                   7649: span.LC_prior_math {
1.925     bisitz   7650:   font-family: $mono;
1.523     albertel 7651:   white-space: pre;
                   7652: }
                   7653: 
1.525     albertel 7654: span.LC_prior_string {
1.925     bisitz   7655:   font-family: $mono;
1.525     albertel 7656:   white-space: pre;
                   7657: }
                   7658: 
1.523     albertel 7659: table.LC_prior_option {
                   7660:   width: 100%;
                   7661:   border-collapse: collapse;
                   7662: }
1.795     www      7663: 
1.911     bisitz   7664: table.LC_prior_rank,
1.795     www      7665: table.LC_prior_match {
1.528     albertel 7666:   border-collapse: collapse;
                   7667: }
1.795     www      7668: 
1.528     albertel 7669: table.LC_prior_option tr td,
                   7670: table.LC_prior_rank tr td,
                   7671: table.LC_prior_match tr td {
1.524     albertel 7672:   border: 1px solid #000000;
1.515     albertel 7673: }
                   7674: 
1.855     bisitz   7675: .LC_nobreak {
1.544     albertel 7676:   white-space: nowrap;
1.519     raeburn  7677: }
                   7678: 
1.576     raeburn  7679: span.LC_cusr_emph {
                   7680:   font-style: italic;
                   7681: }
                   7682: 
1.633     raeburn  7683: span.LC_cusr_subheading {
                   7684:   font-weight: normal;
                   7685:   font-size: 85%;
                   7686: }
                   7687: 
1.861     bisitz   7688: div.LC_docs_entry_move {
1.859     bisitz   7689:   border: 1px solid #BBBBBB;
1.545     albertel 7690:   background: #DDDDDD;
1.861     bisitz   7691:   width: 22px;
1.859     bisitz   7692:   padding: 1px;
                   7693:   margin: 0;
1.545     albertel 7694: }
                   7695: 
1.861     bisitz   7696: table.LC_data_table tr > td.LC_docs_entry_commands,
                   7697: table.LC_data_table tr > td.LC_docs_entry_parameter {
1.545     albertel 7698:   font-size: x-small;
                   7699: }
1.795     www      7700: 
1.861     bisitz   7701: .LC_docs_entry_parameter {
                   7702:   white-space: nowrap;
                   7703: }
                   7704: 
1.544     albertel 7705: .LC_docs_copy {
1.545     albertel 7706:   color: #000099;
1.544     albertel 7707: }
1.795     www      7708: 
1.544     albertel 7709: .LC_docs_cut {
1.545     albertel 7710:   color: #550044;
1.544     albertel 7711: }
1.795     www      7712: 
1.544     albertel 7713: .LC_docs_rename {
1.545     albertel 7714:   color: #009900;
1.544     albertel 7715: }
1.795     www      7716: 
1.544     albertel 7717: .LC_docs_remove {
1.545     albertel 7718:   color: #990000;
                   7719: }
                   7720: 
1.1284    raeburn  7721: .LC_docs_alias {
                   7722:   color: #440055;  
                   7723: }
                   7724: 
1.1286    raeburn  7725: .LC_domprefs_email,
1.1284    raeburn  7726: .LC_docs_alias_name,
1.547     albertel 7727: .LC_docs_reinit_warn,
                   7728: .LC_docs_ext_edit {
                   7729:   font-size: x-small;
                   7730: }
                   7731: 
1.545     albertel 7732: table.LC_docs_adddocs td,
                   7733: table.LC_docs_adddocs th {
                   7734:   border: 1px solid #BBBBBB;
                   7735:   padding: 4px;
                   7736:   background: #DDDDDD;
1.543     albertel 7737: }
                   7738: 
1.584     albertel 7739: table.LC_sty_begin {
                   7740:   background: #BBFFBB;
                   7741: }
1.795     www      7742: 
1.584     albertel 7743: table.LC_sty_end {
                   7744:   background: #FFBBBB;
                   7745: }
                   7746: 
1.589     raeburn  7747: table.LC_double_column {
1.803     bisitz   7748:   border-width: 0;
1.589     raeburn  7749:   border-collapse: collapse;
                   7750:   width: 100%;
                   7751:   padding: 2px;
                   7752: }
                   7753: 
                   7754: table.LC_double_column tr td.LC_left_col {
1.590     raeburn  7755:   top: 2px;
1.589     raeburn  7756:   left: 2px;
                   7757:   width: 47%;
                   7758:   vertical-align: top;
                   7759: }
                   7760: 
                   7761: table.LC_double_column tr td.LC_right_col {
                   7762:   top: 2px;
1.779     bisitz   7763:   right: 2px;
1.589     raeburn  7764:   width: 47%;
                   7765:   vertical-align: top;
                   7766: }
                   7767: 
1.591     raeburn  7768: div.LC_left_float {
                   7769:   float: left;
                   7770:   padding-right: 5%;
1.597     albertel 7771:   padding-bottom: 4px;
1.591     raeburn  7772: }
                   7773: 
                   7774: div.LC_clear_float_header {
1.597     albertel 7775:   padding-bottom: 2px;
1.591     raeburn  7776: }
                   7777: 
                   7778: div.LC_clear_float_footer {
1.597     albertel 7779:   padding-top: 10px;
1.591     raeburn  7780:   clear: both;
                   7781: }
                   7782: 
1.597     albertel 7783: div.LC_grade_show_user {
1.941     bisitz   7784: /*  border-left: 5px solid $sidebg; */
                   7785:   border-top: 5px solid #000000;
                   7786:   margin: 50px 0 0 0;
1.936     bisitz   7787:   padding: 15px 0 5px 10px;
1.597     albertel 7788: }
1.795     www      7789: 
1.936     bisitz   7790: div.LC_grade_show_user_odd_row {
1.941     bisitz   7791: /*  border-left: 5px solid #000000; */
                   7792: }
                   7793: 
                   7794: div.LC_grade_show_user div.LC_Box {
                   7795:   margin-right: 50px;
1.597     albertel 7796: }
                   7797: 
                   7798: div.LC_grade_submissions,
                   7799: div.LC_grade_message_center,
1.936     bisitz   7800: div.LC_grade_info_links {
1.597     albertel 7801:   margin: 5px;
                   7802:   width: 99%;
                   7803:   background: #FFFFFF;
                   7804: }
1.795     www      7805: 
1.597     albertel 7806: div.LC_grade_submissions_header,
1.936     bisitz   7807: div.LC_grade_message_center_header {
1.705     tempelho 7808:   font-weight: bold;
                   7809:   font-size: large;
1.597     albertel 7810: }
1.795     www      7811: 
1.597     albertel 7812: div.LC_grade_submissions_body,
1.936     bisitz   7813: div.LC_grade_message_center_body {
1.597     albertel 7814:   border: 1px solid black;
                   7815:   width: 99%;
                   7816:   background: #FFFFFF;
                   7817: }
1.795     www      7818: 
1.613     albertel 7819: table.LC_scantron_action {
                   7820:   width: 100%;
                   7821: }
1.795     www      7822: 
1.613     albertel 7823: table.LC_scantron_action tr th {
1.698     harmsja  7824:   font-weight:bold;
                   7825:   font-style:normal;
1.613     albertel 7826: }
1.795     www      7827: 
1.779     bisitz   7828: .LC_edit_problem_header,
1.614     albertel 7829: div.LC_edit_problem_footer {
1.705     tempelho 7830:   font-weight: normal;
                   7831:   font-size:  medium;
1.602     albertel 7832:   margin: 2px;
1.1060    bisitz   7833:   background-color: $sidebg;
1.600     albertel 7834: }
1.795     www      7835: 
1.600     albertel 7836: div.LC_edit_problem_header,
1.602     albertel 7837: div.LC_edit_problem_header div,
1.614     albertel 7838: div.LC_edit_problem_footer,
                   7839: div.LC_edit_problem_footer div,
1.602     albertel 7840: div.LC_edit_problem_editxml_header,
                   7841: div.LC_edit_problem_editxml_header div {
1.1205    golterma 7842:   z-index: 100;
1.600     albertel 7843: }
1.795     www      7844: 
1.600     albertel 7845: div.LC_edit_problem_header_title {
1.705     tempelho 7846:   font-weight: bold;
                   7847:   font-size: larger;
1.602     albertel 7848:   background: $tabbg;
                   7849:   padding: 3px;
1.1060    bisitz   7850:   margin: 0 0 5px 0;
1.602     albertel 7851: }
1.795     www      7852: 
1.602     albertel 7853: table.LC_edit_problem_header_title {
                   7854:   width: 100%;
1.600     albertel 7855:   background: $tabbg;
1.602     albertel 7856: }
                   7857: 
1.1205    golterma 7858: div.LC_edit_actionbar {
                   7859:     background-color: $sidebg;
1.1218    droeschl 7860:     margin: 0;
                   7861:     padding: 0;
                   7862:     line-height: 200%;
1.602     albertel 7863: }
1.795     www      7864: 
1.1218    droeschl 7865: div.LC_edit_actionbar div{
                   7866:     padding: 0;
                   7867:     margin: 0;
                   7868:     display: inline-block;
1.600     albertel 7869: }
1.795     www      7870: 
1.1124    bisitz   7871: .LC_edit_opt {
                   7872:   padding-left: 1em;
                   7873:   white-space: nowrap;
                   7874: }
                   7875: 
1.1152    golterma 7876: .LC_edit_problem_latexhelper{
                   7877:     text-align: right;
                   7878: }
                   7879: 
                   7880: #LC_edit_problem_colorful div{
                   7881:     margin-left: 40px;
                   7882: }
                   7883: 
1.1205    golterma 7884: #LC_edit_problem_codemirror div{
                   7885:     margin-left: 0px;
                   7886: }
                   7887: 
1.911     bisitz   7888: img.stift {
1.803     bisitz   7889:   border-width: 0;
                   7890:   vertical-align: middle;
1.677     riegler  7891: }
1.680     riegler  7892: 
1.923     bisitz   7893: table td.LC_mainmenu_col_fieldset {
1.680     riegler  7894:   vertical-align: top;
1.777     tempelho 7895: }
1.795     www      7896: 
1.716     raeburn  7897: div.LC_createcourse {
1.911     bisitz   7898:   margin: 10px 10px 10px 10px;
1.716     raeburn  7899: }
                   7900: 
1.917     raeburn  7901: .LC_dccid {
1.1130    raeburn  7902:   float: right;
1.917     raeburn  7903:   margin: 0.2em 0 0 0;
                   7904:   padding: 0;
                   7905:   font-size: 90%;
                   7906:   display:none;
                   7907: }
                   7908: 
1.897     wenzelju 7909: ol.LC_primary_menu a:hover,
1.721     harmsja  7910: ol#LC_MenuBreadcrumbs a:hover,
                   7911: ol#LC_PathBreadcrumbs a:hover,
1.897     wenzelju 7912: ul#LC_secondary_menu a:hover,
1.721     harmsja  7913: .LC_FormSectionClearButton input:hover
1.795     www      7914: ul.LC_TabContent   li:hover a {
1.952     onken    7915:   color:$button_hover;
1.911     bisitz   7916:   text-decoration:none;
1.693     droeschl 7917: }
                   7918: 
1.779     bisitz   7919: h1 {
1.911     bisitz   7920:   padding: 0;
                   7921:   line-height:130%;
1.693     droeschl 7922: }
1.698     harmsja  7923: 
1.911     bisitz   7924: h2,
                   7925: h3,
                   7926: h4,
                   7927: h5,
                   7928: h6 {
                   7929:   margin: 5px 0 5px 0;
                   7930:   padding: 0;
                   7931:   line-height:130%;
1.693     droeschl 7932: }
1.795     www      7933: 
                   7934: .LC_hcell {
1.911     bisitz   7935:   padding:3px 15px 3px 15px;
                   7936:   margin: 0;
                   7937:   background-color:$tabbg;
                   7938:   color:$fontmenu;
                   7939:   border-bottom:solid 1px $lg_border_color;
1.693     droeschl 7940: }
1.795     www      7941: 
1.840     bisitz   7942: .LC_Box > .LC_hcell {
1.911     bisitz   7943:   margin: 0 -10px 10px -10px;
1.835     bisitz   7944: }
                   7945: 
1.721     harmsja  7946: .LC_noBorder {
1.911     bisitz   7947:   border: 0;
1.698     harmsja  7948: }
1.693     droeschl 7949: 
1.721     harmsja  7950: .LC_FormSectionClearButton input {
1.911     bisitz   7951:   background-color:transparent;
                   7952:   border: none;
                   7953:   cursor:pointer;
                   7954:   text-decoration:underline;
1.693     droeschl 7955: }
1.763     bisitz   7956: 
                   7957: .LC_help_open_topic {
1.911     bisitz   7958:   color: #FFFFFF;
                   7959:   background-color: #EEEEFF;
                   7960:   margin: 1px;
                   7961:   padding: 4px;
                   7962:   border: 1px solid #000033;
                   7963:   white-space: nowrap;
                   7964:   /* vertical-align: middle; */
1.759     neumanie 7965: }
1.693     droeschl 7966: 
1.911     bisitz   7967: dl,
                   7968: ul,
                   7969: div,
                   7970: fieldset {
                   7971:   margin: 10px 10px 10px 0;
                   7972:   /* overflow: hidden; */
1.693     droeschl 7973: }
1.795     www      7974: 
1.1211    raeburn  7975: article.geogebraweb div {
                   7976:     margin: 0;
                   7977: }
                   7978: 
1.838     bisitz   7979: fieldset > legend {
1.911     bisitz   7980:   font-weight: bold;
                   7981:   padding: 0 5px 0 5px;
1.838     bisitz   7982: }
                   7983: 
1.813     bisitz   7984: #LC_nav_bar {
1.911     bisitz   7985:   float: left;
1.995     raeburn  7986:   background-color: $pgbg_or_bgcolor;
1.966     bisitz   7987:   margin: 0 0 2px 0;
1.807     droeschl 7988: }
                   7989: 
1.916     droeschl 7990: #LC_realm {
                   7991:   margin: 0.2em 0 0 0;
                   7992:   padding: 0;
                   7993:   font-weight: bold;
                   7994:   text-align: center;
1.995     raeburn  7995:   background-color: $pgbg_or_bgcolor;
1.916     droeschl 7996: }
                   7997: 
1.911     bisitz   7998: #LC_nav_bar em {
                   7999:   font-weight: bold;
                   8000:   font-style: normal;
1.807     droeschl 8001: }
                   8002: 
1.897     wenzelju 8003: ol.LC_primary_menu {
1.934     droeschl 8004:   margin: 0;
1.1076    raeburn  8005:   padding: 0;
1.807     droeschl 8006: }
                   8007: 
1.852     droeschl 8008: ol#LC_PathBreadcrumbs {
1.911     bisitz   8009:   margin: 0;
1.693     droeschl 8010: }
                   8011: 
1.897     wenzelju 8012: ol.LC_primary_menu li {
1.1076    raeburn  8013:   color: RGB(80, 80, 80);
                   8014:   vertical-align: middle;
                   8015:   text-align: left;
                   8016:   list-style: none;
1.1205    golterma 8017:   position: relative;
1.1076    raeburn  8018:   float: left;
1.1205    golterma 8019:   z-index: 100; /* will be displayed above codemirror and underneath the help-layer */
                   8020:   line-height: 1.5em;
1.1076    raeburn  8021: }
                   8022: 
1.1205    golterma 8023: ol.LC_primary_menu li a,
                   8024: ol.LC_primary_menu li p {
1.1076    raeburn  8025:   display: block;
                   8026:   margin: 0;
                   8027:   padding: 0 5px 0 10px;
                   8028:   text-decoration: none;
                   8029: }
                   8030: 
1.1205    golterma 8031: ol.LC_primary_menu li p span.LC_primary_menu_innertitle {
                   8032:   display: inline-block;
                   8033:   width: 95%;
                   8034:   text-align: left;
                   8035: }
                   8036: 
                   8037: ol.LC_primary_menu li p span.LC_primary_menu_innerarrow {
                   8038:   display: inline-block;	
                   8039:   width: 5%;
                   8040:   float: right;
                   8041:   text-align: right;
                   8042:   font-size: 70%;
                   8043: }
                   8044: 
                   8045: ol.LC_primary_menu ul {
1.1076    raeburn  8046:   display: none;
1.1205    golterma 8047:   width: 15em;
1.1076    raeburn  8048:   background-color: $data_table_light;
1.1205    golterma 8049:   position: absolute;
                   8050:   top: 100%;
1.1076    raeburn  8051: }
                   8052: 
1.1205    golterma 8053: ol.LC_primary_menu ul ul {
                   8054:   left: 100%;
                   8055:   top: 0;
                   8056: }
                   8057: 
                   8058: ol.LC_primary_menu li:hover > ul, ol.LC_primary_menu li.hover > ul {
1.1076    raeburn  8059:   display: block;
                   8060:   position: absolute;
                   8061:   margin: 0;
                   8062:   padding: 0;
1.1078    raeburn  8063:   z-index: 2;
1.1076    raeburn  8064: }
                   8065: 
                   8066: ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
1.1205    golterma 8067: /* First Submenu -> size should be smaller than the menu title of the whole menu */
1.1076    raeburn  8068:   font-size: 90%;
1.911     bisitz   8069:   vertical-align: top;
1.1076    raeburn  8070:   float: none;
1.1079    raeburn  8071:   border-left: 1px solid black;
                   8072:   border-right: 1px solid black;
1.1205    golterma 8073: /* A dark bottom border to visualize different menu options; 
                   8074: overwritten in the create_submenu routine for the last border-bottom of the menu */
                   8075:   border-bottom: 1px solid $data_table_dark; 
1.1076    raeburn  8076: }
                   8077: 
1.1205    golterma 8078: ol.LC_primary_menu li li p:hover {
                   8079:   color:$button_hover;
                   8080:   text-decoration:none;
                   8081:   background-color:$data_table_dark;
1.1076    raeburn  8082: }
                   8083: 
                   8084: ol.LC_primary_menu li li a:hover {
                   8085:    color:$button_hover;
                   8086:    background-color:$data_table_dark;
1.693     droeschl 8087: }
                   8088: 
1.1205    golterma 8089: /* Font-size equal to the size of the predecessors*/
                   8090: ol.LC_primary_menu li:hover li li {
                   8091:   font-size: 100%;
                   8092: }
                   8093: 
1.897     wenzelju 8094: ol.LC_primary_menu li img {
1.911     bisitz   8095:   vertical-align: bottom;
1.934     droeschl 8096:   height: 1.1em;
1.1077    raeburn  8097:   margin: 0.2em 0 0 0;
1.693     droeschl 8098: }
                   8099: 
1.897     wenzelju 8100: ol.LC_primary_menu a {
1.911     bisitz   8101:   color: RGB(80, 80, 80);
                   8102:   text-decoration: none;
1.693     droeschl 8103: }
1.795     www      8104: 
1.949     droeschl 8105: ol.LC_primary_menu a.LC_new_message {
                   8106:   font-weight:bold;
                   8107:   color: darkred;
                   8108: }
                   8109: 
1.975     raeburn  8110: ol.LC_docs_parameters {
                   8111:   margin-left: 0;
                   8112:   padding: 0;
                   8113:   list-style: none;
                   8114: }
                   8115: 
                   8116: ol.LC_docs_parameters li {
                   8117:   margin: 0;
                   8118:   padding-right: 20px;
                   8119:   display: inline;
                   8120: }
                   8121: 
1.976     raeburn  8122: ol.LC_docs_parameters li:before {
                   8123:   content: "\\002022 \\0020";
                   8124: }
                   8125: 
                   8126: li.LC_docs_parameters_title {
                   8127:   font-weight: bold;
                   8128: }
                   8129: 
                   8130: ol.LC_docs_parameters li.LC_docs_parameters_title:before {
                   8131:   content: "";
                   8132: }
                   8133: 
1.897     wenzelju 8134: ul#LC_secondary_menu {
1.1107    raeburn  8135:   clear: right;
1.911     bisitz   8136:   color: $fontmenu;
                   8137:   background: $tabbg;
                   8138:   list-style: none;
                   8139:   padding: 0;
                   8140:   margin: 0;
                   8141:   width: 100%;
1.995     raeburn  8142:   text-align: left;
1.1107    raeburn  8143:   float: left;
1.808     droeschl 8144: }
                   8145: 
1.897     wenzelju 8146: ul#LC_secondary_menu li {
1.911     bisitz   8147:   font-weight: bold;
                   8148:   line-height: 1.8em;
1.1107    raeburn  8149:   border-right: 1px solid black;
                   8150:   float: left;
                   8151: }
                   8152: 
                   8153: ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
                   8154:   background-color: $data_table_light;
                   8155: }
                   8156: 
                   8157: ul#LC_secondary_menu li a {
1.911     bisitz   8158:   padding: 0 0.8em;
1.1107    raeburn  8159: }
                   8160: 
                   8161: ul#LC_secondary_menu li ul {
                   8162:   display: none;
                   8163: }
                   8164: 
                   8165: ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
                   8166:   display: block;
                   8167:   position: absolute;
                   8168:   margin: 0;
                   8169:   padding: 0;
                   8170:   list-style:none;
                   8171:   float: none;
                   8172:   background-color: $data_table_light;
                   8173:   z-index: 2;
                   8174:   margin-left: -1px;
                   8175: }
                   8176: 
                   8177: ul#LC_secondary_menu li ul li {
                   8178:   font-size: 90%;
                   8179:   vertical-align: top;
                   8180:   border-left: 1px solid black;
1.911     bisitz   8181:   border-right: 1px solid black;
1.1119    raeburn  8182:   background-color: $data_table_light;
1.1107    raeburn  8183:   list-style:none;
                   8184:   float: none;
                   8185: }
                   8186: 
                   8187: ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
                   8188:   background-color: $data_table_dark;
1.807     droeschl 8189: }
                   8190: 
1.847     tempelho 8191: ul.LC_TabContent {
1.911     bisitz   8192:   display:block;
                   8193:   background: $sidebg;
                   8194:   border-bottom: solid 1px $lg_border_color;
                   8195:   list-style:none;
1.1020    raeburn  8196:   margin: -1px -10px 0 -10px;
1.911     bisitz   8197:   padding: 0;
1.693     droeschl 8198: }
                   8199: 
1.795     www      8200: ul.LC_TabContent li,
                   8201: ul.LC_TabContentBigger li {
1.911     bisitz   8202:   float:left;
1.741     harmsja  8203: }
1.795     www      8204: 
1.897     wenzelju 8205: ul#LC_secondary_menu li a {
1.911     bisitz   8206:   color: $fontmenu;
                   8207:   text-decoration: none;
1.693     droeschl 8208: }
1.795     www      8209: 
1.721     harmsja  8210: ul.LC_TabContent {
1.952     onken    8211:   min-height:20px;
1.721     harmsja  8212: }
1.795     www      8213: 
                   8214: ul.LC_TabContent li {
1.911     bisitz   8215:   vertical-align:middle;
1.959     onken    8216:   padding: 0 16px 0 10px;
1.911     bisitz   8217:   background-color:$tabbg;
                   8218:   border-bottom:solid 1px $lg_border_color;
1.1020    raeburn  8219:   border-left: solid 1px $font;
1.721     harmsja  8220: }
1.795     www      8221: 
1.847     tempelho 8222: ul.LC_TabContent .right {
1.911     bisitz   8223:   float:right;
1.847     tempelho 8224: }
                   8225: 
1.911     bisitz   8226: ul.LC_TabContent li a,
                   8227: ul.LC_TabContent li {
                   8228:   color:rgb(47,47,47);
                   8229:   text-decoration:none;
                   8230:   font-size:95%;
                   8231:   font-weight:bold;
1.952     onken    8232:   min-height:20px;
                   8233: }
                   8234: 
1.959     onken    8235: ul.LC_TabContent li a:hover,
                   8236: ul.LC_TabContent li a:focus {
1.952     onken    8237:   color: $button_hover;
1.959     onken    8238:   background:none;
                   8239:   outline:none;
1.952     onken    8240: }
                   8241: 
                   8242: ul.LC_TabContent li:hover {
                   8243:   color: $button_hover;
                   8244:   cursor:pointer;
1.721     harmsja  8245: }
1.795     www      8246: 
1.911     bisitz   8247: ul.LC_TabContent li.active {
1.952     onken    8248:   color: $font;
1.911     bisitz   8249:   background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
1.952     onken    8250:   border-bottom:solid 1px #FFFFFF;
                   8251:   cursor: default;
1.744     ehlerst  8252: }
1.795     www      8253: 
1.959     onken    8254: ul.LC_TabContent li.active a {
                   8255:   color:$font;
                   8256:   background:#FFFFFF;
                   8257:   outline: none;
                   8258: }
1.1047    raeburn  8259: 
                   8260: ul.LC_TabContent li.goback {
                   8261:   float: left;
                   8262:   border-left: none;
                   8263: }
                   8264: 
1.870     tempelho 8265: #maincoursedoc {
1.911     bisitz   8266:   clear:both;
1.870     tempelho 8267: }
                   8268: 
                   8269: ul.LC_TabContentBigger {
1.911     bisitz   8270:   display:block;
                   8271:   list-style:none;
                   8272:   padding: 0;
1.870     tempelho 8273: }
                   8274: 
1.795     www      8275: ul.LC_TabContentBigger li {
1.911     bisitz   8276:   vertical-align:bottom;
                   8277:   height: 30px;
                   8278:   font-size:110%;
                   8279:   font-weight:bold;
                   8280:   color: #737373;
1.841     tempelho 8281: }
                   8282: 
1.957     onken    8283: ul.LC_TabContentBigger li.active {
                   8284:   position: relative;
                   8285:   top: 1px;
                   8286: }
                   8287: 
1.870     tempelho 8288: ul.LC_TabContentBigger li a {
1.911     bisitz   8289:   background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
                   8290:   height: 30px;
                   8291:   line-height: 30px;
                   8292:   text-align: center;
                   8293:   display: block;
                   8294:   text-decoration: none;
1.958     onken    8295:   outline: none;  
1.741     harmsja  8296: }
1.795     www      8297: 
1.870     tempelho 8298: ul.LC_TabContentBigger li.active a {
1.911     bisitz   8299:   background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
                   8300:   color:$font;
1.744     ehlerst  8301: }
1.795     www      8302: 
1.870     tempelho 8303: ul.LC_TabContentBigger li b {
1.911     bisitz   8304:   background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
                   8305:   display: block;
                   8306:   float: left;
                   8307:   padding: 0 30px;
1.957     onken    8308:   border-bottom: 1px solid $lg_border_color;
1.870     tempelho 8309: }
                   8310: 
1.956     onken    8311: ul.LC_TabContentBigger li:hover b {
                   8312:   color:$button_hover;
                   8313: }
                   8314: 
1.870     tempelho 8315: ul.LC_TabContentBigger li.active b {
1.911     bisitz   8316:   background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
                   8317:   color:$font;
1.957     onken    8318:   border: 0;
1.741     harmsja  8319: }
1.693     droeschl 8320: 
1.870     tempelho 8321: 
1.862     bisitz   8322: ul.LC_CourseBreadcrumbs {
                   8323:   background: $sidebg;
1.1020    raeburn  8324:   height: 2em;
1.862     bisitz   8325:   padding-left: 10px;
1.1020    raeburn  8326:   margin: 0;
1.862     bisitz   8327:   list-style-position: inside;
                   8328: }
                   8329: 
1.911     bisitz   8330: ol#LC_MenuBreadcrumbs,
1.862     bisitz   8331: ol#LC_PathBreadcrumbs {
1.911     bisitz   8332:   padding-left: 10px;
                   8333:   margin: 0;
1.933     droeschl 8334:   height: 2.5em;  /* equal to #LC_breadcrumbs line-height */
1.693     droeschl 8335: }
                   8336: 
1.911     bisitz   8337: ol#LC_MenuBreadcrumbs li,
                   8338: ol#LC_PathBreadcrumbs li,
1.862     bisitz   8339: ul.LC_CourseBreadcrumbs li {
1.911     bisitz   8340:   display: inline;
1.933     droeschl 8341:   white-space: normal;  
1.693     droeschl 8342: }
                   8343: 
1.823     bisitz   8344: ol#LC_MenuBreadcrumbs li a,
1.862     bisitz   8345: ul.LC_CourseBreadcrumbs li a {
1.911     bisitz   8346:   text-decoration: none;
                   8347:   font-size:90%;
1.693     droeschl 8348: }
1.795     www      8349: 
1.969     droeschl 8350: ol#LC_MenuBreadcrumbs h1 {
                   8351:   display: inline;
                   8352:   font-size: 90%;
                   8353:   line-height: 2.5em;
                   8354:   margin: 0;
                   8355:   padding: 0;
                   8356: }
                   8357: 
1.795     www      8358: ol#LC_PathBreadcrumbs li a {
1.911     bisitz   8359:   text-decoration:none;
                   8360:   font-size:100%;
                   8361:   font-weight:bold;
1.693     droeschl 8362: }
1.795     www      8363: 
1.840     bisitz   8364: .LC_Box {
1.911     bisitz   8365:   border: solid 1px $lg_border_color;
                   8366:   padding: 0 10px 10px 10px;
1.746     neumanie 8367: }
1.795     www      8368: 
1.1020    raeburn  8369: .LC_DocsBox {
                   8370:   border: solid 1px $lg_border_color;
                   8371:   padding: 0 0 10px 10px;
                   8372: }
                   8373: 
1.795     www      8374: .LC_AboutMe_Image {
1.911     bisitz   8375:   float:left;
                   8376:   margin-right:10px;
1.747     neumanie 8377: }
1.795     www      8378: 
                   8379: .LC_Clear_AboutMe_Image {
1.911     bisitz   8380:   clear:left;
1.747     neumanie 8381: }
1.795     www      8382: 
1.721     harmsja  8383: dl.LC_ListStyleClean dt {
1.911     bisitz   8384:   padding-right: 5px;
                   8385:   display: table-header-group;
1.693     droeschl 8386: }
                   8387: 
1.721     harmsja  8388: dl.LC_ListStyleClean dd {
1.911     bisitz   8389:   display: table-row;
1.693     droeschl 8390: }
                   8391: 
1.721     harmsja  8392: .LC_ListStyleClean,
                   8393: .LC_ListStyleSimple,
                   8394: .LC_ListStyleNormal,
1.795     www      8395: .LC_ListStyleSpecial {
1.911     bisitz   8396:   /* display:block; */
                   8397:   list-style-position: inside;
                   8398:   list-style-type: none;
                   8399:   overflow: hidden;
                   8400:   padding: 0;
1.693     droeschl 8401: }
                   8402: 
1.721     harmsja  8403: .LC_ListStyleSimple li,
                   8404: .LC_ListStyleSimple dd,
                   8405: .LC_ListStyleNormal li,
                   8406: .LC_ListStyleNormal dd,
                   8407: .LC_ListStyleSpecial li,
1.795     www      8408: .LC_ListStyleSpecial dd {
1.911     bisitz   8409:   margin: 0;
                   8410:   padding: 5px 5px 5px 10px;
                   8411:   clear: both;
1.693     droeschl 8412: }
                   8413: 
1.721     harmsja  8414: .LC_ListStyleClean li,
                   8415: .LC_ListStyleClean dd {
1.911     bisitz   8416:   padding-top: 0;
                   8417:   padding-bottom: 0;
1.693     droeschl 8418: }
                   8419: 
1.721     harmsja  8420: .LC_ListStyleSimple dd,
1.795     www      8421: .LC_ListStyleSimple li {
1.911     bisitz   8422:   border-bottom: solid 1px $lg_border_color;
1.693     droeschl 8423: }
                   8424: 
1.721     harmsja  8425: .LC_ListStyleSpecial li,
                   8426: .LC_ListStyleSpecial dd {
1.911     bisitz   8427:   list-style-type: none;
                   8428:   background-color: RGB(220, 220, 220);
                   8429:   margin-bottom: 4px;
1.693     droeschl 8430: }
                   8431: 
1.721     harmsja  8432: table.LC_SimpleTable {
1.911     bisitz   8433:   margin:5px;
                   8434:   border:solid 1px $lg_border_color;
1.795     www      8435: }
1.693     droeschl 8436: 
1.721     harmsja  8437: table.LC_SimpleTable tr {
1.911     bisitz   8438:   padding: 0;
                   8439:   border:solid 1px $lg_border_color;
1.693     droeschl 8440: }
1.795     www      8441: 
                   8442: table.LC_SimpleTable thead {
1.911     bisitz   8443:   background:rgb(220,220,220);
1.693     droeschl 8444: }
                   8445: 
1.721     harmsja  8446: div.LC_columnSection {
1.911     bisitz   8447:   display: block;
                   8448:   clear: both;
                   8449:   overflow: hidden;
                   8450:   margin: 0;
1.693     droeschl 8451: }
                   8452: 
1.721     harmsja  8453: div.LC_columnSection>* {
1.911     bisitz   8454:   float: left;
                   8455:   margin: 10px 20px 10px 0;
                   8456:   overflow:hidden;
1.693     droeschl 8457: }
1.721     harmsja  8458: 
1.795     www      8459: table em {
1.911     bisitz   8460:   font-weight: bold;
                   8461:   font-style: normal;
1.748     schulted 8462: }
1.795     www      8463: 
1.779     bisitz   8464: table.LC_tableBrowseRes,
1.795     www      8465: table.LC_tableOfContent {
1.911     bisitz   8466:   border:none;
                   8467:   border-spacing: 1px;
                   8468:   padding: 3px;
                   8469:   background-color: #FFFFFF;
                   8470:   font-size: 90%;
1.753     droeschl 8471: }
1.789     droeschl 8472: 
1.911     bisitz   8473: table.LC_tableOfContent {
                   8474:   border-collapse: collapse;
1.789     droeschl 8475: }
                   8476: 
1.771     droeschl 8477: table.LC_tableBrowseRes a,
1.768     schulted 8478: table.LC_tableOfContent a {
1.911     bisitz   8479:   background-color: transparent;
                   8480:   text-decoration: none;
1.753     droeschl 8481: }
                   8482: 
1.795     www      8483: table.LC_tableOfContent img {
1.911     bisitz   8484:   border: none;
                   8485:   height: 1.3em;
                   8486:   vertical-align: text-bottom;
                   8487:   margin-right: 0.3em;
1.753     droeschl 8488: }
1.757     schulted 8489: 
1.795     www      8490: a#LC_content_toolbar_firsthomework {
1.911     bisitz   8491:   background-image:url(/res/adm/pages/open-first-problem.gif);
1.774     ehlerst  8492: }
                   8493: 
1.795     www      8494: a#LC_content_toolbar_everything {
1.911     bisitz   8495:   background-image:url(/res/adm/pages/show-all.gif);
1.774     ehlerst  8496: }
                   8497: 
1.795     www      8498: a#LC_content_toolbar_uncompleted {
1.911     bisitz   8499:   background-image:url(/res/adm/pages/show-incomplete-problems.gif);
1.774     ehlerst  8500: }
                   8501: 
1.795     www      8502: #LC_content_toolbar_clearbubbles {
1.911     bisitz   8503:   background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
1.774     ehlerst  8504: }
                   8505: 
1.795     www      8506: a#LC_content_toolbar_changefolder {
1.911     bisitz   8507:   background : url(/res/adm/pages/close-all-folders.gif) top center ;
1.757     schulted 8508: }
                   8509: 
1.795     www      8510: a#LC_content_toolbar_changefolder_toggled {
1.911     bisitz   8511:   background-image:url(/res/adm/pages/open-all-folders.gif);
1.757     schulted 8512: }
                   8513: 
1.1043    raeburn  8514: a#LC_content_toolbar_edittoplevel {
                   8515:   background-image:url(/res/adm/pages/edittoplevel.gif);
                   8516: }
                   8517: 
1.795     www      8518: ul#LC_toolbar li a:hover {
1.911     bisitz   8519:   background-position: bottom center;
1.757     schulted 8520: }
                   8521: 
1.795     www      8522: ul#LC_toolbar {
1.911     bisitz   8523:   padding: 0;
                   8524:   margin: 2px;
                   8525:   list-style:none;
                   8526:   position:relative;
                   8527:   background-color:white;
1.1082    raeburn  8528:   overflow: auto;
1.757     schulted 8529: }
                   8530: 
1.795     www      8531: ul#LC_toolbar li {
1.911     bisitz   8532:   border:1px solid white;
                   8533:   padding: 0;
                   8534:   margin: 0;
                   8535:   float: left;
                   8536:   display:inline;
                   8537:   vertical-align:middle;
1.1082    raeburn  8538:   white-space: nowrap;
1.911     bisitz   8539: }
1.757     schulted 8540: 
1.783     amueller 8541: 
1.795     www      8542: a.LC_toolbarItem {
1.911     bisitz   8543:   display:block;
                   8544:   padding: 0;
                   8545:   margin: 0;
                   8546:   height: 32px;
                   8547:   width: 32px;
                   8548:   color:white;
                   8549:   border: none;
                   8550:   background-repeat:no-repeat;
                   8551:   background-color:transparent;
1.757     schulted 8552: }
                   8553: 
1.915     droeschl 8554: ul.LC_funclist {
                   8555:     margin: 0;
                   8556:     padding: 0.5em 1em 0.5em 0;
                   8557: }
                   8558: 
1.933     droeschl 8559: ul.LC_funclist > li:first-child {
                   8560:     font-weight:bold; 
                   8561:     margin-left:0.8em;
                   8562: }
                   8563: 
1.915     droeschl 8564: ul.LC_funclist + ul.LC_funclist {
                   8565:     /* 
                   8566:        left border as a seperator if we have more than
                   8567:        one list 
                   8568:     */
                   8569:     border-left: 1px solid $sidebg;
                   8570:     /* 
                   8571:        this hides the left border behind the border of the 
                   8572:        outer box if element is wrapped to the next 'line' 
                   8573:     */
                   8574:     margin-left: -1px;
                   8575: }
                   8576: 
1.843     bisitz   8577: ul.LC_funclist li {
1.915     droeschl 8578:   display: inline;
1.782     bisitz   8579:   white-space: nowrap;
1.915     droeschl 8580:   margin: 0 0 0 25px;
                   8581:   line-height: 150%;
1.782     bisitz   8582: }
                   8583: 
1.974     wenzelju 8584: .LC_hidden {
                   8585:   display: none;
                   8586: }
                   8587: 
1.1030    www      8588: .LCmodal-overlay {
                   8589: 		position:fixed;
                   8590: 		top:0;
                   8591: 		right:0;
                   8592: 		bottom:0;
                   8593: 		left:0;
                   8594: 		height:100%;
                   8595: 		width:100%;
                   8596: 		margin:0;
                   8597: 		padding:0;
                   8598: 		background:#999;
                   8599: 		opacity:.75;
                   8600: 		filter: alpha(opacity=75);
                   8601: 		-moz-opacity: 0.75;
                   8602: 		z-index:101;
                   8603: }
                   8604: 
                   8605: * html .LCmodal-overlay {   
                   8606: 		position: absolute;
                   8607: 		height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
                   8608: }
                   8609: 
                   8610: .LCmodal-window {
                   8611: 		position:fixed;
                   8612: 		top:50%;
                   8613: 		left:50%;
                   8614: 		margin:0;
                   8615: 		padding:0;
                   8616: 		z-index:102;
                   8617: 	}
                   8618: 
                   8619: * html .LCmodal-window {
                   8620: 		position:absolute;
                   8621: }
                   8622: 
                   8623: .LCclose-window {
                   8624: 		position:absolute;
                   8625: 		width:32px;
                   8626: 		height:32px;
                   8627: 		right:8px;
                   8628: 		top:8px;
                   8629: 		background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
                   8630: 		text-indent:-99999px;
                   8631: 		overflow:hidden;
                   8632: 		cursor:pointer;
                   8633: }
                   8634: 
1.1369    raeburn  8635: .LCisDisabled {
                   8636:   cursor: not-allowed;
                   8637:   opacity: 0.5;
                   8638: }
                   8639: 
                   8640: a[aria-disabled="true"] {
                   8641:   color: currentColor;
                   8642:   display: inline-block;  /* For IE11/ MS Edge bug */
                   8643:   pointer-events: none;
                   8644:   text-decoration: none;
                   8645: }
                   8646: 
1.1335    raeburn  8647: pre.LC_wordwrap {
                   8648:   white-space: pre-wrap;
                   8649:   white-space: -moz-pre-wrap;
                   8650:   white-space: -pre-wrap;
                   8651:   white-space: -o-pre-wrap;
                   8652:   word-wrap: break-word;
                   8653: }
                   8654: 
1.1100    raeburn  8655: /*
1.1231    damieng  8656:   styles used for response display
                   8657: */
                   8658: div.LC_radiofoil, div.LC_rankfoil {
                   8659:   margin: .5em 0em .5em 0em;
                   8660: }
                   8661: table.LC_itemgroup {
                   8662:   margin-top: 1em;
                   8663: }
                   8664: 
                   8665: /*
1.1100    raeburn  8666:   styles used by TTH when "Default set of options to pass to tth/m
                   8667:   when converting TeX" in course settings has been set
                   8668: 
                   8669:   option passed: -t
                   8670: 
                   8671: */
                   8672: 
                   8673: td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
                   8674: td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
                   8675: td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
                   8676: td div.norm {line-height:normal;}
                   8677: 
                   8678: /*
                   8679:   option passed -y3
                   8680: */
                   8681: 
                   8682: span.roman {font-family: serif; font-style: normal; font-weight: normal;}
                   8683: span.overacc2 {position: relative;  left: .8em; top: -1.2ex;}
                   8684: span.overacc1 {position: relative;  left: .6em; top: -1.2ex;}
                   8685: 
1.1230    damieng  8686: /*
                   8687:   sections with roles, for content only
                   8688: */
                   8689: section[class^="role-"] {
                   8690:   padding-left: 10px;
                   8691:   padding-right: 5px;
                   8692:   margin-top: 8px;
                   8693:   margin-bottom: 8px;
                   8694:   border: 1px solid #2A4;
                   8695:   border-radius: 5px;
                   8696:   box-shadow: 0px 1px 1px #BBB;
                   8697: }
                   8698: section[class^="role-"]>h1 {
                   8699:   position: relative;
                   8700:   margin: 0px;
                   8701:   padding-top: 10px;
                   8702:   padding-left: 40px;
                   8703: }
                   8704: section[class^="role-"]>h1:before {
                   8705:   position: absolute;
                   8706:   left: -5px;
                   8707:   top: 5px;
                   8708: }
                   8709: section.role-activity>h1:before {
                   8710:   content:url('/adm/daxe/images/section_icons/activity.png');
                   8711: }
                   8712: section.role-advice>h1:before {
                   8713:   content:url('/adm/daxe/images/section_icons/advice.png');
                   8714: }
                   8715: section.role-bibliography>h1:before {
                   8716:   content:url('/adm/daxe/images/section_icons/bibliography.png');
                   8717: }
                   8718: section.role-citation>h1:before {
                   8719:   content:url('/adm/daxe/images/section_icons/citation.png');
                   8720: }
                   8721: section.role-conclusion>h1:before {
                   8722:   content:url('/adm/daxe/images/section_icons/conclusion.png');
                   8723: }
                   8724: section.role-definition>h1:before {
                   8725:   content:url('/adm/daxe/images/section_icons/definition.png');
                   8726: }
                   8727: section.role-demonstration>h1:before {
                   8728:   content:url('/adm/daxe/images/section_icons/demonstration.png');
                   8729: }
                   8730: section.role-example>h1:before {
                   8731:   content:url('/adm/daxe/images/section_icons/example.png');
                   8732: }
                   8733: section.role-explanation>h1:before {
                   8734:   content:url('/adm/daxe/images/section_icons/explanation.png');
                   8735: }
                   8736: section.role-introduction>h1:before {
                   8737:   content:url('/adm/daxe/images/section_icons/introduction.png');
                   8738: }
                   8739: section.role-method>h1:before {
                   8740:   content:url('/adm/daxe/images/section_icons/method.png');
                   8741: }
                   8742: section.role-more_information>h1:before {
                   8743:   content:url('/adm/daxe/images/section_icons/more_information.png');
                   8744: }
                   8745: section.role-objectives>h1:before {
                   8746:   content:url('/adm/daxe/images/section_icons/objectives.png');
                   8747: }
                   8748: section.role-prerequisites>h1:before {
                   8749:   content:url('/adm/daxe/images/section_icons/prerequisites.png');
                   8750: }
                   8751: section.role-remark>h1:before {
                   8752:   content:url('/adm/daxe/images/section_icons/remark.png');
                   8753: }
                   8754: section.role-reminder>h1:before {
                   8755:   content:url('/adm/daxe/images/section_icons/reminder.png');
                   8756: }
                   8757: section.role-summary>h1:before {
                   8758:   content:url('/adm/daxe/images/section_icons/summary.png');
                   8759: }
                   8760: section.role-syntax>h1:before {
                   8761:   content:url('/adm/daxe/images/section_icons/syntax.png');
                   8762: }
                   8763: section.role-warning>h1:before {
                   8764:   content:url('/adm/daxe/images/section_icons/warning.png');
                   8765: }
                   8766: 
1.1269    raeburn  8767: #LC_minitab_header {
                   8768:   float:left;
                   8769:   width:100%;
                   8770:   background:#DAE0D2 url("/res/adm/pages/minitabmenu_bg.gif") repeat-x bottom;
                   8771:   font-size:93%;
                   8772:   line-height:normal;
                   8773:   margin: 0.5em 0 0.5em 0;
                   8774: }
                   8775: #LC_minitab_header ul {
                   8776:   margin:0;
                   8777:   padding:10px 10px 0;
                   8778:   list-style:none;
                   8779: }
                   8780: #LC_minitab_header li {
                   8781:   float:left;
                   8782:   background:url("/res/adm/pages/minitabmenu_left.gif") no-repeat left top;
                   8783:   margin:0;
                   8784:   padding:0 0 0 9px;
                   8785: }
                   8786: #LC_minitab_header a {
                   8787:   display:block;
                   8788:   background:url("/res/adm/pages/minitabmenu_right.gif") no-repeat right top;
                   8789:   padding:5px 15px 4px 6px;
                   8790: }
                   8791: #LC_minitab_header #LC_current_minitab {
                   8792:   background-image:url("/res/adm/pages/minitabmenu_left_on.gif");
                   8793: }
                   8794: #LC_minitab_header #LC_current_minitab a {
                   8795:   background-image:url("/res/adm/pages/minitabmenu_right_on.gif");
                   8796:   padding-bottom:5px;
                   8797: }
                   8798: 
                   8799: 
1.343     albertel 8800: END
                   8801: }
                   8802: 
1.306     albertel 8803: =pod
                   8804: 
                   8805: =item * &headtag()
                   8806: 
                   8807: Returns a uniform footer for LON-CAPA web pages.
                   8808: 
1.307     albertel 8809: Inputs: $title - optional title for the head
                   8810:         $head_extra - optional extra HTML to put inside the <head>
1.315     albertel 8811:         $args - optional arguments
1.319     albertel 8812:             force_register - if is true call registerurl so the remote is 
                   8813:                              informed
1.415     albertel 8814:             redirect       -> array ref of
                   8815:                                    1- seconds before redirect occurs
                   8816:                                    2- url to redirect to
                   8817:                                    3- whether the side effect should occur
1.315     albertel 8818:                            (side effect of setting 
                   8819:                                $env{'internal.head.redirect'} to the url 
                   8820:                                redirected too)
1.352     albertel 8821:             domain         -> force to color decorate a page for a specific
                   8822:                                domain
                   8823:             function       -> force usage of a specific rolish color scheme
                   8824:             bgcolor        -> override the default page bgcolor
1.460     albertel 8825:             no_auto_mt_title
                   8826:                            -> prevent &mt()ing the title arg
1.464     albertel 8827: 
1.306     albertel 8828: =cut
                   8829: 
                   8830: sub headtag {
1.313     albertel 8831:     my ($title,$head_extra,$args) = @_;
1.306     albertel 8832:     
1.363     albertel 8833:     my $function = $args->{'function'} || &get_users_function();
                   8834:     my $domain   = $args->{'domain'}   || &determinedomain();
                   8835:     my $bgcolor  = $args->{'bgcolor'}  || &designparm($function.'.pgbg',$domain);
1.1154    raeburn  8836:     my $httphost = $args->{'use_absolute'};
1.418     albertel 8837:     my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458     albertel 8838: 		   $Apache::lonnet::perlvar{'lonVersion'},
1.531     albertel 8839: 		   #time(),
1.418     albertel 8840: 		   $env{'environment.color.timestamp'},
1.363     albertel 8841: 		   $function,$domain,$bgcolor);
                   8842: 
1.369     www      8843:     $url = '/adm/css/'.&escape($url).'.css';
1.363     albertel 8844: 
1.308     albertel 8845:     my $result =
                   8846: 	'<head>'.
1.1160    raeburn  8847: 	&font_settings($args);
1.319     albertel 8848: 
1.1188    raeburn  8849:     my $inhibitprint;
                   8850:     if ($args->{'print_suppress'}) {
                   8851:         $inhibitprint = &print_suppression();
                   8852:     }
1.1064    raeburn  8853: 
1.461     albertel 8854:     if (!$args->{'frameset'}) {
                   8855: 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
                   8856:     }
1.962     droeschl 8857:     if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
                   8858:         $result .= Apache::lonxml::display_title();
1.319     albertel 8859:     }
1.436     albertel 8860:     if (!$args->{'no_nav_bar'} 
                   8861: 	&& !$args->{'only_body'}
                   8862: 	&& !$args->{'frameset'}) {
1.1154    raeburn  8863: 	$result .= &help_menu_js($httphost);
1.1032    www      8864:         $result.=&modal_window();
1.1038    www      8865:         $result.=&togglebox_script();
1.1034    www      8866:         $result.=&wishlist_window();
1.1041    www      8867:         $result.=&LCprogressbarUpdate_script();
1.1034    www      8868:     } else {
                   8869:         if ($args->{'add_modal'}) {
                   8870:            $result.=&modal_window();
                   8871:         }
                   8872:         if ($args->{'add_wishlist'}) {
                   8873:            $result.=&wishlist_window();
                   8874:         }
1.1038    www      8875:         if ($args->{'add_togglebox'}) {
                   8876:            $result.=&togglebox_script();
                   8877:         }
1.1041    www      8878:         if ($args->{'add_progressbar'}) {
                   8879:            $result.=&LCprogressbarUpdate_script();
                   8880:         }
1.436     albertel 8881:     }
1.314     albertel 8882:     if (ref($args->{'redirect'})) {
1.414     albertel 8883: 	my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315     albertel 8884: 	$url = &Apache::lonenc::check_encrypt($url);
1.414     albertel 8885: 	if (!$inhibit_continue) {
                   8886: 	    $env{'internal.head.redirect'} = $url;
                   8887: 	}
1.313     albertel 8888: 	$result.=<<ADDMETA
                   8889: <meta http-equiv="pragma" content="no-cache" />
1.344     albertel 8890: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313     albertel 8891: ADDMETA
1.1210    raeburn  8892:     } else {
                   8893:         unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) {
                   8894:             my $requrl = $env{'request.uri'};
                   8895:             if ($requrl eq '') {
                   8896:                 $requrl = $ENV{'REQUEST_URI'};
                   8897:                 $requrl =~ s/\?.+$//;
                   8898:             }
                   8899:             unless (($requrl =~ m{^/adm/(?:switchserver|login|authenticate|logout|groupsort|cleanup|helper|slotrequest|grades)(\?|$)}) ||
                   8900:                     (($requrl =~ m{^/res/}) && (($env{'form.submitted'} eq 'scantron') ||
                   8901:                      ($env{'form.grade_symb'}) || ($Apache::lonhomework::scantronmode)))) {
                   8902:                 my $dom_in_use = $Apache::lonnet::perlvar{'lonDefDomain'};
                   8903:                 unless (&Apache::lonnet::allowed('mau',$dom_in_use)) {
                   8904:                     my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use);
1.1340    raeburn  8905:                     my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
1.1352    raeburn  8906:                     my ($offload,$offloadoth);
1.1210    raeburn  8907:                     if (ref($domdefs{'offloadnow'}) eq 'HASH') {
                   8908:                         if ($domdefs{'offloadnow'}{$lonhost}) {
1.1340    raeburn  8909:                             $offload = 1;
1.1353    raeburn  8910:                             if (($env{'user.domain'} ne '') && ($env{'user.domain'} ne $dom_in_use) &&
                   8911:                                 (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
                   8912:                                 unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
                   8913:                                     $offloadoth = 1;
                   8914:                                     $dom_in_use = $env{'user.domain'};
                   8915:                                 }
                   8916:                             }
1.1340    raeburn  8917:                         }
                   8918:                     }
                   8919:                     unless ($offload) {
                   8920:                         if (ref($domdefs{'offloadoth'}) eq 'HASH') {
                   8921:                             if ($domdefs{'offloadoth'}{$lonhost}) {
                   8922:                                 if (($env{'user.domain'} ne '') && ($env{'user.domain'} ne $dom_in_use) &&
                   8923:                                     (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
                   8924:                                     unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
                   8925:                                         $offload = 1;
1.1352    raeburn  8926:                                         $offloadoth = 1;
1.1340    raeburn  8927:                                         $dom_in_use = $env{'user.domain'};
                   8928:                                     }
1.1210    raeburn  8929:                                 }
1.1340    raeburn  8930:                             }
                   8931:                         }
                   8932:                     }
                   8933:                     if ($offload) {
1.1358    raeburn  8934:                         my $newserver = &Apache::lonnet::spareserver(undef,30000,undef,1,$dom_in_use);
1.1352    raeburn  8935:                         if (($newserver eq '') && ($offloadoth)) {
                   8936:                             my @domains = &Apache::lonnet::current_machine_domains();
                   8937:                             if (($dom_in_use ne '') && (!grep(/^\Q$dom_in_use\E$/,@domains))) { 
                   8938:                                 ($newserver) = &Apache::lonnet::choose_server($dom_in_use);
                   8939:                             }
                   8940:                         }
1.1340    raeburn  8941:                         if (($newserver) && ($newserver ne $lonhost)) {
                   8942:                             my $numsec = 5;
                   8943:                             my $timeout = $numsec * 1000;
                   8944:                             my ($newurl,$locknum,%locks,$msg);
                   8945:                             if ($env{'request.role.adv'}) {
                   8946:                                 ($locknum,%locks) = &Apache::lonnet::get_locks();
                   8947:                             }
                   8948:                             my $disable_submit = 0;
                   8949:                             if ($requrl =~ /$LONCAPA::assess_re/) {
                   8950:                                 $disable_submit = 1;
                   8951:                             }
                   8952:                             if ($locknum) {
                   8953:                                 my @lockinfo = sort(values(%locks));
1.1354    raeburn  8954:                                 $msg = &mt('Once the following tasks are complete:')." \n".
1.1340    raeburn  8955:                                        join(", ",sort(values(%locks)))."\n";
                   8956:                                 if (&show_course()) {
                   8957:                                     $msg .= &mt('your session will be transferred to a different server, after you click "Courses".');
                   8958:                                 } else {
                   8959:                                     $msg .= &mt('your session will be transferred to a different server, after you click "Roles".');
1.1210    raeburn  8960:                                 }
1.1340    raeburn  8961:                             } else {
                   8962:                                 if (($requrl =~ m{^/res/}) && ($env{'form.submitted'} =~ /^part_/)) {
                   8963:                                     $msg = &mt('Your LON-CAPA submission has been recorded')."\n";
                   8964:                                 }
                   8965:                                 $msg .= &mt('Your current LON-CAPA session will be transferred to a different server in [quant,_1,second].',$numsec);
                   8966:                                 $newurl = '/adm/switchserver?otherserver='.$newserver;
                   8967:                                 if (($env{'request.role'}) && ($env{'request.role'} ne 'cm')) {
                   8968:                                     $newurl .= '&role='.$env{'request.role'};
                   8969:                                 }
                   8970:                                 if ($env{'request.symb'}) {
                   8971:                                     my $shownsymb = &Apache::lonenc::check_encrypt($env{'request.symb'});
                   8972:                                     if ($shownsymb =~ m{^/enc/}) {
                   8973:                                         my $reqdmajor = 2;
                   8974:                                         my $reqdminor = 11;
                   8975:                                         my $reqdsubminor = 3;
                   8976:                                         my $newserverrev = &Apache::lonnet::get_server_loncaparev('',$newserver);
                   8977:                                         my $remoterev = &Apache::lonnet::get_server_loncaparev(undef,$newserver);
                   8978:                                         my ($major,$minor,$subminor) = ($remoterev =~ /^\'?(\d+)\.(\d+)\.(\d+|)[\w.\-]+\'?$/);
                   8979:                                         if (($major eq '' && $minor eq '') ||
                   8980:                                             (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)) ||
                   8981:                                             (($reqdmajor == $major) && ($reqdminor == $minor) && (($subminor eq '') ||
                   8982:                                              ($reqdsubminor > $subminor))))) {
                   8983:                                             undef($shownsymb);
                   8984:                                         }
1.1210    raeburn  8985:                                     }
1.1340    raeburn  8986:                                     if ($shownsymb) {
                   8987:                                         &js_escape(\$shownsymb);
                   8988:                                         $newurl .= '&symb='.$shownsymb;
1.1210    raeburn  8989:                                     }
1.1340    raeburn  8990:                                 } else {
                   8991:                                     my $shownurl = &Apache::lonenc::check_encrypt($requrl);
                   8992:                                     &js_escape(\$shownurl);
                   8993:                                     $newurl .= '&origurl='.$shownurl;
1.1210    raeburn  8994:                                 }
1.1340    raeburn  8995:                             }
                   8996:                             &js_escape(\$msg);
                   8997:                             $result.=<<OFFLOAD
1.1210    raeburn  8998: <meta http-equiv="pragma" content="no-cache" />
                   8999: <script type="text/javascript">
1.1215    raeburn  9000: // <![CDATA[
1.1210    raeburn  9001: function LC_Offload_Now() {
                   9002:     var dest = "$newurl";
                   9003:     if (dest != '') {
                   9004:         window.location.href="$newurl";
                   9005:     }
                   9006: }
1.1214    raeburn  9007: \$(document).ready(function () {
                   9008:     window.alert('$msg');
                   9009:     if ($disable_submit) {
1.1210    raeburn  9010:         \$(".LC_hwk_submit").prop("disabled", true);
                   9011:         \$( ".LC_textline" ).prop( "readonly", "readonly");
1.1214    raeburn  9012:     }
                   9013:     setTimeout('LC_Offload_Now()', $timeout);
                   9014: });
1.1215    raeburn  9015: // ]]>
1.1210    raeburn  9016: </script>
                   9017: OFFLOAD
                   9018:                         }
                   9019:                     }
                   9020:                 }
                   9021:             }
                   9022:         }
1.313     albertel 9023:     }
1.306     albertel 9024:     if (!defined($title)) {
                   9025: 	$title = 'The LearningOnline Network with CAPA';
                   9026:     }
1.460     albertel 9027:     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
                   9028:     $result .= '<title> LON-CAPA '.$title.'</title>'
1.1168    raeburn  9029: 	.'<link rel="stylesheet" type="text/css" href="'.$url.'"';
                   9030:     if (!$args->{'frameset'}) {
                   9031:         $result .= ' /';
                   9032:     }
                   9033:     $result .= '>' 
1.1064    raeburn  9034:         .$inhibitprint
1.414     albertel 9035: 	.$head_extra;
1.1242    raeburn  9036:     my $clientmobile;
                   9037:     if (($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
                   9038:         (undef,undef,undef,undef,undef,undef,$clientmobile) = &decode_user_agent();
                   9039:     } else {
                   9040:         $clientmobile = $env{'browser.mobile'};
                   9041:     }
                   9042:     if ($clientmobile) {
1.1137    raeburn  9043:         $result .= '
                   9044: <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
                   9045: <meta name="apple-mobile-web-app-capable" content="yes" />';
                   9046:     }
1.1278    raeburn  9047:     $result .= '<meta name="google" content="notranslate" />'."\n";
1.962     droeschl 9048:     return $result.'</head>';
1.306     albertel 9049: }
                   9050: 
                   9051: =pod
                   9052: 
1.340     albertel 9053: =item * &font_settings()
                   9054: 
                   9055: Returns neccessary <meta> to set the proper encoding
                   9056: 
1.1160    raeburn  9057: Inputs: optional reference to HASH -- $args passed to &headtag()
1.340     albertel 9058: 
                   9059: =cut
                   9060: 
                   9061: sub font_settings {
1.1160    raeburn  9062:     my ($args) = @_;
1.340     albertel 9063:     my $headerstring='';
1.1160    raeburn  9064:     if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
                   9065:         ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
1.1168    raeburn  9066:         $headerstring.=
                   9067:             '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"';
                   9068:         if (!$args->{'frameset'}) {
                   9069: 	    $headerstring.= ' /';
                   9070:         }
                   9071: 	$headerstring .= '>'."\n";
1.340     albertel 9072:     }
                   9073:     return $headerstring;
                   9074: }
                   9075: 
1.341     albertel 9076: =pod
                   9077: 
1.1064    raeburn  9078: =item * &print_suppression()
                   9079: 
                   9080: In course context returns css which causes the body to be blank when media="print",
                   9081: if printout generation is unavailable for the current resource.
                   9082: 
                   9083: This could be because:
                   9084: 
                   9085: (a) printstartdate is in the future
                   9086: 
                   9087: (b) printenddate is in the past
                   9088: 
                   9089: (c) there is an active exam block with "printout"
                   9090: functionality blocked
                   9091: 
                   9092: Users with pav, pfo or evb privileges are exempt.
                   9093: 
                   9094: Inputs: none
                   9095: 
                   9096: =cut
                   9097: 
                   9098: 
                   9099: sub print_suppression {
                   9100:     my $noprint;
                   9101:     if ($env{'request.course.id'}) {
                   9102:         my $scope = $env{'request.course.id'};
                   9103:         if ((&Apache::lonnet::allowed('pav',$scope)) ||
                   9104:             (&Apache::lonnet::allowed('pfo',$scope))) {
                   9105:             return;
                   9106:         }
                   9107:         if ($env{'request.course.sec'} ne '') {
                   9108:             $scope .= "/$env{'request.course.sec'}";
                   9109:             if ((&Apache::lonnet::allowed('pav',$scope)) ||
                   9110:                 (&Apache::lonnet::allowed('pfo',$scope))) {
1.1065    raeburn  9111:                 return;
1.1064    raeburn  9112:             }
                   9113:         }
                   9114:         my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   9115:         my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1372    raeburn  9116:         my $clientip = &Apache::lonnet::get_requestor_ip();
                   9117:         my $blocked = &blocking_status('printout',$clientip,$cnum,$cdom,undef,1);
1.1064    raeburn  9118:         if ($blocked) {
                   9119:             my $checkrole = "cm./$cdom/$cnum";
                   9120:             if ($env{'request.course.sec'} ne '') {
                   9121:                 $checkrole .= "/$env{'request.course.sec'}";
                   9122:             }
                   9123:             unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
                   9124:                     ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
                   9125:                 $noprint = 1;
                   9126:             }
                   9127:         }
                   9128:         unless ($noprint) {
                   9129:             my $symb = &Apache::lonnet::symbread();
                   9130:             if ($symb ne '') {
                   9131:                 my $navmap = Apache::lonnavmaps::navmap->new();
                   9132:                 if (ref($navmap)) {
                   9133:                     my $res = $navmap->getBySymb($symb);
                   9134:                     if (ref($res)) {
                   9135:                         if (!$res->resprintable()) {
                   9136:                             $noprint = 1;
                   9137:                         }
                   9138:                     }
                   9139:                 }
                   9140:             }
                   9141:         }
                   9142:         if ($noprint) {
                   9143:             return <<"ENDSTYLE";
                   9144: <style type="text/css" media="print">
                   9145:     body { display:none }
                   9146: </style>
                   9147: ENDSTYLE
                   9148:         }
                   9149:     }
                   9150:     return;
                   9151: }
                   9152: 
                   9153: =pod
                   9154: 
1.341     albertel 9155: =item * &xml_begin()
                   9156: 
                   9157: Returns the needed doctype and <html>
                   9158: 
                   9159: Inputs: none
                   9160: 
                   9161: =cut
                   9162: 
                   9163: sub xml_begin {
1.1168    raeburn  9164:     my ($is_frameset) = @_;
1.341     albertel 9165:     my $output='';
                   9166: 
                   9167:     if ($env{'browser.mathml'}) {
                   9168: 	$output='<?xml version="1.0"?>'
                   9169:             #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
                   9170: #            .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
                   9171:             
                   9172: #	    .'<!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">] >'
                   9173: 	    .'<!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">'
                   9174:             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" ' 
                   9175: 	    .'xmlns="http://www.w3.org/1999/xhtml">';
1.1168    raeburn  9176:     } elsif ($is_frameset) {
                   9177:         $output='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'."\n".
                   9178:                 '<html>'."\n";
1.341     albertel 9179:     } else {
1.1168    raeburn  9180: 	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n".
                   9181:                 '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
1.341     albertel 9182:     }
                   9183:     return $output;
                   9184: }
1.340     albertel 9185: 
                   9186: =pod
                   9187: 
1.306     albertel 9188: =item * &start_page()
                   9189: 
                   9190: Returns a complete <html> .. <body> section for LON-CAPA web pages.
                   9191: 
1.648     raeburn  9192: Inputs:
                   9193: 
                   9194: =over 4
                   9195: 
                   9196: $title - optional title for the page
                   9197: 
                   9198: $head_extra - optional extra HTML to incude inside the <head>
                   9199: 
                   9200: $args - additional optional args supported are:
                   9201: 
                   9202: =over 8
                   9203: 
                   9204:              only_body      -> is true will set &bodytag() onlybodytag
1.317     albertel 9205:                                     arg on
1.814     bisitz   9206:              no_nav_bar     -> is true will set &bodytag() no_nav_bar arg on
1.648     raeburn  9207:              add_entries    -> additional attributes to add to the  <body>
                   9208:              domain         -> force to color decorate a page for a 
1.317     albertel 9209:                                     specific domain
1.648     raeburn  9210:              function       -> force usage of a specific rolish color
1.317     albertel 9211:                                     scheme
1.648     raeburn  9212:              redirect       -> see &headtag()
                   9213:              bgcolor        -> override the default page bg color
                   9214:              js_ready       -> return a string ready for being used in 
1.317     albertel 9215:                                     a javascript writeln
1.648     raeburn  9216:              html_encode    -> return a string ready for being used in 
1.320     albertel 9217:                                     a html attribute
1.648     raeburn  9218:              force_register -> if is true will turn on the &bodytag()
1.317     albertel 9219:                                     $forcereg arg
1.648     raeburn  9220:              frameset       -> if true will start with a <frameset>
1.330     albertel 9221:                                     rather than <body>
1.648     raeburn  9222:              skip_phases    -> hash ref of 
1.338     albertel 9223:                                     head -> skip the <html><head> generation
                   9224:                                     body -> skip all <body> generation
1.648     raeburn  9225:              no_auto_mt_title -> prevent &mt()ing the title arg
1.867     kalberla 9226:              bread_crumbs ->             Array containing breadcrumbs
1.983     raeburn  9227:              bread_crumbs_component ->  if exists show it as headline else show only the breadcrumbs
1.1272    raeburn  9228:              bread_crumbs_nomenu -> if true will pass false as the value of $menulink
                   9229:                                     to lonhtmlcommon::breadcrumbs
1.1096    raeburn  9230:              group          -> includes the current group, if page is for a 
1.1274    raeburn  9231:                                specific group
                   9232:              use_absolute   -> for request for external resource or syllabus, this
                   9233:                                will contain https://<hostname> if server uses
                   9234:                                https (as per hosts.tab), but request is for http
                   9235:              hostname       -> hostname, originally from $r->hostname(), (optional).
1.1369    raeburn  9236:              links_disabled -> Links in primary and secondary menus are disabled
                   9237:                                (Can enable them once page has loaded - see lonroles.pm
                   9238:                                for an example).
1.361     albertel 9239: 
1.648     raeburn  9240: =back
1.460     albertel 9241: 
1.648     raeburn  9242: =back
1.562     albertel 9243: 
1.306     albertel 9244: =cut
                   9245: 
                   9246: sub start_page {
1.309     albertel 9247:     my ($title,$head_extra,$args) = @_;
1.318     albertel 9248:     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.319     albertel 9249: 
1.315     albertel 9250:     $env{'internal.start_page'}++;
1.1359    raeburn  9251:     my ($result,@advtools,$ltiscope,$ltiuri,%ltimenu,$menucoll,%menu);
1.964     droeschl 9252: 
1.338     albertel 9253:     if (! exists($args->{'skip_phases'}{'head'}) ) {
1.1168    raeburn  9254:         $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
1.338     albertel 9255:     }
1.1316    raeburn  9256: 
                   9257:     if (($env{'request.course.id'}) && ($env{'request.lti.login'})) {
1.1318    raeburn  9258:         if ($env{'course.'.$env{'request.course.id'}.'.lti.override'}) {
                   9259:             unless ($env{'course.'.$env{'request.course.id'}.'.lti.topmenu'}) {
                   9260:                 $args->{'no_primary_menu'} = 1;
                   9261:             }
                   9262:             unless ($env{'course.'.$env{'request.course.id'}.'.lti.inlinemenu'}) {
                   9263:                 $args->{'no_inline_menu'} = 1;
                   9264:             }
                   9265:             if ($env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'}) {
                   9266:                 map { $ltimenu{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'});
                   9267:             }
                   9268:         } else {
                   9269:             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   9270:             my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
                   9271:             if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') {
                   9272:                 unless ($lti{$env{'request.lti.login'}}{'topmenu'}) {
                   9273:                     $args->{'no_primary_menu'} = 1;
                   9274:                 }
                   9275:                 unless ($lti{$env{'request.lti.login'}}{'inlinemenu'}) {
                   9276:                     $args->{'no_inline_menu'} = 1;
                   9277:                 }
                   9278:                 if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') {
                   9279:                     map { $ltimenu{$_} = 1; } @{$lti{$env{'request.lti.login'}}{'lcmenu'}};
                   9280:                 }
                   9281:             }
                   9282:         }
1.1316    raeburn  9283:         ($ltiscope,$ltiuri) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'},
                   9284:                                   $env{'course.'.$env{'request.course.id'}.'.domain'},
                   9285:                                   $env{'course.'.$env{'request.course.id'}.'.num'});
1.1359    raeburn  9286:     } elsif ($env{'request.course.id'}) {
                   9287:         my $expiretime=600;
                   9288:         if ((time-$env{'course.'.$env{'request.course.id'}.'.last_cache'}) > $expiretime) {
                   9289:             &Apache::lonnet::coursedescription($env{'request.course.id'},{'freshen_cache' => 1});
                   9290:         }
                   9291:         my ($deeplinkmenu,$menuref);
                   9292:         ($menucoll,$deeplinkmenu,$menuref) = &menucoll_in_effect();
                   9293:         if ($menucoll) {
                   9294:             if (ref($menuref) eq 'HASH') {
                   9295:                 %menu = %{$menuref};
                   9296:             }
                   9297:             if ($menu{'top'} eq 'n') {
                   9298:                 $args->{'no_primary_menu'} = 1;
                   9299:             }
                   9300:             if ($menu{'inline'} eq 'n') {
                   9301:                 unless (&Apache::lonnet::allowed('opa')) {
                   9302:                     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   9303:                     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   9304:                     my $crstype = &course_type();
                   9305:                     my $now = time;
                   9306:                     my $ccrole;
                   9307:                     if ($crstype eq 'Community') {
                   9308:                         $ccrole = 'co';
                   9309:                     } else {
                   9310:                         $ccrole = 'cc';
                   9311:                     }
                   9312:                     if ($env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum}) {
                   9313:                         my ($start,$end) = split(/\./,$env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum});
                   9314:                         if ((($start) && ($start<0)) ||
                   9315:                             (($end) && ($end<$now))  ||
                   9316:                             (($start) && ($now<$start))) {
                   9317:                             $args->{'no_inline_menu'} = 1;
                   9318:                         }
                   9319:                     } else {
                   9320:                         $args->{'no_inline_menu'} = 1;
                   9321:                     }
                   9322:                 }
                   9323:             }
                   9324:         }
1.1316    raeburn  9325:     }
1.1359    raeburn  9326: 
1.338     albertel 9327:     if (! exists($args->{'skip_phases'}{'body'}) ) {
                   9328: 	if ($args->{'frameset'}) {
                   9329: 	    my $attr_string = &make_attr_string($args->{'force_register'},
                   9330: 						$args->{'add_entries'});
                   9331: 	    $result .= "\n<frameset $attr_string>\n";
1.831     bisitz   9332:         } else {
                   9333:             $result .=
                   9334:                 &bodytag($title, 
                   9335:                          $args->{'function'},       $args->{'add_entries'},
                   9336:                          $args->{'only_body'},      $args->{'domain'},
                   9337:                          $args->{'force_register'}, $args->{'no_nav_bar'},
1.1096    raeburn  9338:                          $args->{'bgcolor'},        $args,
1.1359    raeburn  9339:                          \@advtools,$ltiscope,$ltiuri,\%ltimenu,$menucoll,\%menu);
1.831     bisitz   9340:         }
1.330     albertel 9341:     }
1.338     albertel 9342: 
1.315     albertel 9343:     if ($args->{'js_ready'}) {
1.713     kaisler  9344: 		$result = &js_ready($result);
1.315     albertel 9345:     }
1.320     albertel 9346:     if ($args->{'html_encode'}) {
1.713     kaisler  9347: 		$result = &html_encode($result);
                   9348:     }
                   9349: 
1.813     bisitz   9350:     # Preparation for new and consistent functionlist at top of screen
                   9351:     # if ($args->{'functionlist'}) {
                   9352:     #            $result .= &build_functionlist();
                   9353:     #}
                   9354: 
1.964     droeschl 9355:     # Don't add anything more if only_body wanted or in const space
                   9356:     return $result if    $args->{'only_body'} 
                   9357:                       || $env{'request.state'} eq 'construct';
1.813     bisitz   9358: 
                   9359:     #Breadcrumbs
1.758     kaisler  9360:     if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
                   9361: 		&Apache::lonhtmlcommon::clear_breadcrumbs();
                   9362: 		#if any br links exists, add them to the breadcrumbs
                   9363: 		if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {         
                   9364: 			foreach my $crumb (@{$args->{'bread_crumbs'}}){
                   9365: 				&Apache::lonhtmlcommon::add_breadcrumb($crumb);
                   9366: 			}
                   9367: 		}
1.1096    raeburn  9368:                 # if @advtools array contains items add then to the breadcrumbs
                   9369:                 if (@advtools > 0) {
                   9370:                     &Apache::lonmenu::advtools_crumbs(@advtools);
                   9371:                 }
1.1272    raeburn  9372:                 my $menulink;
                   9373:                 # if arg: bread_crumbs_nomenu is true pass 0 as $menulink item.
                   9374:                 if ((exists($args->{'bread_crumbs_nomenu'})) ||
1.1312    raeburn  9375:                      ($ltiscope eq 'map') || ($ltiscope eq 'resource') ||
1.1272    raeburn  9376:                      ((($args->{'crstype'} eq 'Placement') || (($env{'request.course.id'}) &&
                   9377:                      ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Placement'))) &&
                   9378:                      (!$env{'request.role.adv'}))) {
                   9379:                     $menulink = 0;
                   9380:                 } else {
                   9381:                     undef($menulink);
                   9382:                 }
1.758     kaisler  9383: 		#if bread_crumbs_component exists show it as headline else show only the breadcrumbs
                   9384: 		if(exists($args->{'bread_crumbs_component'})){
1.1272    raeburn  9385: 			$result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},'',$menulink);
1.1237    raeburn  9386:                 } else {
1.1272    raeburn  9387: 			$result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink);
1.758     kaisler  9388: 		}
1.320     albertel 9389:     }
1.315     albertel 9390:     return $result;
1.306     albertel 9391: }
                   9392: 
                   9393: sub end_page {
1.315     albertel 9394:     my ($args) = @_;
                   9395:     $env{'internal.end_page'}++;
1.330     albertel 9396:     my $result;
1.335     albertel 9397:     if ($args->{'discussion'}) {
                   9398: 	my ($target,$parser);
                   9399: 	if (ref($args->{'discussion'})) {
                   9400: 	    ($target,$parser) =($args->{'discussion'}{'target'},
                   9401: 				$args->{'discussion'}{'parser'});
                   9402: 	}
                   9403: 	$result .= &Apache::lonxml::xmlend($target,$parser);
                   9404:     }
1.330     albertel 9405:     if ($args->{'frameset'}) {
                   9406: 	$result .= '</frameset>';
                   9407:     } else {
1.635     raeburn  9408: 	$result .= &endbodytag($args);
1.330     albertel 9409:     }
1.1080    raeburn  9410:     unless ($args->{'notbody'}) {
                   9411:         $result .= "\n</html>";
                   9412:     }
1.330     albertel 9413: 
1.315     albertel 9414:     if ($args->{'js_ready'}) {
1.317     albertel 9415: 	$result = &js_ready($result);
1.315     albertel 9416:     }
1.335     albertel 9417: 
1.320     albertel 9418:     if ($args->{'html_encode'}) {
                   9419: 	$result = &html_encode($result);
                   9420:     }
1.335     albertel 9421: 
1.315     albertel 9422:     return $result;
                   9423: }
                   9424: 
1.1359    raeburn  9425: sub menucoll_in_effect {
                   9426:     my ($menucoll,$deeplinkmenu,%menu);
                   9427:     if ($env{'request.course.id'}) {
                   9428:         $menucoll = $env{'course.'.$env{'request.course.id'}.'.menudefault'};
1.1362    raeburn  9429:         if ($env{'request.deeplink.login'}) {
1.1370    raeburn  9430:             my ($deeplink_symb,$deeplink,$check_login_symb);
1.1362    raeburn  9431:             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   9432:             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   9433:             if ($env{'request.noversionuri'} =~ m{^/(res|uploaded)/}) {
                   9434:                 if ($env{'request.noversionuri'} =~ /\.(page|sequence)$/) {
                   9435:                     my $navmap = Apache::lonnavmaps::navmap->new();
                   9436:                     if (ref($navmap)) {
                   9437:                         $deeplink = $navmap->get_mapparam(undef,
                   9438:                                                           &Apache::lonnet::declutter($env{'request.noversionuri'}),
                   9439:                                                           '0.deeplink');
1.1370    raeburn  9440:                     } else {
                   9441:                         $check_login_symb = 1;
1.1362    raeburn  9442:                     }
                   9443:                 } else {
1.1370    raeburn  9444:                     my $symb = &Apache::lonnet::symbread();
                   9445:                     if ($symb) {
                   9446:                         $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$symb);
                   9447:                     } else {
                   9448:                         $check_login_symb = 1;
                   9449:                     }
1.1362    raeburn  9450:                 }
                   9451:             } else {
1.1370    raeburn  9452:                 $check_login_symb = 1;
                   9453:             }
                   9454:             if ($check_login_symb) {
1.1362    raeburn  9455:                 $deeplink_symb = &deeplink_login_symb($cnum,$cdom);
                   9456:                 if ($deeplink_symb =~ /\.(page|sequence)$/) {
                   9457:                     my $mapname = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($deeplink_symb))[2]);
                   9458:                     my $navmap = Apache::lonnavmaps::navmap->new();
                   9459:                     if (ref($navmap)) {
                   9460:                         $deeplink = $navmap->get_mapparam(undef,$mapname,'0.deeplink');
                   9461:                     }
                   9462:                 } else {
                   9463:                     $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$deeplink_symb);
                   9464:                 }
                   9465:             }
1.1359    raeburn  9466:             if ($deeplink ne '') {
1.1378  ! raeburn  9467:                 my ($state,$others,$listed,$scope,$protect,$display,$target) = split(/,/,$deeplink);
1.1359    raeburn  9468:                 if ($display =~ /^\d+$/) {
                   9469:                     $deeplinkmenu = 1;
                   9470:                     $menucoll = $display;
                   9471:                 }
                   9472:             }
                   9473:         }
                   9474:         if ($menucoll) {
                   9475:             %menu = &page_menu($env{'course.'.$env{'request.course.id'}.'.menucollections'},$menucoll);
                   9476:         }
                   9477:     }
                   9478:     return ($menucoll,$deeplinkmenu,\%menu);
                   9479: }
                   9480: 
1.1362    raeburn  9481: sub deeplink_login_symb {
                   9482:     my ($cnum,$cdom) = @_;
                   9483:     my $login_symb;
                   9484:     if ($env{'request.deeplink.login'}) {
1.1364    raeburn  9485:         $login_symb = &symb_from_tinyurl($env{'request.deeplink.login'},$cnum,$cdom);
                   9486:     }
                   9487:     return $login_symb;
                   9488: }
                   9489: 
                   9490: sub symb_from_tinyurl {
                   9491:     my ($url,$cnum,$cdom) = @_;
                   9492:     if ($url =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
                   9493:         my $key = $1;
                   9494:         my ($tinyurl,$login);
                   9495:         my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
                   9496:         if (defined($cached)) {
                   9497:             $tinyurl = $result;
                   9498:         } else {
                   9499:             my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
                   9500:             my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
                   9501:             if ($currtiny{$key} ne '') {
                   9502:                 $tinyurl = $currtiny{$key};
                   9503:                 &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
1.1362    raeburn  9504:             }
1.1364    raeburn  9505:         }
                   9506:         if ($tinyurl ne '') {
                   9507:             my ($cnumreq,$symb) = split(/\&/,$tinyurl);
                   9508:             if (wantarray) {
                   9509:                 return ($cnumreq,$symb);
                   9510:             } elsif ($cnumreq eq $cnum) {
                   9511:                 return $symb;
1.1362    raeburn  9512:             }
                   9513:         }
                   9514:     }
1.1364    raeburn  9515:     if (wantarray) {
                   9516:         return ();
                   9517:     } else {
                   9518:         return;
                   9519:     }
1.1362    raeburn  9520: }
                   9521: 
1.1034    www      9522: sub wishlist_window {
                   9523:     return(<<'ENDWISHLIST');
1.1046    raeburn  9524: <script type="text/javascript">
1.1034    www      9525: // <![CDATA[
                   9526: // <!-- BEGIN LON-CAPA Internal
                   9527: function set_wishlistlink(title, path) {
                   9528:     if (!title) {
                   9529:         title = document.title;
                   9530:         title = title.replace(/^LON-CAPA /,'');
                   9531:     }
1.1175    raeburn  9532:     title = encodeURIComponent(title);
1.1203    raeburn  9533:     title = title.replace("'","\\\'");
1.1034    www      9534:     if (!path) {
                   9535:         path = location.pathname;
                   9536:     }
1.1175    raeburn  9537:     path = encodeURIComponent(path);
1.1203    raeburn  9538:     path = path.replace("'","\\\'");
1.1034    www      9539:     Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path,
                   9540:                       'wishlistNewLink','width=560,height=350,scrollbars=0');
                   9541: }
                   9542: // END LON-CAPA Internal -->
                   9543: // ]]>
                   9544: </script>
                   9545: ENDWISHLIST
                   9546: }
                   9547: 
1.1030    www      9548: sub modal_window {
                   9549:     return(<<'ENDMODAL');
1.1046    raeburn  9550: <script type="text/javascript">
1.1030    www      9551: // <![CDATA[
                   9552: // <!-- BEGIN LON-CAPA Internal
                   9553: var modalWindow = {
                   9554: 	parent:"body",
                   9555: 	windowId:null,
                   9556: 	content:null,
                   9557: 	width:null,
                   9558: 	height:null,
                   9559: 	close:function()
                   9560: 	{
                   9561: 	        $(".LCmodal-window").remove();
                   9562: 	        $(".LCmodal-overlay").remove();
                   9563: 	},
                   9564: 	open:function()
                   9565: 	{
                   9566: 		var modal = "";
                   9567: 		modal += "<div class=\"LCmodal-overlay\"></div>";
                   9568: 		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;\">";
                   9569: 		modal += this.content;
                   9570: 		modal += "</div>";	
                   9571: 
                   9572: 		$(this.parent).append(modal);
                   9573: 
                   9574: 		$(".LCmodal-window").append("<a class=\"LCclose-window\"></a>");
                   9575: 		$(".LCclose-window").click(function(){modalWindow.close();});
                   9576: 		$(".LCmodal-overlay").click(function(){modalWindow.close();});
                   9577: 	}
                   9578: };
1.1140    raeburn  9579: 	var openMyModal = function(source,width,height,scrolling,transparency,style)
1.1030    www      9580: 	{
1.1266    raeburn  9581:                 source = source.replace(/'/g,"&#39;");
1.1030    www      9582: 		modalWindow.windowId = "myModal";
                   9583: 		modalWindow.width = width;
                   9584: 		modalWindow.height = height;
1.1196    raeburn  9585: 		modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'></iframe>";
1.1030    www      9586: 		modalWindow.open();
1.1208    raeburn  9587: 	};
1.1030    www      9588: // END LON-CAPA Internal -->
                   9589: // ]]>
                   9590: </script>
                   9591: ENDMODAL
                   9592: }
                   9593: 
                   9594: sub modal_link {
1.1140    raeburn  9595:     my ($link,$linktext,$width,$height,$target,$scrolling,$title,$transparency,$style)=@_;
1.1030    www      9596:     unless ($width) { $width=480; }
                   9597:     unless ($height) { $height=400; }
1.1031    www      9598:     unless ($scrolling) { $scrolling='yes'; }
1.1140    raeburn  9599:     unless ($transparency) { $transparency='true'; }
                   9600: 
1.1074    raeburn  9601:     my $target_attr;
                   9602:     if (defined($target)) {
                   9603:         $target_attr = 'target="'.$target.'"';
                   9604:     }
                   9605:     return <<"ENDLINK";
1.1336    raeburn  9606: <a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">$linktext</a>
1.1074    raeburn  9607: ENDLINK
1.1030    www      9608: }
                   9609: 
1.1032    www      9610: sub modal_adhoc_script {
1.1365    raeburn  9611:     my ($funcname,$width,$height,$content,$possmathjax)=@_;
                   9612:     my $mathjax;
                   9613:     if ($possmathjax) {
                   9614:         $mathjax = <<'ENDJAX';
                   9615:                if (typeof MathJax == 'object') {
                   9616:                    MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
                   9617:                }
                   9618: ENDJAX
                   9619:     }
1.1032    www      9620:     return (<<ENDADHOC);
1.1046    raeburn  9621: <script type="text/javascript">
1.1032    www      9622: // <![CDATA[
                   9623:         var $funcname = function()
                   9624:         {
                   9625:                 modalWindow.windowId = "myModal";
                   9626:                 modalWindow.width = $width;
                   9627:                 modalWindow.height = $height;
                   9628:                 modalWindow.content = '$content';
                   9629:                 modalWindow.open();
1.1365    raeburn  9630:                 $mathjax
1.1032    www      9631:         };  
                   9632: // ]]>
                   9633: </script>
                   9634: ENDADHOC
                   9635: }
                   9636: 
1.1041    www      9637: sub modal_adhoc_inner {
1.1365    raeburn  9638:     my ($funcname,$width,$height,$content,$possmathjax)=@_;
1.1041    www      9639:     my $innerwidth=$width-20;
                   9640:     $content=&js_ready(
1.1140    raeburn  9641:                  &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
                   9642:                  &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).
                   9643:                  $content.
1.1041    www      9644:                  &end_scrollbox().
1.1140    raeburn  9645:                  &end_page()
1.1041    www      9646:              );
1.1365    raeburn  9647:     return &modal_adhoc_script($funcname,$width,$height,$content,$possmathjax);
1.1041    www      9648: }
                   9649: 
                   9650: sub modal_adhoc_window {
1.1365    raeburn  9651:     my ($funcname,$width,$height,$content,$linktext,$possmathjax)=@_;
                   9652:     return &modal_adhoc_inner($funcname,$width,$height,$content,$possmathjax).
1.1041    www      9653:            "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
                   9654: }
                   9655: 
                   9656: sub modal_adhoc_launch {
                   9657:     my ($funcname,$width,$height,$content)=@_;
                   9658:     return &modal_adhoc_inner($funcname,$width,$height,$content).(<<ENDLAUNCH);
                   9659: <script type="text/javascript">
                   9660: // <![CDATA[
                   9661: $funcname();
                   9662: // ]]>
                   9663: </script>
                   9664: ENDLAUNCH
                   9665: }
                   9666: 
                   9667: sub modal_adhoc_close {
                   9668:     return (<<ENDCLOSE);
                   9669: <script type="text/javascript">
                   9670: // <![CDATA[
                   9671: modalWindow.close();
                   9672: // ]]>
                   9673: </script>
                   9674: ENDCLOSE
                   9675: }
                   9676: 
1.1038    www      9677: sub togglebox_script {
                   9678:    return(<<ENDTOGGLE);
                   9679: <script type="text/javascript"> 
                   9680: // <![CDATA[
                   9681: function LCtoggleDisplay(id,hidetext,showtext) {
                   9682:    link = document.getElementById(id + "link").childNodes[0];
                   9683:    with (document.getElementById(id).style) {
                   9684:       if (display == "none" ) {
                   9685:           display = "inline";
                   9686:           link.nodeValue = hidetext;
                   9687:         } else {
                   9688:           display = "none";
                   9689:           link.nodeValue = showtext;
                   9690:        }
                   9691:    }
                   9692: }
                   9693: // ]]>
                   9694: </script>
                   9695: ENDTOGGLE
                   9696: }
                   9697: 
1.1039    www      9698: sub start_togglebox {
                   9699:     my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
                   9700:     unless ($heading) { $heading=''; } else { $heading.=' '; }
                   9701:     unless ($showtext) { $showtext=&mt('show'); }
                   9702:     unless ($hidetext) { $hidetext=&mt('hide'); }
                   9703:     unless ($headerbg) { $headerbg='#FFFFFF'; }
                   9704:     return &start_data_table().
                   9705:            &start_data_table_header_row().
                   9706:            '<td bgcolor="'.$headerbg.'">'.$heading.
                   9707:            '[<a id="'.$id.'link" href="javascript:LCtoggleDisplay(\''.$id.'\',\''.$hidetext.'\',\''.
                   9708:            $showtext.'\')">'.$showtext.'</a>]</td>'.
                   9709:            &end_data_table_header_row().
                   9710:            '<tr id="'.$id.'" style="display:none""><td>';
                   9711: }
                   9712: 
                   9713: sub end_togglebox {
                   9714:     return '</td></tr>'.&end_data_table();
                   9715: }
                   9716: 
1.1041    www      9717: sub LCprogressbar_script {
1.1302    raeburn  9718:    my ($id,$number_to_do)=@_;
                   9719:    if ($number_to_do) {
                   9720:        return(<<ENDPROGRESS);
1.1041    www      9721: <script type="text/javascript">
                   9722: // <![CDATA[
1.1045    www      9723: \$('#progressbar$id').progressbar({
1.1041    www      9724:   value: 0,
                   9725:   change: function(event, ui) {
                   9726:     var newVal = \$(this).progressbar('option', 'value');
                   9727:     \$('.pblabel', this).text(LCprogressTxt);
                   9728:   }
                   9729: });
                   9730: // ]]>
                   9731: </script>
                   9732: ENDPROGRESS
1.1302    raeburn  9733:    } else {
                   9734:        return(<<ENDPROGRESS);
                   9735: <script type="text/javascript">
                   9736: // <![CDATA[
                   9737: \$('#progressbar$id').progressbar({
                   9738:   value: false,
                   9739:   create: function(event, ui) {
                   9740:     \$('.ui-widget-header', this).css({'background':'#F0F0F0'});
                   9741:     \$('.ui-progressbar-overlay', this).css({'margin':'0'});
                   9742:   }
                   9743: });
                   9744: // ]]>
                   9745: </script>
                   9746: ENDPROGRESS
                   9747:    }
1.1041    www      9748: }
                   9749: 
                   9750: sub LCprogressbarUpdate_script {
                   9751:    return(<<ENDPROGRESSUPDATE);
                   9752: <style type="text/css">
                   9753: .ui-progressbar { position:relative; }
1.1302    raeburn  9754: .progress-label {position: absolute; width: 100%; text-align: center; top: 1px; font-weight: bold; text-shadow: 1px 1px 0 #fff;margin: 0; line-height: 200%; }
1.1041    www      9755: .pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
                   9756: </style>
                   9757: <script type="text/javascript">
                   9758: // <![CDATA[
1.1045    www      9759: var LCprogressTxt='---';
                   9760: 
1.1302    raeburn  9761: function LCupdateProgress(percent,progresstext,id,maxnum) {
1.1041    www      9762:    LCprogressTxt=progresstext;
1.1302    raeburn  9763:    if ((maxnum == '') || (maxnum == undefined) || (maxnum == null)) {
                   9764:        \$('#progressbar'+id).find('.progress-label').text(LCprogressTxt);
                   9765:    } else if (percent === \$('#progressbar'+id).progressbar( "value" )) {
1.1301    raeburn  9766:        \$('#progressbar'+id).find('.pblabel').text(LCprogressTxt);
                   9767:    } else {
                   9768:        \$('#progressbar'+id).progressbar('value',percent);
                   9769:    }
1.1041    www      9770: }
                   9771: // ]]>
                   9772: </script>
                   9773: ENDPROGRESSUPDATE
                   9774: }
                   9775: 
1.1042    www      9776: my $LClastpercent;
1.1045    www      9777: my $LCidcnt;
                   9778: my $LCcurrentid;
1.1042    www      9779: 
1.1041    www      9780: sub LCprogressbar {
1.1302    raeburn  9781:     my ($r,$number_to_do,$preamble)=@_;
1.1042    www      9782:     $LClastpercent=0;
1.1045    www      9783:     $LCidcnt++;
                   9784:     $LCcurrentid=$$.'_'.$LCidcnt;
1.1302    raeburn  9785:     my ($starting,$content);
                   9786:     if ($number_to_do) {
                   9787:         $starting=&mt('Starting');
                   9788:         $content=(<<ENDPROGBAR);
                   9789: $preamble
1.1045    www      9790:   <div id="progressbar$LCcurrentid">
1.1041    www      9791:     <span class="pblabel">$starting</span>
                   9792:   </div>
                   9793: ENDPROGBAR
1.1302    raeburn  9794:     } else {
                   9795:         $starting=&mt('Loading...');
                   9796:         $LClastpercent='false';
                   9797:         $content=(<<ENDPROGBAR);
                   9798: $preamble
                   9799:   <div id="progressbar$LCcurrentid">
                   9800:       <div class="progress-label">$starting</div>
                   9801:   </div>
                   9802: ENDPROGBAR
                   9803:     }
                   9804:     &r_print($r,$content.&LCprogressbar_script($LCcurrentid,$number_to_do));
1.1041    www      9805: }
                   9806: 
                   9807: sub LCprogressbarUpdate {
1.1302    raeburn  9808:     my ($r,$val,$text,$number_to_do)=@_;
                   9809:     if ($number_to_do) {
                   9810:         unless ($val) { 
                   9811:             if ($LClastpercent) {
                   9812:                 $val=$LClastpercent;
                   9813:             } else {
                   9814:                 $val=0;
                   9815:             }
                   9816:         }
                   9817:         if ($val<0) { $val=0; }
                   9818:         if ($val>100) { $val=0; }
                   9819:         $LClastpercent=$val;
                   9820:         unless ($text) { $text=$val.'%'; }
                   9821:     } else {
                   9822:         $val = 'false';
1.1042    www      9823:     }
1.1041    www      9824:     $text=&js_ready($text);
1.1044    www      9825:     &r_print($r,<<ENDUPDATE);
1.1041    www      9826: <script type="text/javascript">
                   9827: // <![CDATA[
1.1302    raeburn  9828: LCupdateProgress($val,'$text','$LCcurrentid','$number_to_do');
1.1041    www      9829: // ]]>
                   9830: </script>
                   9831: ENDUPDATE
1.1035    www      9832: }
                   9833: 
1.1042    www      9834: sub LCprogressbarClose {
                   9835:     my ($r)=@_;
                   9836:     $LClastpercent=0;
1.1044    www      9837:     &r_print($r,<<ENDCLOSE);
1.1042    www      9838: <script type="text/javascript">
                   9839: // <![CDATA[
1.1045    www      9840: \$("#progressbar$LCcurrentid").hide('slow'); 
1.1042    www      9841: // ]]>
                   9842: </script>
                   9843: ENDCLOSE
1.1044    www      9844: }
                   9845: 
                   9846: sub r_print {
                   9847:     my ($r,$to_print)=@_;
                   9848:     if ($r) {
                   9849:       $r->print($to_print);
                   9850:       $r->rflush();
                   9851:     } else {
                   9852:       print($to_print);
                   9853:     }
1.1042    www      9854: }
                   9855: 
1.320     albertel 9856: sub html_encode {
                   9857:     my ($result) = @_;
                   9858: 
1.322     albertel 9859:     $result = &HTML::Entities::encode($result,'<>&"');
1.320     albertel 9860:     
                   9861:     return $result;
                   9862: }
1.1044    www      9863: 
1.317     albertel 9864: sub js_ready {
                   9865:     my ($result) = @_;
                   9866: 
1.323     albertel 9867:     $result =~ s/[\n\r]/ /xmsg;
                   9868:     $result =~ s/\\/\\\\/xmsg;
                   9869:     $result =~ s/'/\\'/xmsg;
1.372     albertel 9870:     $result =~ s{</}{<\\/}xmsg;
1.317     albertel 9871:     
                   9872:     return $result;
                   9873: }
                   9874: 
1.315     albertel 9875: sub validate_page {
                   9876:     if (  exists($env{'internal.start_page'})
1.316     albertel 9877: 	  &&     $env{'internal.start_page'} > 1) {
                   9878: 	&Apache::lonnet::logthis('start_page called multiple times '.
1.318     albertel 9879: 				 $env{'internal.start_page'}.' '.
1.316     albertel 9880: 				 $ENV{'request.filename'});
1.315     albertel 9881:     }
                   9882:     if (  exists($env{'internal.end_page'})
1.316     albertel 9883: 	  &&     $env{'internal.end_page'} > 1) {
                   9884: 	&Apache::lonnet::logthis('end_page called multiple times '.
1.318     albertel 9885: 				 $env{'internal.end_page'}.' '.
1.316     albertel 9886: 				 $env{'request.filename'});
1.315     albertel 9887:     }
                   9888:     if (     exists($env{'internal.start_page'})
                   9889: 	&& ! exists($env{'internal.end_page'})) {
1.316     albertel 9890: 	&Apache::lonnet::logthis('start_page called without end_page '.
                   9891: 				 $env{'request.filename'});
1.315     albertel 9892:     }
                   9893:     if (   ! exists($env{'internal.start_page'})
                   9894: 	&&   exists($env{'internal.end_page'})) {
1.316     albertel 9895: 	&Apache::lonnet::logthis('end_page called without start_page'.
                   9896: 				 $env{'request.filename'});
1.315     albertel 9897:     }
1.306     albertel 9898: }
1.315     albertel 9899: 
1.996     www      9900: 
                   9901: sub start_scrollbox {
1.1140    raeburn  9902:     my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready) = @_;
1.998     raeburn  9903:     unless ($outerwidth) { $outerwidth='520px'; }
                   9904:     unless ($width) { $width='500px'; }
                   9905:     unless ($height) { $height='200px'; }
1.1075    raeburn  9906:     my ($table_id,$div_id,$tdcol);
1.1018    raeburn  9907:     if ($id ne '') {
1.1140    raeburn  9908:         $table_id = ' id="table_'.$id.'"';
1.1137    raeburn  9909:         $div_id = ' id="div_'.$id.'"';
1.1018    raeburn  9910:     }
1.1075    raeburn  9911:     if ($bgcolor ne '') {
                   9912:         $tdcol = "background-color: $bgcolor;";
                   9913:     }
1.1137    raeburn  9914:     my $nicescroll_js;
                   9915:     if ($env{'browser.mobile'}) {
1.1140    raeburn  9916:         $nicescroll_js = &nicescroll_javascript('div_'.$id,$cursor,$needjsready);
                   9917:     }
                   9918:     return <<"END";
                   9919: $nicescroll_js
                   9920: 
                   9921: <table style="width: $outerwidth; border: 1px solid none;"$table_id><tr><td style="width: $width;$tdcol">
                   9922: <div style="overflow:auto; width:$width; height:$height;"$div_id>
                   9923: END
                   9924: }
                   9925: 
                   9926: sub end_scrollbox {
                   9927:     return '</div></td></tr></table>';
                   9928: }
                   9929: 
                   9930: sub nicescroll_javascript {
                   9931:     my ($id,$cursor,$needjsready,$framecheck,$location) = @_;
                   9932:     my %options;
                   9933:     if (ref($cursor) eq 'HASH') {
                   9934:         %options = %{$cursor};
                   9935:     }
                   9936:     unless ($options{'railalign'} =~ /^left|right$/) {
                   9937:         $options{'railalign'} = 'left';
                   9938:     }
                   9939:     unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
                   9940:         my $function  = &get_users_function();
                   9941:         $options{'cursorcolor'} = &designparm($function.'.sidebg',$env{'request.role.domain'});
1.1138    raeburn  9942:         unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
1.1140    raeburn  9943:             $options{'cursorcolor'} = '#00F';
1.1138    raeburn  9944:         }
1.1140    raeburn  9945:     }
                   9946:     if ($options{'cursoropacity'} =~ /^[\d.]+$/) {
                   9947:         unless ($options{'cursoropacity'} >= 0.0 && $options{'cursoropacity'} <=1.0) {
1.1138    raeburn  9948:             $options{'cursoropacity'}='1.0';
                   9949:         }
1.1140    raeburn  9950:     } else {
                   9951:         $options{'cursoropacity'}='1.0';
                   9952:     }
                   9953:     if ($options{'cursorfixedheight'} eq 'none') {
                   9954:         delete($options{'cursorfixedheight'});
                   9955:     } else {
                   9956:         unless ($options{'cursorfixedheight'} =~ /^\d+$/) { $options{'cursorfixedheight'}='50'; }
                   9957:     }
                   9958:     unless ($options{'railoffset'} =~ /^{[\w\:\d\-,]+}$/) {
                   9959:         delete($options{'railoffset'});
                   9960:     }
                   9961:     my @niceoptions;
                   9962:     while (my($key,$value) = each(%options)) {
                   9963:         if ($value =~ /^\{.+\}$/) {
                   9964:             push(@niceoptions,$key.':'.$value);
1.1138    raeburn  9965:         } else {
1.1140    raeburn  9966:             push(@niceoptions,$key.':"'.$value.'"');
1.1138    raeburn  9967:         }
1.1140    raeburn  9968:     }
                   9969:     my $nicescroll_js = '
1.1137    raeburn  9970: $(document).ready(
1.1140    raeburn  9971:       function() {
                   9972:           $("#'.$id.'").niceScroll({'.join(',',@niceoptions).'});
                   9973:       }
1.1137    raeburn  9974: );
                   9975: ';
1.1140    raeburn  9976:     if ($framecheck) {
                   9977:         $nicescroll_js .= '
                   9978: function expand_div(caller) {
                   9979:     if (top === self) {
                   9980:         document.getElementById("'.$id.'").style.width = "auto";
                   9981:         document.getElementById("'.$id.'").style.height = "auto";
                   9982:     } else {
                   9983:         try {
                   9984:             if (parent.frames) {
                   9985:                 if (parent.frames.length > 1) {
                   9986:                     var framesrc = parent.frames[1].location.href;
                   9987:                     var currsrc = framesrc.replace(/\#.*$/,"");
                   9988:                     if ((caller == "search") || (currsrc == "'.$location.'")) {
                   9989:                         document.getElementById("'.$id.'").style.width = "auto";
                   9990:                         document.getElementById("'.$id.'").style.height = "auto";
                   9991:                     }
                   9992:                 }
                   9993:             }
                   9994:         } catch (e) {
                   9995:             return;
                   9996:         }
1.1137    raeburn  9997:     }
1.1140    raeburn  9998:     return;
1.996     www      9999: }
1.1140    raeburn  10000: ';
                   10001:     }
                   10002:     if ($needjsready) {
                   10003:         $nicescroll_js = '
                   10004: <script type="text/javascript">'."\n".$nicescroll_js."\n</script>\n";
                   10005:     } else {
                   10006:         $nicescroll_js = &Apache::lonhtmlcommon::scripttag($nicescroll_js);
                   10007:     }
                   10008:     return $nicescroll_js;
1.996     www      10009: }
                   10010: 
1.318     albertel 10011: sub simple_error_page {
1.1150    bisitz   10012:     my ($r,$title,$msg,$args) = @_;
1.1304    raeburn  10013:     my %displayargs;
1.1151    raeburn  10014:     if (ref($args) eq 'HASH') {
                   10015:         if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
1.1304    raeburn  10016:         if ($args->{'only_body'}) {
                   10017:             $displayargs{'only_body'} = 1;
                   10018:         }
                   10019:         if ($args->{'no_nav_bar'}) {
                   10020:             $displayargs{'no_nav_bar'} = 1;
                   10021:         }
1.1151    raeburn  10022:     } else {
                   10023:         $msg = &mt($msg);
                   10024:     }
1.1150    bisitz   10025: 
1.318     albertel 10026:     my $page =
1.1304    raeburn  10027: 	&Apache::loncommon::start_page($title,'',\%displayargs).
1.1150    bisitz   10028: 	'<p class="LC_error">'.$msg.'</p>'.
1.318     albertel 10029: 	&Apache::loncommon::end_page();
                   10030:     if (ref($r)) {
                   10031: 	$r->print($page);
1.327     albertel 10032: 	return;
1.318     albertel 10033:     }
                   10034:     return $page;
                   10035: }
1.347     albertel 10036: 
                   10037: {
1.610     albertel 10038:     my @row_count;
1.961     onken    10039: 
                   10040:     sub start_data_table_count {
                   10041:         unshift(@row_count, 0);
                   10042:         return;
                   10043:     }
                   10044: 
                   10045:     sub end_data_table_count {
                   10046:         shift(@row_count);
                   10047:         return;
                   10048:     }
                   10049: 
1.347     albertel 10050:     sub start_data_table {
1.1018    raeburn  10051: 	my ($add_class,$id) = @_;
1.422     albertel 10052: 	my $css_class = (join(' ','LC_data_table',$add_class));
1.1018    raeburn  10053:         my $table_id;
                   10054:         if (defined($id)) {
                   10055:             $table_id = ' id="'.$id.'"';
                   10056:         }
1.961     onken    10057: 	&start_data_table_count();
1.1018    raeburn  10058: 	return '<table class="'.$css_class.'"'.$table_id.'>'."\n";
1.347     albertel 10059:     }
                   10060: 
                   10061:     sub end_data_table {
1.961     onken    10062: 	&end_data_table_count();
1.389     albertel 10063: 	return '</table>'."\n";;
1.347     albertel 10064:     }
                   10065: 
                   10066:     sub start_data_table_row {
1.974     wenzelju 10067: 	my ($add_class, $id) = @_;
1.610     albertel 10068: 	$row_count[0]++;
                   10069: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900     bisitz   10070: 	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
1.974     wenzelju 10071:         $id = (' id="'.$id.'"') unless ($id eq '');
                   10072:         return  '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.347     albertel 10073:     }
1.471     banghart 10074:     
                   10075:     sub continue_data_table_row {
1.974     wenzelju 10076: 	my ($add_class, $id) = @_;
1.610     albertel 10077: 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.974     wenzelju 10078: 	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
                   10079:         $id = (' id="'.$id.'"') unless ($id eq '');
                   10080:         return  '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.471     banghart 10081:     }
1.347     albertel 10082: 
                   10083:     sub end_data_table_row {
1.389     albertel 10084: 	return '</tr>'."\n";;
1.347     albertel 10085:     }
1.367     www      10086: 
1.421     albertel 10087:     sub start_data_table_empty_row {
1.707     bisitz   10088: #	$row_count[0]++;
1.421     albertel 10089: 	return  '<tr class="LC_empty_row" >'."\n";;
                   10090:     }
                   10091: 
                   10092:     sub end_data_table_empty_row {
                   10093: 	return '</tr>'."\n";;
                   10094:     }
                   10095: 
1.367     www      10096:     sub start_data_table_header_row {
1.389     albertel 10097: 	return  '<tr class="LC_header_row">'."\n";;
1.367     www      10098:     }
                   10099: 
                   10100:     sub end_data_table_header_row {
1.389     albertel 10101: 	return '</tr>'."\n";;
1.367     www      10102:     }
1.890     droeschl 10103: 
                   10104:     sub data_table_caption {
                   10105:         my $caption = shift;
                   10106:         return "<caption class=\"LC_caption\">$caption</caption>";
                   10107:     }
1.347     albertel 10108: }
                   10109: 
1.548     albertel 10110: =pod
                   10111: 
                   10112: =item * &inhibit_menu_check($arg)
                   10113: 
                   10114: Checks for a inhibitmenu state and generates output to preserve it
                   10115: 
                   10116: Inputs:         $arg - can be any of
                   10117:                      - undef - in which case the return value is a string 
                   10118:                                to add  into arguments list of a uri
                   10119:                      - 'input' - in which case the return value is a HTML
                   10120:                                  <form> <input> field of type hidden to
                   10121:                                  preserve the value
                   10122:                      - a url - in which case the return value is the url with
                   10123:                                the neccesary cgi args added to preserve the
                   10124:                                inhibitmenu state
                   10125:                      - a ref to a url - no return value, but the string is
                   10126:                                         updated to include the neccessary cgi
                   10127:                                         args to preserve the inhibitmenu state
                   10128: 
                   10129: =cut
                   10130: 
                   10131: sub inhibit_menu_check {
                   10132:     my ($arg) = @_;
                   10133:     &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
                   10134:     if ($arg eq 'input') {
                   10135: 	if ($env{'form.inhibitmenu'}) {
                   10136: 	    return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
                   10137: 	} else {
                   10138: 	    return
                   10139: 	}
                   10140:     }
                   10141:     if ($env{'form.inhibitmenu'}) {
                   10142: 	if (ref($arg)) {
                   10143: 	    $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   10144: 	} elsif ($arg eq '') {
                   10145: 	    $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
                   10146: 	} else {
                   10147: 	    $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
                   10148: 	}
                   10149:     }
                   10150:     if (!ref($arg)) {
                   10151: 	return $arg;
                   10152:     }
                   10153: }
                   10154: 
1.251     albertel 10155: ###############################################
1.182     matthew  10156: 
                   10157: =pod
                   10158: 
1.549     albertel 10159: =back
                   10160: 
                   10161: =head1 User Information Routines
                   10162: 
                   10163: =over 4
                   10164: 
1.405     albertel 10165: =item * &get_users_function()
1.182     matthew  10166: 
                   10167: Used by &bodytag to determine the current users primary role.
                   10168: Returns either 'student','coordinator','admin', or 'author'.
                   10169: 
                   10170: =cut
                   10171: 
                   10172: ###############################################
                   10173: sub get_users_function {
1.815     tempelho 10174:     my $function = 'norole';
1.818     tempelho 10175:     if ($env{'request.role'}=~/^(st)/) {
                   10176:         $function='student';
                   10177:     }
1.907     raeburn  10178:     if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/) {
1.182     matthew  10179:         $function='coordinator';
                   10180:     }
1.258     albertel 10181:     if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182     matthew  10182:         $function='admin';
                   10183:     }
1.826     bisitz   10184:     if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.1025    raeburn  10185:         ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
1.182     matthew  10186:         $function='author';
                   10187:     }
                   10188:     return $function;
1.54      www      10189: }
1.99      www      10190: 
                   10191: ###############################################
                   10192: 
1.233     raeburn  10193: =pod
                   10194: 
1.821     raeburn  10195: =item * &show_course()
                   10196: 
                   10197: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
                   10198: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
                   10199: 
                   10200: Inputs:
                   10201: None
                   10202: 
                   10203: Outputs:
                   10204: Scalar: 1 if 'Course' to be used, 0 otherwise.
                   10205: 
                   10206: =cut
                   10207: 
                   10208: ###############################################
                   10209: sub show_course {
                   10210:     my $course = !$env{'user.adv'};
                   10211:     if (!$env{'user.adv'}) {
                   10212:         foreach my $env (keys(%env)) {
                   10213:             next if ($env !~ m/^user\.priv\./);
                   10214:             if ($env !~ m/^user\.priv\.(?:st|cm)/) {
                   10215:                 $course = 0;
                   10216:                 last;
                   10217:             }
                   10218:         }
                   10219:     }
                   10220:     return $course;
                   10221: }
                   10222: 
                   10223: ###############################################
                   10224: 
                   10225: =pod
                   10226: 
1.542     raeburn  10227: =item * &check_user_status()
1.274     raeburn  10228: 
                   10229: Determines current status of supplied role for a
                   10230: specific user. Roles can be active, previous or future.
                   10231: 
                   10232: Inputs: 
                   10233: user's domain, user's username, course's domain,
1.375     raeburn  10234: course's number, optional section ID.
1.274     raeburn  10235: 
                   10236: Outputs:
                   10237: role status: active, previous or future. 
                   10238: 
                   10239: =cut
                   10240: 
                   10241: sub check_user_status {
1.412     raeburn  10242:     my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.1073    raeburn  10243:     my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
1.1202    raeburn  10244:     my @uroles = keys(%userinfo);
1.274     raeburn  10245:     my $srchstr;
                   10246:     my $active_chk = 'none';
1.412     raeburn  10247:     my $now = time;
1.274     raeburn  10248:     if (@uroles > 0) {
1.908     raeburn  10249:         if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
1.274     raeburn  10250:             $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
                   10251:         } else {
1.412     raeburn  10252:             $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
                   10253:         }
                   10254:         if (grep/^\Q$srchstr\E$/,@uroles) {
1.274     raeburn  10255:             my $role_end = 0;
                   10256:             my $role_start = 0;
                   10257:             $active_chk = 'active';
1.412     raeburn  10258:             if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
                   10259:                 $role_end = $1;
                   10260:                 if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
                   10261:                     $role_start = $1;
1.274     raeburn  10262:                 }
                   10263:             }
                   10264:             if ($role_start > 0) {
1.412     raeburn  10265:                 if ($now < $role_start) {
1.274     raeburn  10266:                     $active_chk = 'future';
                   10267:                 }
                   10268:             }
                   10269:             if ($role_end > 0) {
1.412     raeburn  10270:                 if ($now > $role_end) {
1.274     raeburn  10271:                     $active_chk = 'previous';
                   10272:                 }
                   10273:             }
                   10274:         }
                   10275:     }
                   10276:     return $active_chk;
                   10277: }
                   10278: 
                   10279: ###############################################
                   10280: 
                   10281: =pod
                   10282: 
1.405     albertel 10283: =item * &get_sections()
1.233     raeburn  10284: 
                   10285: Determines all the sections for a course including
                   10286: sections with students and sections containing other roles.
1.419     raeburn  10287: Incoming parameters: 
                   10288: 
                   10289: 1. domain
                   10290: 2. course number 
                   10291: 3. reference to array containing roles for which sections should 
                   10292: be gathered (optional).
                   10293: 4. reference to array containing status types for which sections 
                   10294: should be gathered (optional).
                   10295: 
                   10296: If the third argument is undefined, sections are gathered for any role. 
                   10297: If the fourth argument is undefined, sections are gathered for any status.
                   10298: Permissible values are 'active' or 'future' or 'previous'.
1.233     raeburn  10299:  
1.374     raeburn  10300: Returns section hash (keys are section IDs, values are
                   10301: number of users in each section), subject to the
1.419     raeburn  10302: optional roles filter, optional status filter 
1.233     raeburn  10303: 
                   10304: =cut
                   10305: 
                   10306: ###############################################
                   10307: sub get_sections {
1.419     raeburn  10308:     my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366     albertel 10309:     if (!defined($cdom) || !defined($cnum)) {
                   10310:         my $cid =  $env{'request.course.id'};
                   10311: 
                   10312: 	return if (!defined($cid));
                   10313: 
                   10314:         $cdom = $env{'course.'.$cid.'.domain'};
                   10315:         $cnum = $env{'course.'.$cid.'.num'};
                   10316:     }
                   10317: 
                   10318:     my %sectioncount;
1.419     raeburn  10319:     my $now = time;
1.240     albertel 10320: 
1.1118    raeburn  10321:     my $check_students = 1;
                   10322:     my $only_students = 0;
                   10323:     if (ref($possible_roles) eq 'ARRAY') {
                   10324:         if (grep(/^st$/,@{$possible_roles})) {
                   10325:             if (@{$possible_roles} == 1) {
                   10326:                 $only_students = 1;
                   10327:             }
                   10328:         } else {
                   10329:             $check_students = 0;
                   10330:         }
                   10331:     }
                   10332: 
                   10333:     if ($check_students) { 
1.276     albertel 10334: 	my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240     albertel 10335: 	my $sec_index = &Apache::loncoursedata::CL_SECTION();
                   10336: 	my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419     raeburn  10337:         my $start_index = &Apache::loncoursedata::CL_START();
                   10338:         my $end_index = &Apache::loncoursedata::CL_END();
                   10339:         my $status;
1.366     albertel 10340: 	while (my ($student,$data) = each(%$classlist)) {
1.419     raeburn  10341: 	    my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
                   10342: 				                     $data->[$status_index],
                   10343:                                                      $data->[$start_index],
                   10344:                                                      $data->[$end_index]);
                   10345:             if ($stu_status eq 'Active') {
                   10346:                 $status = 'active';
                   10347:             } elsif ($end < $now) {
                   10348:                 $status = 'previous';
                   10349:             } elsif ($start > $now) {
                   10350:                 $status = 'future';
                   10351:             } 
                   10352: 	    if ($section ne '-1' && $section !~ /^\s*$/) {
                   10353:                 if ((!defined($possible_status)) || (($status ne '') && 
                   10354:                     (grep/^\Q$status\E$/,@{$possible_status}))) { 
                   10355: 		    $sectioncount{$section}++;
                   10356:                 }
1.240     albertel 10357: 	    }
                   10358: 	}
                   10359:     }
1.1118    raeburn  10360:     if ($only_students) {
                   10361:         return %sectioncount;
                   10362:     }
1.240     albertel 10363:     my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   10364:     foreach my $user (sort(keys(%courseroles))) {
                   10365: 	if ($user !~ /^(\w{2})/) { next; }
                   10366: 	my ($role) = ($user =~ /^(\w{2})/);
                   10367: 	if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419     raeburn  10368: 	my ($section,$status);
1.240     albertel 10369: 	if ($role eq 'cr' &&
                   10370: 	    $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
                   10371: 	    $section=$1;
                   10372: 	}
                   10373: 	if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
                   10374: 	if (!defined($section) || $section eq '-1') { next; }
1.419     raeburn  10375:         my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
                   10376:         if ($end == -1 && $start == -1) {
                   10377:             next; #deleted role
                   10378:         }
                   10379:         if (!defined($possible_status)) { 
                   10380:             $sectioncount{$section}++;
                   10381:         } else {
                   10382:             if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
                   10383:                 $status = 'active';
                   10384:             } elsif ($end < $now) {
                   10385:                 $status = 'future';
                   10386:             } elsif ($start > $now) {
                   10387:                 $status = 'previous';
                   10388:             }
                   10389:             if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
                   10390:                 $sectioncount{$section}++;
                   10391:             }
                   10392:         }
1.233     raeburn  10393:     }
1.366     albertel 10394:     return %sectioncount;
1.233     raeburn  10395: }
                   10396: 
1.274     raeburn  10397: ###############################################
1.294     raeburn  10398: 
                   10399: =pod
1.405     albertel 10400: 
                   10401: =item * &get_course_users()
                   10402: 
1.275     raeburn  10403: Retrieves usernames:domains for users in the specified course
                   10404: with specific role(s), and access status. 
                   10405: 
                   10406: Incoming parameters:
1.277     albertel 10407: 1. course domain
                   10408: 2. course number
                   10409: 3. access status: users must have - either active, 
1.275     raeburn  10410: previous, future, or all.
1.277     albertel 10411: 4. reference to array of permissible roles
1.288     raeburn  10412: 5. reference to array of section restrictions (optional)
                   10413: 6. reference to results object (hash of hashes).
                   10414: 7. reference to optional userdata hash
1.609     raeburn  10415: 8. reference to optional statushash
1.630     raeburn  10416: 9. flag if privileged users (except those set to unhide in
                   10417:    course settings) should be excluded    
1.609     raeburn  10418: Keys of top level results hash are roles.
1.275     raeburn  10419: Keys of inner hashes are username:domain, with 
                   10420: values set to access type.
1.288     raeburn  10421: Optional userdata hash returns an array with arguments in the 
                   10422: same order as loncoursedata::get_classlist() for student data.
                   10423: 
1.609     raeburn  10424: Optional statushash returns
                   10425: 
1.288     raeburn  10426: Entries for end, start, section and status are blank because
                   10427: of the possibility of multiple values for non-student roles.
                   10428: 
1.275     raeburn  10429: =cut
1.405     albertel 10430: 
1.275     raeburn  10431: ###############################################
1.405     albertel 10432: 
1.275     raeburn  10433: sub get_course_users {
1.630     raeburn  10434:     my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288     raeburn  10435:     my %idx = ();
1.419     raeburn  10436:     my %seclists;
1.288     raeburn  10437: 
                   10438:     $idx{udom} = &Apache::loncoursedata::CL_SDOM();
                   10439:     $idx{uname} =  &Apache::loncoursedata::CL_SNAME();
                   10440:     $idx{end} = &Apache::loncoursedata::CL_END();
                   10441:     $idx{start} = &Apache::loncoursedata::CL_START();
                   10442:     $idx{id} = &Apache::loncoursedata::CL_ID();
                   10443:     $idx{section} = &Apache::loncoursedata::CL_SECTION();
                   10444:     $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
                   10445:     $idx{status} = &Apache::loncoursedata::CL_STATUS();
                   10446: 
1.290     albertel 10447:     if (grep(/^st$/,@{$roles})) {
1.276     albertel 10448:         my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278     raeburn  10449:         my $now = time;
1.277     albertel 10450:         foreach my $student (keys(%{$classlist})) {
1.288     raeburn  10451:             my $match = 0;
1.412     raeburn  10452:             my $secmatch = 0;
1.419     raeburn  10453:             my $section = $$classlist{$student}[$idx{section}];
1.609     raeburn  10454:             my $status = $$classlist{$student}[$idx{status}];
1.419     raeburn  10455:             if ($section eq '') {
                   10456:                 $section = 'none';
                   10457:             }
1.291     albertel 10458:             if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 10459:                 if (grep(/^all$/,@{$sections})) {
1.412     raeburn  10460:                     $secmatch = 1;
                   10461:                 } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420     albertel 10462:                     if (grep(/^none$/,@{$sections})) {
1.412     raeburn  10463:                         $secmatch = 1;
                   10464:                     }
                   10465:                 } else {  
1.419     raeburn  10466: 		    if (grep(/^\Q$section\E$/,@{$sections})) {
1.412     raeburn  10467: 		        $secmatch = 1;
                   10468:                     }
1.290     albertel 10469: 		}
1.412     raeburn  10470:                 if (!$secmatch) {
                   10471:                     next;
                   10472:                 }
1.419     raeburn  10473:             }
1.275     raeburn  10474:             if (defined($$types{'active'})) {
1.288     raeburn  10475:                 if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275     raeburn  10476:                     push(@{$$users{st}{$student}},'active');
1.288     raeburn  10477:                     $match = 1;
1.275     raeburn  10478:                 }
                   10479:             }
                   10480:             if (defined($$types{'previous'})) {
1.609     raeburn  10481:                 if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275     raeburn  10482:                     push(@{$$users{st}{$student}},'previous');
1.288     raeburn  10483:                     $match = 1;
1.275     raeburn  10484:                 }
                   10485:             }
                   10486:             if (defined($$types{'future'})) {
1.609     raeburn  10487:                 if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275     raeburn  10488:                     push(@{$$users{st}{$student}},'future');
1.288     raeburn  10489:                     $match = 1;
1.275     raeburn  10490:                 }
                   10491:             }
1.609     raeburn  10492:             if ($match) {
                   10493:                 push(@{$seclists{$student}},$section);
                   10494:                 if (ref($userdata) eq 'HASH') {
                   10495:                     $$userdata{$student} = $$classlist{$student};
                   10496:                 }
                   10497:                 if (ref($statushash) eq 'HASH') {
                   10498:                     $statushash->{$student}{'st'}{$section} = $status;
                   10499:                 }
1.288     raeburn  10500:             }
1.275     raeburn  10501:         }
                   10502:     }
1.412     raeburn  10503:     if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439     raeburn  10504:         my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   10505:         my $now = time;
1.609     raeburn  10506:         my %displaystatus = ( previous => 'Expired',
                   10507:                               active   => 'Active',
                   10508:                               future   => 'Future',
                   10509:                             );
1.1121    raeburn  10510:         my (%nothide,@possdoms);
1.630     raeburn  10511:         if ($hidepriv) {
                   10512:             my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
                   10513:             foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
                   10514:                 if ($user !~ /:/) {
                   10515:                     $nothide{join(':',split(/[\@]/,$user))}=1;
                   10516:                 } else {
                   10517:                     $nothide{$user} = 1;
                   10518:                 }
                   10519:             }
1.1121    raeburn  10520:             my @possdoms = ($cdom);
                   10521:             if ($coursehash{'checkforpriv'}) {
                   10522:                 push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
                   10523:             }
1.630     raeburn  10524:         }
1.439     raeburn  10525:         foreach my $person (sort(keys(%coursepersonnel))) {
1.288     raeburn  10526:             my $match = 0;
1.412     raeburn  10527:             my $secmatch = 0;
1.439     raeburn  10528:             my $status;
1.412     raeburn  10529:             my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275     raeburn  10530:             $user =~ s/:$//;
1.439     raeburn  10531:             my ($end,$start) = split(/:/,$coursepersonnel{$person});
                   10532:             if ($end == -1 || $start == -1) {
                   10533:                 next;
                   10534:             }
                   10535:             if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
                   10536:                 (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412     raeburn  10537:                 my ($uname,$udom) = split(/:/,$user);
                   10538:                 if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420     albertel 10539:                     if (grep(/^all$/,@{$sections})) {
1.412     raeburn  10540:                         $secmatch = 1;
                   10541:                     } elsif ($usec eq '') {
1.420     albertel 10542:                         if (grep(/^none$/,@{$sections})) {
1.412     raeburn  10543:                             $secmatch = 1;
                   10544:                         }
                   10545:                     } else {
                   10546:                         if (grep(/^\Q$usec\E$/,@{$sections})) {
                   10547:                             $secmatch = 1;
                   10548:                         }
                   10549:                     }
                   10550:                     if (!$secmatch) {
                   10551:                         next;
                   10552:                     }
1.288     raeburn  10553:                 }
1.419     raeburn  10554:                 if ($usec eq '') {
                   10555:                     $usec = 'none';
                   10556:                 }
1.275     raeburn  10557:                 if ($uname ne '' && $udom ne '') {
1.630     raeburn  10558:                     if ($hidepriv) {
1.1121    raeburn  10559:                         if ((&Apache::lonnet::privileged($uname,$udom,\@possdoms)) &&
1.630     raeburn  10560:                             (!$nothide{$uname.':'.$udom})) {
                   10561:                             next;
                   10562:                         }
                   10563:                     }
1.503     raeburn  10564:                     if ($end > 0 && $end < $now) {
1.439     raeburn  10565:                         $status = 'previous';
                   10566:                     } elsif ($start > $now) {
                   10567:                         $status = 'future';
                   10568:                     } else {
                   10569:                         $status = 'active';
                   10570:                     }
1.277     albertel 10571:                     foreach my $type (keys(%{$types})) { 
1.275     raeburn  10572:                         if ($status eq $type) {
1.420     albertel 10573:                             if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419     raeburn  10574:                                 push(@{$$users{$role}{$user}},$type);
                   10575:                             }
1.288     raeburn  10576:                             $match = 1;
                   10577:                         }
                   10578:                     }
1.419     raeburn  10579:                     if (($match) && (ref($userdata) eq 'HASH')) {
                   10580:                         if (!exists($$userdata{$uname.':'.$udom})) {
                   10581: 			    &get_user_info($udom,$uname,\%idx,$userdata);
                   10582:                         }
1.420     albertel 10583:                         if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419     raeburn  10584:                             push(@{$seclists{$uname.':'.$udom}},$usec);
                   10585:                         }
1.609     raeburn  10586:                         if (ref($statushash) eq 'HASH') {
                   10587:                             $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
                   10588:                         }
1.275     raeburn  10589:                     }
                   10590:                 }
                   10591:             }
                   10592:         }
1.290     albertel 10593:         if (grep(/^ow$/,@{$roles})) {
1.279     raeburn  10594:             if ((defined($cdom)) && (defined($cnum))) {
                   10595:                 my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
                   10596:                 if ( defined($csettings{'internal.courseowner'}) ) {
                   10597:                     my $owner = $csettings{'internal.courseowner'};
1.609     raeburn  10598:                     next if ($owner eq '');
                   10599:                     my ($ownername,$ownerdom);
                   10600:                     if ($owner =~ /^([^:]+):([^:]+)$/) {
                   10601:                         $ownername = $1;
                   10602:                         $ownerdom = $2;
                   10603:                     } else {
                   10604:                         $ownername = $owner;
                   10605:                         $ownerdom = $cdom;
                   10606:                         $owner = $ownername.':'.$ownerdom;
1.439     raeburn  10607:                     }
                   10608:                     @{$$users{'ow'}{$owner}} = 'any';
1.290     albertel 10609:                     if (defined($userdata) && 
1.609     raeburn  10610: 			!exists($$userdata{$owner})) {
                   10611: 			&get_user_info($ownerdom,$ownername,\%idx,$userdata);
                   10612:                         if (!grep(/^none$/,@{$seclists{$owner}})) {
                   10613:                             push(@{$seclists{$owner}},'none');
                   10614:                         }
                   10615:                         if (ref($statushash) eq 'HASH') {
                   10616:                             $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419     raeburn  10617:                         }
1.290     albertel 10618: 		    }
1.279     raeburn  10619:                 }
                   10620:             }
                   10621:         }
1.419     raeburn  10622:         foreach my $user (keys(%seclists)) {
                   10623:             @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
                   10624:             $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
                   10625:         }
1.275     raeburn  10626:     }
                   10627:     return;
                   10628: }
                   10629: 
1.288     raeburn  10630: sub get_user_info {
                   10631:     my ($udom,$uname,$idx,$userdata) = @_;
1.289     albertel 10632:     $$userdata{$uname.':'.$udom}[$$idx{fullname}] = 
                   10633: 	&plainname($uname,$udom,'lastname');
1.291     albertel 10634:     $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297     raeburn  10635:     $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609     raeburn  10636:     my %idhash =  &Apache::lonnet::idrget($udom,($uname));
                   10637:     $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname}; 
1.288     raeburn  10638:     return;
                   10639: }
1.275     raeburn  10640: 
1.472     raeburn  10641: ###############################################
                   10642: 
                   10643: =pod
                   10644: 
                   10645: =item * &get_user_quota()
                   10646: 
1.1134    raeburn  10647: Retrieves quota assigned for storage of user files.
                   10648: Default is to report quota for portfolio files.
1.472     raeburn  10649: 
                   10650: Incoming parameters:
                   10651: 1. user's username
                   10652: 2. user's domain
1.1134    raeburn  10653: 3. quota name - portfolio, author, or course
1.1136    raeburn  10654:    (if no quota name provided, defaults to portfolio).
1.1237    raeburn  10655: 4. crstype - official, unofficial, textbook, placement or community, 
                   10656:    if quota name is course
1.472     raeburn  10657: 
                   10658: Returns:
1.1163    raeburn  10659: 1. Disk quota (in MB) assigned to student.
1.536     raeburn  10660: 2. (Optional) Type of setting: custom or default
                   10661:    (individually assigned or default for user's 
                   10662:    institutional status).
                   10663: 3. (Optional) - User's institutional status (e.g., faculty, staff
                   10664:    or student - types as defined in localenroll::inst_usertypes 
                   10665:    for user's domain, which determines default quota for user.
                   10666: 4. (Optional) - Default quota which would apply to the user.
1.472     raeburn  10667: 
                   10668: If a value has been stored in the user's environment, 
1.536     raeburn  10669: it will return that, otherwise it returns the maximal default
1.1134    raeburn  10670: defined for the user's institutional status(es) in the domain.
1.472     raeburn  10671: 
                   10672: =cut
                   10673: 
                   10674: ###############################################
                   10675: 
                   10676: 
                   10677: sub get_user_quota {
1.1136    raeburn  10678:     my ($uname,$udom,$quotaname,$crstype) = @_;
1.536     raeburn  10679:     my ($quota,$quotatype,$settingstatus,$defquota);
1.472     raeburn  10680:     if (!defined($udom)) {
                   10681:         $udom = $env{'user.domain'};
                   10682:     }
                   10683:     if (!defined($uname)) {
                   10684:         $uname = $env{'user.name'};
                   10685:     }
                   10686:     if (($udom eq '' || $uname eq '') ||
                   10687:         ($udom eq 'public') && ($uname eq 'public')) {
                   10688:         $quota = 0;
1.536     raeburn  10689:         $quotatype = 'default';
                   10690:         $defquota = 0; 
1.472     raeburn  10691:     } else {
1.536     raeburn  10692:         my $inststatus;
1.1134    raeburn  10693:         if ($quotaname eq 'course') {
                   10694:             if (($env{'course.'.$udom.'_'.$uname.'.num'} eq $uname) &&
                   10695:                 ($env{'course.'.$udom.'_'.$uname.'.domain'} eq $udom)) {
                   10696:                 $quota = $env{'course.'.$udom.'_'.$uname.'.internal.uploadquota'};
                   10697:             } else {
                   10698:                 my %cenv = &Apache::lonnet::coursedescription("$udom/$uname");
                   10699:                 $quota = $cenv{'internal.uploadquota'};
                   10700:             }
1.536     raeburn  10701:         } else {
1.1134    raeburn  10702:             if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
                   10703:                 if ($quotaname eq 'author') {
                   10704:                     $quota = $env{'environment.authorquota'};
                   10705:                 } else {
                   10706:                     $quota = $env{'environment.portfolioquota'};
                   10707:                 }
                   10708:                 $inststatus = $env{'environment.inststatus'};
                   10709:             } else {
                   10710:                 my %userenv = 
                   10711:                     &Apache::lonnet::get('environment',['portfolioquota',
                   10712:                                          'authorquota','inststatus'],$udom,$uname);
                   10713:                 my ($tmp) = keys(%userenv);
                   10714:                 if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
                   10715:                     if ($quotaname eq 'author') {
                   10716:                         $quota = $userenv{'authorquota'};
                   10717:                     } else {
                   10718:                         $quota = $userenv{'portfolioquota'};
                   10719:                     }
                   10720:                     $inststatus = $userenv{'inststatus'};
                   10721:                 } else {
                   10722:                     undef(%userenv);
                   10723:                 }
                   10724:             }
                   10725:         }
                   10726:         if ($quota eq '' || wantarray) {
                   10727:             if ($quotaname eq 'course') {
                   10728:                 my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
1.1165    raeburn  10729:                 if (($crstype eq 'official') || ($crstype eq 'unofficial') || 
1.1237    raeburn  10730:                     ($crstype eq 'community') || ($crstype eq 'textbook') ||
                   10731:                     ($crstype eq 'placement')) { 
1.1136    raeburn  10732:                     $defquota = $domdefs{$crstype.'quota'};
                   10733:                 }
                   10734:                 if ($defquota eq '') {
                   10735:                     $defquota = 500;
                   10736:                 }
1.1134    raeburn  10737:             } else {
                   10738:                 ($defquota,$settingstatus) = &default_quota($udom,$inststatus,$quotaname);
                   10739:             }
                   10740:             if ($quota eq '') {
                   10741:                 $quota = $defquota;
                   10742:                 $quotatype = 'default';
                   10743:             } else {
                   10744:                 $quotatype = 'custom';
                   10745:             }
1.472     raeburn  10746:         }
                   10747:     }
1.536     raeburn  10748:     if (wantarray) {
                   10749:         return ($quota,$quotatype,$settingstatus,$defquota);
                   10750:     } else {
                   10751:         return $quota;
                   10752:     }
1.472     raeburn  10753: }
                   10754: 
                   10755: ###############################################
                   10756: 
                   10757: =pod
                   10758: 
                   10759: =item * &default_quota()
                   10760: 
1.536     raeburn  10761: Retrieves default quota assigned for storage of user portfolio files,
                   10762: given an (optional) user's institutional status.
1.472     raeburn  10763: 
                   10764: Incoming parameters:
1.1142    raeburn  10765: 
1.472     raeburn  10766: 1. domain
1.536     raeburn  10767: 2. (Optional) institutional status(es).  This is a : separated list of 
                   10768:    status types (e.g., faculty, staff, student etc.)
                   10769:    which apply to the user for whom the default is being retrieved.
                   10770:    If the institutional status string in undefined, the domain
1.1134    raeburn  10771:    default quota will be returned.
                   10772: 3.  quota name - portfolio, author, or course
                   10773:    (if no quota name provided, defaults to portfolio).
1.472     raeburn  10774: 
                   10775: Returns:
1.1142    raeburn  10776: 
1.1163    raeburn  10777: 1. Default disk quota (in MB) for user portfolios in the domain.
1.536     raeburn  10778: 2. (Optional) institutional type which determined the value of the
                   10779:    default quota.
1.472     raeburn  10780: 
                   10781: If a value has been stored in the domain's configuration db,
                   10782: it will return that, otherwise it returns 20 (for backwards 
                   10783: compatibility with domains which have not set up a configuration
1.1163    raeburn  10784: db file; the original statically defined portfolio quota was 20 MB). 
1.472     raeburn  10785: 
1.536     raeburn  10786: If the user's status includes multiple types (e.g., staff and student),
                   10787: the largest default quota which applies to the user determines the
                   10788: default quota returned.
                   10789: 
1.472     raeburn  10790: =cut
                   10791: 
                   10792: ###############################################
                   10793: 
                   10794: 
                   10795: sub default_quota {
1.1134    raeburn  10796:     my ($udom,$inststatus,$quotaname) = @_;
1.536     raeburn  10797:     my ($defquota,$settingstatus);
                   10798:     my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622     raeburn  10799:                                             ['quotas'],$udom);
1.1134    raeburn  10800:     my $key = 'defaultquota';
                   10801:     if ($quotaname eq 'author') {
                   10802:         $key = 'authorquota';
                   10803:     }
1.622     raeburn  10804:     if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536     raeburn  10805:         if ($inststatus ne '') {
1.765     raeburn  10806:             my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536     raeburn  10807:             foreach my $item (@statuses) {
1.1134    raeburn  10808:                 if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
                   10809:                     if ($quotahash{'quotas'}{$key}{$item} ne '') {
1.711     raeburn  10810:                         if ($defquota eq '') {
1.1134    raeburn  10811:                             $defquota = $quotahash{'quotas'}{$key}{$item};
1.711     raeburn  10812:                             $settingstatus = $item;
1.1134    raeburn  10813:                         } elsif ($quotahash{'quotas'}{$key}{$item} > $defquota) {
                   10814:                             $defquota = $quotahash{'quotas'}{$key}{$item};
1.711     raeburn  10815:                             $settingstatus = $item;
                   10816:                         }
                   10817:                     }
1.1134    raeburn  10818:                 } elsif ($key eq 'defaultquota') {
1.711     raeburn  10819:                     if ($quotahash{'quotas'}{$item} ne '') {
                   10820:                         if ($defquota eq '') {
                   10821:                             $defquota = $quotahash{'quotas'}{$item};
                   10822:                             $settingstatus = $item;
                   10823:                         } elsif ($quotahash{'quotas'}{$item} > $defquota) {
                   10824:                             $defquota = $quotahash{'quotas'}{$item};
                   10825:                             $settingstatus = $item;
                   10826:                         }
1.536     raeburn  10827:                     }
                   10828:                 }
                   10829:             }
                   10830:         }
                   10831:         if ($defquota eq '') {
1.1134    raeburn  10832:             if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
                   10833:                 $defquota = $quotahash{'quotas'}{$key}{'default'};
                   10834:             } elsif ($key eq 'defaultquota') {
1.711     raeburn  10835:                 $defquota = $quotahash{'quotas'}{'default'};
                   10836:             }
1.536     raeburn  10837:             $settingstatus = 'default';
1.1139    raeburn  10838:             if ($defquota eq '') {
                   10839:                 if ($quotaname eq 'author') {
                   10840:                     $defquota = 500;
                   10841:                 }
                   10842:             }
1.536     raeburn  10843:         }
                   10844:     } else {
                   10845:         $settingstatus = 'default';
1.1134    raeburn  10846:         if ($quotaname eq 'author') {
                   10847:             $defquota = 500;
                   10848:         } else {
                   10849:             $defquota = 20;
                   10850:         }
1.536     raeburn  10851:     }
                   10852:     if (wantarray) {
                   10853:         return ($defquota,$settingstatus);
1.472     raeburn  10854:     } else {
1.536     raeburn  10855:         return $defquota;
1.472     raeburn  10856:     }
                   10857: }
                   10858: 
1.1135    raeburn  10859: ###############################################
                   10860: 
                   10861: =pod
                   10862: 
1.1136    raeburn  10863: =item * &excess_filesize_warning()
1.1135    raeburn  10864: 
                   10865: Returns warning message if upload of file to authoring space, or copying
1.1136    raeburn  10866: of existing file within authoring space will cause quota for the authoring
1.1146    raeburn  10867: space to be exceeded.
1.1136    raeburn  10868: 
                   10869: Same, if upload of a file directly to a course/community via Course Editor
1.1137    raeburn  10870: will cause quota for uploaded content for the course to be exceeded.
1.1135    raeburn  10871: 
1.1165    raeburn  10872: Inputs: 7 
1.1136    raeburn  10873: 1. username or coursenum
1.1135    raeburn  10874: 2. domain
1.1136    raeburn  10875: 3. context ('author' or 'course')
1.1135    raeburn  10876: 4. filename of file for which action is being requested
                   10877: 5. filesize (kB) of file
                   10878: 6. action being taken: copy or upload.
1.1237    raeburn  10879: 7. quotatype (in course context -- official, unofficial, textbook, placement or community).
1.1135    raeburn  10880: 
                   10881: Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
1.1142    raeburn  10882:          otherwise return null.
                   10883: 
                   10884: =back
1.1135    raeburn  10885: 
                   10886: =cut
                   10887: 
1.1136    raeburn  10888: sub excess_filesize_warning {
1.1165    raeburn  10889:     my ($uname,$udom,$context,$filename,$filesize,$action,$quotatype) = @_;
1.1136    raeburn  10890:     my $current_disk_usage = 0;
1.1165    raeburn  10891:     my $disk_quota = &get_user_quota($uname,$udom,$context,$quotatype); #expressed in MB
1.1136    raeburn  10892:     if ($context eq 'author') {
                   10893:         my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname";
                   10894:         $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace);
                   10895:     } else {
                   10896:         foreach my $subdir ('docs','supplemental') {
                   10897:             $current_disk_usage += &Apache::lonnet::diskusage($udom,$uname,"userfiles/$subdir",1);
                   10898:         }
                   10899:     }
1.1135    raeburn  10900:     $disk_quota = int($disk_quota * 1000);
                   10901:     if (($current_disk_usage + $filesize) > $disk_quota) {
1.1179    bisitz   10902:         return '<p class="LC_warning">'.
1.1135    raeburn  10903:                 &mt("Unable to $action [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.",
1.1179    bisitz   10904:                     '<span class="LC_filename">'.$filename.'</span>',$filesize).'</p>'.
                   10905:                '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
1.1135    raeburn  10906:                             $disk_quota,$current_disk_usage).
                   10907:                '</p>';
                   10908:     }
                   10909:     return;
                   10910: }
                   10911: 
                   10912: ###############################################
                   10913: 
                   10914: 
1.1136    raeburn  10915: 
                   10916: 
1.384     raeburn  10917: sub get_secgrprole_info {
                   10918:     my ($cdom,$cnum,$needroles,$type)  = @_;
                   10919:     my %sections_count = &get_sections($cdom,$cnum);
                   10920:     my @sections =  (sort {$a <=> $b} keys(%sections_count));
                   10921:     my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
                   10922:     my @groups = sort(keys(%curr_groups));
                   10923:     my $allroles = [];
                   10924:     my $rolehash;
                   10925:     my $accesshash = {
                   10926:                      active => 'Currently has access',
                   10927:                      future => 'Will have future access',
                   10928:                      previous => 'Previously had access',
                   10929:                   };
                   10930:     if ($needroles) {
                   10931:         $rolehash = {'all' => 'all'};
1.385     albertel 10932:         my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
                   10933: 	if (&Apache::lonnet::error(%user_roles)) {
                   10934: 	    undef(%user_roles);
                   10935: 	}
                   10936:         foreach my $item (keys(%user_roles)) {
1.384     raeburn  10937:             my ($role)=split(/\:/,$item,2);
                   10938:             if ($role eq 'cr') { next; }
                   10939:             if ($role =~ /^cr/) {
                   10940:                 $$rolehash{$role} = (split('/',$role))[3];
                   10941:             } else {
                   10942:                 $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
                   10943:             }
                   10944:         }
                   10945:         foreach my $key (sort(keys(%{$rolehash}))) {
                   10946:             push(@{$allroles},$key);
                   10947:         }
                   10948:         push (@{$allroles},'st');
                   10949:         $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
                   10950:     }
                   10951:     return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
                   10952: }
                   10953: 
1.555     raeburn  10954: sub user_picker {
1.1279    raeburn  10955:     my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context,$fixeddom,$noinstd) = @_;
1.555     raeburn  10956:     my $currdom = $dom;
1.1253    raeburn  10957:     my @alldoms = &Apache::lonnet::all_domains();
                   10958:     if (@alldoms == 1) {
                   10959:         my %domsrch = &Apache::lonnet::get_dom('configuration',
                   10960:                                                ['directorysrch'],$alldoms[0]);
                   10961:         my $domdesc = &Apache::lonnet::domain($alldoms[0],'description');
                   10962:         my $showdom = $domdesc;
                   10963:         if ($showdom eq '') {
                   10964:             $showdom = $dom;
                   10965:         }
                   10966:         if (ref($domsrch{'directorysrch'}) eq 'HASH') {
                   10967:             if ((!$domsrch{'directorysrch'}{'available'}) &&
                   10968:                 ($domsrch{'directorysrch'}{'lcavailable'} eq '0')) {
                   10969:                 return (&mt('LON-CAPA directory search is not available in domain: [_1]',$showdom),0);
                   10970:             }
                   10971:         }
                   10972:     }
1.555     raeburn  10973:     my %curr_selected = (
                   10974:                         srchin => 'dom',
1.580     raeburn  10975:                         srchby => 'lastname',
1.555     raeburn  10976:                       );
                   10977:     my $srchterm;
1.625     raeburn  10978:     if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555     raeburn  10979:         if ($srch->{'srchby'} ne '') {
                   10980:             $curr_selected{'srchby'} = $srch->{'srchby'};
                   10981:         }
                   10982:         if ($srch->{'srchin'} ne '') {
                   10983:             $curr_selected{'srchin'} = $srch->{'srchin'};
                   10984:         }
                   10985:         if ($srch->{'srchtype'} ne '') {
                   10986:             $curr_selected{'srchtype'} = $srch->{'srchtype'};
                   10987:         }
                   10988:         if ($srch->{'srchdomain'} ne '') {
                   10989:             $currdom = $srch->{'srchdomain'};
                   10990:         }
                   10991:         $srchterm = $srch->{'srchterm'};
                   10992:     }
1.1222    damieng  10993:     my %html_lt=&Apache::lonlocal::texthash(
1.573     raeburn  10994:                     'usr'       => 'Search criteria',
1.563     raeburn  10995:                     'doma'      => 'Domain/institution to search',
1.558     albertel 10996:                     'uname'     => 'username',
                   10997:                     'lastname'  => 'last name',
1.555     raeburn  10998:                     'lastfirst' => 'last name, first name',
1.558     albertel 10999:                     'crs'       => 'in this course',
1.576     raeburn  11000:                     'dom'       => 'in selected LON-CAPA domain', 
1.558     albertel 11001:                     'alc'       => 'all LON-CAPA',
1.573     raeburn  11002:                     'instd'     => 'in institutional directory for selected domain',
1.558     albertel 11003:                     'exact'     => 'is',
                   11004:                     'contains'  => 'contains',
1.569     raeburn  11005:                     'begins'    => 'begins with',
1.1222    damieng  11006:                                        );
                   11007:     my %js_lt=&Apache::lonlocal::texthash(
1.571     raeburn  11008:                     'youm'      => "You must include some text to search for.",
                   11009:                     'thte'      => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
                   11010:                     'thet'      => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
                   11011:                     'yomc'      => "You must choose a domain when using an institutional directory search.",
                   11012:                     'ymcd'      => "You must choose a domain when using a domain search.",
                   11013:                     'whus'      => "When using searching by last,first you must include a comma as separator between last name and first name.",
                   11014:                     'whse'      => "When searching by last,first you must include at least one character in the first name.",
                   11015:                      'thfo'     => "The following need to be corrected before the search can be run:",
1.555     raeburn  11016:                                        );
1.1222    damieng  11017:     &html_escape(\%html_lt);
                   11018:     &js_escape(\%js_lt);
1.1255    raeburn  11019:     my $domform;
1.1277    raeburn  11020:     my $allow_blank = 1;
1.1255    raeburn  11021:     if ($fixeddom) {
1.1277    raeburn  11022:         $allow_blank = 0;
                   11023:         $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1,undef,[$currdom]);
1.1255    raeburn  11024:     } else {
1.1287    raeburn  11025:         my $defdom = $env{'request.role.domain'};
1.1288    raeburn  11026:         my ($trusted,$untrusted);
1.1287    raeburn  11027:         if (($context eq 'requestcrs') || ($context eq 'course')) {
1.1288    raeburn  11028:             ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('enroll',$defdom);
1.1287    raeburn  11029:         } elsif ($context eq 'author') {
1.1288    raeburn  11030:             ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('othcoau',$defdom);
1.1287    raeburn  11031:         } elsif ($context eq 'domain') {
1.1288    raeburn  11032:             ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('domroles',$defdom);
1.1287    raeburn  11033:         }
1.1288    raeburn  11034:         $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1,undef,$trusted,$untrusted);
1.1255    raeburn  11035:     }
1.563     raeburn  11036:     my $srchinsel = ' <select name="srchin">';
1.555     raeburn  11037: 
                   11038:     my @srchins = ('crs','dom','alc','instd');
                   11039: 
                   11040:     foreach my $option (@srchins) {
                   11041:         # FIXME 'alc' option unavailable until 
                   11042:         #       loncreateuser::print_user_query_page()
                   11043:         #       has been completed.
                   11044:         next if ($option eq 'alc');
1.880     raeburn  11045:         next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));  
1.555     raeburn  11046:         next if ($option eq 'crs' && !$env{'request.course.id'});
1.1279    raeburn  11047:         next if (($option eq 'instd') && ($noinstd));
1.563     raeburn  11048:         if ($curr_selected{'srchin'} eq $option) {
                   11049:             $srchinsel .= ' 
1.1222    damieng  11050:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.563     raeburn  11051:         } else {
                   11052:             $srchinsel .= '
1.1222    damieng  11053:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.563     raeburn  11054:         }
1.555     raeburn  11055:     }
1.563     raeburn  11056:     $srchinsel .= "\n  </select>\n";
1.555     raeburn  11057: 
                   11058:     my $srchbysel =  ' <select name="srchby">';
1.580     raeburn  11059:     foreach my $option ('lastname','lastfirst','uname') {
1.555     raeburn  11060:         if ($curr_selected{'srchby'} eq $option) {
                   11061:             $srchbysel .= '
1.1222    damieng  11062:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555     raeburn  11063:         } else {
                   11064:             $srchbysel .= '
1.1222    damieng  11065:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555     raeburn  11066:          }
                   11067:     }
                   11068:     $srchbysel .= "\n  </select>\n";
                   11069: 
                   11070:     my $srchtypesel = ' <select name="srchtype">';
1.580     raeburn  11071:     foreach my $option ('begins','contains','exact') {
1.555     raeburn  11072:         if ($curr_selected{'srchtype'} eq $option) {
                   11073:             $srchtypesel .= '
1.1222    damieng  11074:    <option value="'.$option.'" selected="selected">'.$html_lt{$option}.'</option>';
1.555     raeburn  11075:         } else {
                   11076:             $srchtypesel .= '
1.1222    damieng  11077:    <option value="'.$option.'">'.$html_lt{$option}.'</option>';
1.555     raeburn  11078:         }
                   11079:     }
                   11080:     $srchtypesel .= "\n  </select>\n";
                   11081: 
1.558     albertel 11082:     my ($newuserscript,$new_user_create);
1.994     raeburn  11083:     my $context_dom = $env{'request.role.domain'};
                   11084:     if ($context eq 'requestcrs') {
                   11085:         if ($env{'form.coursedom'} ne '') { 
                   11086:             $context_dom = $env{'form.coursedom'};
                   11087:         }
                   11088:     }
1.556     raeburn  11089:     if ($forcenewuser) {
1.576     raeburn  11090:         if (ref($srch) eq 'HASH') {
1.994     raeburn  11091:             if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
1.627     raeburn  11092:                 if ($cancreate) {
                   11093:                     $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>';
                   11094:                 } else {
1.799     bisitz   11095:                     my $helplink = 'javascript:helpMenu('."'display'".')';
1.627     raeburn  11096:                     my %usertypetext = (
                   11097:                         official   => 'institutional',
                   11098:                         unofficial => 'non-institutional',
                   11099:                     );
1.799     bisitz   11100:                     $new_user_create = '<p class="LC_warning">'
                   11101:                                       .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
                   11102:                                       .' '
                   11103:                                       .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
                   11104:                                           ,'<a href="'.$helplink.'">','</a>')
                   11105:                                       .'</p><br />';
1.627     raeburn  11106:                 }
1.576     raeburn  11107:             }
                   11108:         }
                   11109: 
1.556     raeburn  11110:         $newuserscript = <<"ENDSCRIPT";
                   11111: 
1.570     raeburn  11112: function setSearch(createnew,callingForm) {
1.556     raeburn  11113:     if (createnew == 1) {
1.570     raeburn  11114:         for (var i=0; i<callingForm.srchby.length; i++) {
                   11115:             if (callingForm.srchby.options[i].value == 'uname') {
                   11116:                 callingForm.srchby.selectedIndex = i;
1.556     raeburn  11117:             }
                   11118:         }
1.570     raeburn  11119:         for (var i=0; i<callingForm.srchin.length; i++) {
                   11120:             if ( callingForm.srchin.options[i].value == 'dom') {
                   11121: 		callingForm.srchin.selectedIndex = i;
1.556     raeburn  11122:             }
                   11123:         }
1.570     raeburn  11124:         for (var i=0; i<callingForm.srchtype.length; i++) {
                   11125:             if (callingForm.srchtype.options[i].value == 'exact') {
                   11126:                 callingForm.srchtype.selectedIndex = i;
1.556     raeburn  11127:             }
                   11128:         }
1.570     raeburn  11129:         for (var i=0; i<callingForm.srchdomain.length; i++) {
1.994     raeburn  11130:             if (callingForm.srchdomain.options[i].value == '$context_dom') {
1.570     raeburn  11131:                 callingForm.srchdomain.selectedIndex = i;
1.556     raeburn  11132:             }
                   11133:         }
                   11134:     }
                   11135: }
                   11136: ENDSCRIPT
1.558     albertel 11137: 
1.556     raeburn  11138:     }
                   11139: 
1.555     raeburn  11140:     my $output = <<"END_BLOCK";
1.556     raeburn  11141: <script type="text/javascript">
1.824     bisitz   11142: // <![CDATA[
1.570     raeburn  11143: function validateEntry(callingForm) {
1.558     albertel 11144: 
1.556     raeburn  11145:     var checkok = 1;
1.558     albertel 11146:     var srchin;
1.570     raeburn  11147:     for (var i=0; i<callingForm.srchin.length; i++) {
                   11148: 	if ( callingForm.srchin[i].checked ) {
                   11149: 	    srchin = callingForm.srchin[i].value;
1.558     albertel 11150: 	}
                   11151:     }
                   11152: 
1.570     raeburn  11153:     var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
                   11154:     var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
                   11155:     var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
                   11156:     var srchterm =  callingForm.srchterm.value;
                   11157:     var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556     raeburn  11158:     var msg = "";
                   11159: 
                   11160:     if (srchterm == "") {
                   11161:         checkok = 0;
1.1222    damieng  11162:         msg += "$js_lt{'youm'}\\n";
1.556     raeburn  11163:     }
                   11164: 
1.569     raeburn  11165:     if (srchtype== 'begins') {
                   11166:         if (srchterm.length < 2) {
                   11167:             checkok = 0;
1.1222    damieng  11168:             msg += "$js_lt{'thte'}\\n";
1.569     raeburn  11169:         }
                   11170:     }
                   11171: 
1.556     raeburn  11172:     if (srchtype== 'contains') {
                   11173:         if (srchterm.length < 3) {
                   11174:             checkok = 0;
1.1222    damieng  11175:             msg += "$js_lt{'thet'}\\n";
1.556     raeburn  11176:         }
                   11177:     }
                   11178:     if (srchin == 'instd') {
                   11179:         if (srchdomain == '') {
                   11180:             checkok = 0;
1.1222    damieng  11181:             msg += "$js_lt{'yomc'}\\n";
1.556     raeburn  11182:         }
                   11183:     }
                   11184:     if (srchin == 'dom') {
                   11185:         if (srchdomain == '') {
                   11186:             checkok = 0;
1.1222    damieng  11187:             msg += "$js_lt{'ymcd'}\\n";
1.556     raeburn  11188:         }
                   11189:     }
                   11190:     if (srchby == 'lastfirst') {
                   11191:         if (srchterm.indexOf(",") == -1) {
                   11192:             checkok = 0;
1.1222    damieng  11193:             msg += "$js_lt{'whus'}\\n";
1.556     raeburn  11194:         }
                   11195:         if (srchterm.indexOf(",") == srchterm.length -1) {
                   11196:             checkok = 0;
1.1222    damieng  11197:             msg += "$js_lt{'whse'}\\n";
1.556     raeburn  11198:         }
                   11199:     }
                   11200:     if (checkok == 0) {
1.1222    damieng  11201:         alert("$js_lt{'thfo'}\\n"+msg);
1.556     raeburn  11202:         return;
                   11203:     }
                   11204:     if (checkok == 1) {
1.570     raeburn  11205:         callingForm.submit();
1.556     raeburn  11206:     }
                   11207: }
                   11208: 
                   11209: $newuserscript
                   11210: 
1.824     bisitz   11211: // ]]>
1.556     raeburn  11212: </script>
1.558     albertel 11213: 
                   11214: $new_user_create
                   11215: 
1.555     raeburn  11216: END_BLOCK
1.558     albertel 11217: 
1.876     raeburn  11218:     $output .= &Apache::lonhtmlcommon::start_pick_box().
1.1222    damieng  11219:                &Apache::lonhtmlcommon::row_title($html_lt{'doma'}).
1.876     raeburn  11220:                $domform.
                   11221:                &Apache::lonhtmlcommon::row_closure().
1.1222    damieng  11222:                &Apache::lonhtmlcommon::row_title($html_lt{'usr'}).
1.876     raeburn  11223:                $srchbysel.
                   11224:                $srchtypesel. 
                   11225:                '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
                   11226:                $srchinsel.
                   11227:                &Apache::lonhtmlcommon::row_closure(1). 
                   11228:                &Apache::lonhtmlcommon::end_pick_box().
                   11229:                '<br />';
1.1253    raeburn  11230:     return ($output,1);
1.555     raeburn  11231: }
                   11232: 
1.612     raeburn  11233: sub user_rule_check {
1.615     raeburn  11234:     my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.1226    raeburn  11235:     my ($response,%inst_response);
1.612     raeburn  11236:     if (ref($usershash) eq 'HASH') {
1.1226    raeburn  11237:         if (keys(%{$usershash}) > 1) {
                   11238:             my (%by_username,%by_id,%userdoms);
                   11239:             my $checkid; 
                   11240:             if (ref($checks) eq 'HASH') {
                   11241:                 if ((!defined($checks->{'username'})) && (defined($checks->{'id'}))) {
                   11242:                     $checkid = 1;
                   11243:                 }
                   11244:             }
                   11245:             foreach my $user (keys(%{$usershash})) {
                   11246:                 my ($uname,$udom) = split(/:/,$user);
                   11247:                 if ($checkid) {
                   11248:                     if (ref($usershash->{$user}) eq 'HASH') {
                   11249:                         if ($usershash->{$user}->{'id'} ne '') {
1.1227    raeburn  11250:                             $by_id{$udom}{$usershash->{$user}->{'id'}} = $uname; 
1.1226    raeburn  11251:                             $userdoms{$udom} = 1;
1.1227    raeburn  11252:                             if (ref($inst_results) eq 'HASH') {
                   11253:                                 $inst_results->{$uname.':'.$udom} = {};
                   11254:                             }
1.1226    raeburn  11255:                         }
                   11256:                     }
                   11257:                 } else {
                   11258:                     $by_username{$udom}{$uname} = 1;
                   11259:                     $userdoms{$udom} = 1;
1.1227    raeburn  11260:                     if (ref($inst_results) eq 'HASH') {
                   11261:                         $inst_results->{$uname.':'.$udom} = {};
                   11262:                     }
1.1226    raeburn  11263:                 }
                   11264:             }
                   11265:             foreach my $udom (keys(%userdoms)) {
                   11266:                 if (!$got_rules->{$udom}) {
                   11267:                     my %domconfig = &Apache::lonnet::get_dom('configuration',
                   11268:                                                              ['usercreation'],$udom);
                   11269:                     if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   11270:                         foreach my $item ('username','id') {
                   11271:                             if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
1.1227    raeburn  11272:                                 $$curr_rules{$udom}{$item} =
                   11273:                                     $domconfig{'usercreation'}{$item.'_rule'};
1.1226    raeburn  11274:                             }
                   11275:                         }
                   11276:                     }
                   11277:                     $got_rules->{$udom} = 1;
                   11278:                 }
1.612     raeburn  11279:             }
1.1226    raeburn  11280:             if ($checkid) {
                   11281:                 foreach my $udom (keys(%by_id)) {
                   11282:                     my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_id{$udom},'id');
                   11283:                     if ($outcome eq 'ok') {
1.1227    raeburn  11284:                         foreach my $id (keys(%{$by_id{$udom}})) {
                   11285:                             my $uname = $by_id{$udom}{$id};
                   11286:                             $inst_response{$uname.':'.$udom} = $outcome;
                   11287:                         }
1.1226    raeburn  11288:                         if (ref($results) eq 'HASH') {
                   11289:                             foreach my $uname (keys(%{$results})) {
1.1227    raeburn  11290:                                 if (exists($inst_response{$uname.':'.$udom})) {
                   11291:                                     $inst_response{$uname.':'.$udom} = $outcome;
                   11292:                                     $inst_results->{$uname.':'.$udom} = $results->{$uname};
                   11293:                                 }
1.1226    raeburn  11294:                             }
                   11295:                         }
                   11296:                     }
1.612     raeburn  11297:                 }
1.615     raeburn  11298:             } else {
1.1226    raeburn  11299:                 foreach my $udom (keys(%by_username)) {
                   11300:                     my ($outcome,$results) = &Apache::lonnet::get_multiple_instusers($udom,$by_username{$udom});
                   11301:                     if ($outcome eq 'ok') {
1.1227    raeburn  11302:                         foreach my $uname (keys(%{$by_username{$udom}})) {
                   11303:                             $inst_response{$uname.':'.$udom} = $outcome;
                   11304:                         }
1.1226    raeburn  11305:                         if (ref($results) eq 'HASH') {
                   11306:                             foreach my $uname (keys(%{$results})) {
                   11307:                                 $inst_results->{$uname.':'.$udom} = $results->{$uname};
                   11308:                             }
                   11309:                         }
                   11310:                     }
                   11311:                 }
1.612     raeburn  11312:             }
1.1226    raeburn  11313:         } elsif (keys(%{$usershash}) == 1) {
                   11314:             my $user = (keys(%{$usershash}))[0];
                   11315:             my ($uname,$udom) = split(/:/,$user);
                   11316:             if (($udom ne '') && ($uname ne '')) {
                   11317:                 if (ref($usershash->{$user}) eq 'HASH') {
                   11318:                     if (ref($checks) eq 'HASH') {
                   11319:                         if (defined($checks->{'username'})) {
                   11320:                             ($inst_response{$user},%{$inst_results->{$user}}) = 
                   11321:                                 &Apache::lonnet::get_instuser($udom,$uname);
                   11322:                         } elsif (defined($checks->{'id'})) {
                   11323:                             if ($usershash->{$user}->{'id'} ne '') {
                   11324:                                 ($inst_response{$user},%{$inst_results->{$user}}) =
                   11325:                                     &Apache::lonnet::get_instuser($udom,undef,
                   11326:                                                                   $usershash->{$user}->{'id'});
                   11327:                             } else {
                   11328:                                 ($inst_response{$user},%{$inst_results->{$user}}) =
                   11329:                                     &Apache::lonnet::get_instuser($udom,$uname);
                   11330:                             }
1.585     raeburn  11331:                         }
1.1226    raeburn  11332:                     } else {
                   11333:                        ($inst_response{$user},%{$inst_results->{$user}}) =
                   11334:                             &Apache::lonnet::get_instuser($udom,$uname);
                   11335:                        return;
                   11336:                     }
                   11337:                     if (!$got_rules->{$udom}) {
                   11338:                         my %domconfig = &Apache::lonnet::get_dom('configuration',
                   11339:                                                                  ['usercreation'],$udom);
                   11340:                         if (ref($domconfig{'usercreation'}) eq 'HASH') {
                   11341:                             foreach my $item ('username','id') {
                   11342:                                 if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
                   11343:                                    $$curr_rules{$udom}{$item} = 
                   11344:                                        $domconfig{'usercreation'}{$item.'_rule'};
                   11345:                                 }
                   11346:                             }
                   11347:                         }
                   11348:                         $got_rules->{$udom} = 1;
1.585     raeburn  11349:                     }
                   11350:                 }
1.1226    raeburn  11351:             } else {
                   11352:                 return;
                   11353:             }
                   11354:         } else {
                   11355:             return;
                   11356:         }
                   11357:         foreach my $user (keys(%{$usershash})) {
                   11358:             my ($uname,$udom) = split(/:/,$user);
                   11359:             next if (($udom eq '') || ($uname eq ''));
                   11360:             my $id;
1.1227    raeburn  11361:             if (ref($inst_results) eq 'HASH') {
                   11362:                 if (ref($inst_results->{$user}) eq 'HASH') {
                   11363:                     $id = $inst_results->{$user}->{'id'};
                   11364:                 }
                   11365:             }
                   11366:             if ($id eq '') { 
                   11367:                 if (ref($usershash->{$user})) {
                   11368:                     $id = $usershash->{$user}->{'id'};
                   11369:                 }
1.585     raeburn  11370:             }
1.612     raeburn  11371:             foreach my $item (keys(%{$checks})) {
                   11372:                 if (ref($$curr_rules{$udom}) eq 'HASH') {
                   11373:                     if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
                   11374:                         if (@{$$curr_rules{$udom}{$item}} > 0) {
1.1226    raeburn  11375:                             my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,
                   11376:                                                                              $$curr_rules{$udom}{$item});
1.612     raeburn  11377:                             foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
                   11378:                                 if ($rule_check{$rule}) {
                   11379:                                     $$rulematch{$user}{$item} = $rule;
1.1226    raeburn  11380:                                     if ($inst_response{$user} eq 'ok') {
1.615     raeburn  11381:                                         if (ref($inst_results) eq 'HASH') {
                   11382:                                             if (ref($inst_results->{$user}) eq 'HASH') {
                   11383:                                                 if (keys(%{$inst_results->{$user}}) == 0) {
                   11384:                                                     $$alerts{$item}{$udom}{$uname} = 1;
1.1227    raeburn  11385:                                                 } elsif ($item eq 'id') {
                   11386:                                                     if ($inst_results->{$user}->{'id'} eq '') {
                   11387:                                                         $$alerts{$item}{$udom}{$uname} = 1;
                   11388:                                                     }
1.615     raeburn  11389:                                                 }
1.612     raeburn  11390:                                             }
                   11391:                                         }
1.615     raeburn  11392:                                     }
                   11393:                                     last;
1.585     raeburn  11394:                                 }
                   11395:                             }
                   11396:                         }
                   11397:                     }
                   11398:                 }
                   11399:             }
                   11400:         }
                   11401:     }
1.612     raeburn  11402:     return;
                   11403: }
                   11404: 
                   11405: sub user_rule_formats {
                   11406:     my ($domain,$domdesc,$curr_rules,$check) = @_;
                   11407:     my %text = ( 
                   11408:                  'username' => 'Usernames',
                   11409:                  'id'       => 'IDs',
                   11410:                );
                   11411:     my $output;
                   11412:     my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
                   11413:     if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
                   11414:         if (@{$ruleorder} > 0) {
1.1102    raeburn  11415:             $output = '<br />'.
                   11416:                       &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
                   11417:                           '<span class="LC_cusr_emph">','</span>',$domdesc).
                   11418:                       ' <ul>';
1.612     raeburn  11419:             foreach my $rule (@{$ruleorder}) {
                   11420:                 if (ref($curr_rules) eq 'ARRAY') {
                   11421:                     if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
                   11422:                         if (ref($rules->{$rule}) eq 'HASH') {
                   11423:                             $output .= '<li>'.$rules->{$rule}{'name'}.': '.
                   11424:                                         $rules->{$rule}{'desc'}.'</li>';
                   11425:                         }
                   11426:                     }
                   11427:                 }
                   11428:             }
                   11429:             $output .= '</ul>';
                   11430:         }
                   11431:     }
                   11432:     return $output;
                   11433: }
                   11434: 
                   11435: sub instrule_disallow_msg {
1.615     raeburn  11436:     my ($checkitem,$domdesc,$count,$mode) = @_;
1.612     raeburn  11437:     my $response;
                   11438:     my %text = (
                   11439:                   item   => 'username',
                   11440:                   items  => 'usernames',
                   11441:                   match  => 'matches',
                   11442:                   do     => 'does',
                   11443:                   action => 'a username',
                   11444:                   one    => 'one',
                   11445:                );
                   11446:     if ($count > 1) {
                   11447:         $text{'item'} = 'usernames';
                   11448:         $text{'match'} ='match';
                   11449:         $text{'do'} = 'do';
                   11450:         $text{'action'} = 'usernames',
                   11451:         $text{'one'} = 'ones';
                   11452:     }
                   11453:     if ($checkitem eq 'id') {
                   11454:         $text{'items'} = 'IDs';
                   11455:         $text{'item'} = 'ID';
                   11456:         $text{'action'} = 'an ID';
1.615     raeburn  11457:         if ($count > 1) {
                   11458:             $text{'item'} = 'IDs';
                   11459:             $text{'action'} = 'IDs';
                   11460:         }
1.612     raeburn  11461:     }
1.674     bisitz   11462:     $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  11463:     if ($mode eq 'upload') {
                   11464:         if ($checkitem eq 'username') {
                   11465:             $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'}.");
                   11466:         } elsif ($checkitem eq 'id') {
1.674     bisitz   11467:             $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  11468:         }
1.669     raeburn  11469:     } elsif ($mode eq 'selfcreate') {
                   11470:         if ($checkitem eq 'id') {
                   11471:             $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.");
                   11472:         }
1.615     raeburn  11473:     } else {
                   11474:         if ($checkitem eq 'username') {
                   11475:             $response .= &mt("You must choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
                   11476:         } elsif ($checkitem eq 'id') {
                   11477:             $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.");
                   11478:         }
1.612     raeburn  11479:     }
                   11480:     return $response;
1.585     raeburn  11481: }
                   11482: 
1.624     raeburn  11483: sub personal_data_fieldtitles {
                   11484:     my %fieldtitles = &Apache::lonlocal::texthash (
                   11485:                         id => 'Student/Employee ID',
                   11486:                         permanentemail => 'E-mail address',
                   11487:                         lastname => 'Last Name',
                   11488:                         firstname => 'First Name',
                   11489:                         middlename => 'Middle Name',
                   11490:                         generation => 'Generation',
                   11491:                         gen => 'Generation',
1.765     raeburn  11492:                         inststatus => 'Affiliation',
1.624     raeburn  11493:                    );
                   11494:     return %fieldtitles;
                   11495: }
                   11496: 
1.642     raeburn  11497: sub sorted_inst_types {
                   11498:     my ($dom) = @_;
1.1185    raeburn  11499:     my ($usertypes,$order);
                   11500:     my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
                   11501:     if (ref($domdefaults{'inststatus'}) eq 'HASH') {
                   11502:         $usertypes = $domdefaults{'inststatus'}{'inststatustypes'};
                   11503:         $order = $domdefaults{'inststatus'}{'inststatusorder'};
                   11504:     } else {
                   11505:         ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
                   11506:     }
1.642     raeburn  11507:     my $othertitle = &mt('All users');
                   11508:     if ($env{'request.course.id'}) {
1.668     raeburn  11509:         $othertitle  = &mt('Any users');
1.642     raeburn  11510:     }
                   11511:     my @types;
                   11512:     if (ref($order) eq 'ARRAY') {
                   11513:         @types = @{$order};
                   11514:     }
                   11515:     if (@types == 0) {
                   11516:         if (ref($usertypes) eq 'HASH') {
                   11517:             @types = sort(keys(%{$usertypes}));
                   11518:         }
                   11519:     }
                   11520:     if (keys(%{$usertypes}) > 0) {
                   11521:         $othertitle = &mt('Other users');
                   11522:     }
                   11523:     return ($othertitle,$usertypes,\@types);
                   11524: }
                   11525: 
1.645     raeburn  11526: sub get_institutional_codes {
1.1361    raeburn  11527:     my ($cdom,$crs,$settings,$allcourses,$LC_code) = @_;
1.645     raeburn  11528: # Get complete list of course sections to update
                   11529:     my @currsections = ();
                   11530:     my @currxlists = ();
1.1361    raeburn  11531:     my (%unclutteredsec,%unclutteredlcsec);
1.645     raeburn  11532:     my $coursecode = $$settings{'internal.coursecode'};
1.1361    raeburn  11533:     my $crskey = $crs.':'.$coursecode;
                   11534:     @{$unclutteredsec{$crskey}} = ();
                   11535:     @{$unclutteredlcsec{$crskey}} = ();
1.645     raeburn  11536: 
                   11537:     if ($$settings{'internal.sectionnums'} ne '') {
                   11538:         @currsections = split(/,/,$$settings{'internal.sectionnums'});
                   11539:     }
                   11540: 
                   11541:     if ($$settings{'internal.crosslistings'} ne '') {
                   11542:         @currxlists = split(/,/,$$settings{'internal.crosslistings'});
                   11543:     }
                   11544: 
                   11545:     if (@currxlists > 0) {
1.1361    raeburn  11546:         foreach my $xl (@currxlists) {
                   11547:             if ($xl =~ /^([^:]+):(\w*)$/) {
1.645     raeburn  11548:                 unless (grep/^$1$/,@{$allcourses}) {
1.1263    raeburn  11549:                     push(@{$allcourses},$1);
1.645     raeburn  11550:                     $$LC_code{$1} = $2;
                   11551:                 }
                   11552:             }
                   11553:         }
                   11554:     }
1.1361    raeburn  11555: 
1.645     raeburn  11556:     if (@currsections > 0) {
1.1361    raeburn  11557:         foreach my $sec (@currsections) {
                   11558:             if ($sec =~ m/^(\w+):(\w*)$/ ) {
                   11559:                 my $instsec = $1;
1.645     raeburn  11560:                 my $lc_sec = $2;
1.1361    raeburn  11561:                 unless (grep/^\Q$instsec\E$/,@{$unclutteredsec{$crskey}}) {
                   11562:                     push(@{$unclutteredsec{$crskey}},$instsec);
                   11563:                     push(@{$unclutteredlcsec{$crskey}},$lc_sec);
                   11564:                 }
                   11565:             }
                   11566:         }
                   11567:     }
                   11568: 
                   11569:     if (@{$unclutteredsec{$crskey}} > 0) {
                   11570:         my %formattedsec = &Apache::lonnet::auto_instsec_reformat($cdom,'clutter',\%unclutteredsec);
                   11571:         if ((ref($formattedsec{$crskey}) eq 'ARRAY') && (ref($unclutteredlcsec{$crskey}) eq 'ARRAY')) {
                   11572:             for (my $i=0; $i<@{$formattedsec{$crskey}}; $i++) {
                   11573:                 my $sec = $coursecode.$formattedsec{$crskey}[$i];
                   11574:                 unless (grep/^\Q$sec\E$/,@{$allcourses}) {
1.1263    raeburn  11575:                     push(@{$allcourses},$sec);
1.1361    raeburn  11576:                     $$LC_code{$sec} = $unclutteredlcsec{$crskey}[$i];
1.645     raeburn  11577:                 }
                   11578:             }
                   11579:         }
                   11580:     }
                   11581:     return;
                   11582: }
                   11583: 
1.971     raeburn  11584: sub get_standard_codeitems {
                   11585:     return ('Year','Semester','Department','Number','Section');
                   11586: }
                   11587: 
1.112     bowersj2 11588: =pod
                   11589: 
1.780     raeburn  11590: =head1 Slot Helpers
                   11591: 
                   11592: =over 4
                   11593: 
                   11594: =item * sorted_slots()
                   11595: 
1.1040    raeburn  11596: Sorts an array of slot names in order of an optional sort key,
                   11597: default sort is by slot start time (earliest first). 
1.780     raeburn  11598: 
                   11599: Inputs:
                   11600: 
                   11601: =over 4
                   11602: 
                   11603: slotsarr  - Reference to array of unsorted slot names.
                   11604: 
                   11605: slots     - Reference to hash of hash, where outer hash keys are slot names.
                   11606: 
1.1040    raeburn  11607: sortkey   - Name of key in inner hash to be sorted on (e.g., starttime).
                   11608: 
1.549     albertel 11609: =back
                   11610: 
1.780     raeburn  11611: Returns:
                   11612: 
                   11613: =over 4
                   11614: 
1.1040    raeburn  11615: sorted   - An array of slot names sorted by a specified sort key 
                   11616:            (default sort key is start time of the slot).
1.780     raeburn  11617: 
                   11618: =back
                   11619: 
                   11620: =cut
                   11621: 
                   11622: 
                   11623: sub sorted_slots {
1.1040    raeburn  11624:     my ($slotsarr,$slots,$sortkey) = @_;
                   11625:     if ($sortkey eq '') {
                   11626:         $sortkey = 'starttime';
                   11627:     }
1.780     raeburn  11628:     my @sorted;
                   11629:     if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
                   11630:         @sorted =
                   11631:             sort {
                   11632:                      if (ref($slots->{$a}) && ref($slots->{$b})) {
1.1040    raeburn  11633:                          return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
1.780     raeburn  11634:                      }
                   11635:                      if (ref($slots->{$a})) { return -1;}
                   11636:                      if (ref($slots->{$b})) { return 1;}
                   11637:                      return 0;
                   11638:                  } @{$slotsarr};
                   11639:     }
                   11640:     return @sorted;
                   11641: }
                   11642: 
1.1040    raeburn  11643: =pod
                   11644: 
                   11645: =item * get_future_slots()
                   11646: 
                   11647: Inputs:
                   11648: 
                   11649: =over 4
                   11650: 
                   11651: cnum - course number
                   11652: 
                   11653: cdom - course domain
                   11654: 
                   11655: now - current UNIX time
                   11656: 
                   11657: symb - optional symb
                   11658: 
                   11659: =back
                   11660: 
                   11661: Returns:
                   11662: 
                   11663: =over 4
                   11664: 
                   11665: sorted_reservable - ref to array of student_schedulable slots currently 
                   11666:                     reservable, ordered by end date of reservation period.
                   11667: 
                   11668: reservable_now - ref to hash of student_schedulable slots currently
                   11669:                  reservable.
                   11670: 
                   11671:     Keys in inner hash are:
                   11672:     (a) symb: either blank or symb to which slot use is restricted.
1.1250    raeburn  11673:     (b) endreserve: end date of reservation period.
                   11674:     (c) uniqueperiod: start,end dates when slot is to be uniquely
                   11675:         selected.
1.1040    raeburn  11676: 
                   11677: sorted_future - ref to array of student_schedulable slots reservable in
                   11678:                 the future, ordered by start date of reservation period.
                   11679: 
                   11680: future_reservable - ref to hash of student_schedulable slots reservable
                   11681:                     in the future.
                   11682: 
                   11683:     Keys in inner hash are:
                   11684:     (a) symb: either blank or symb to which slot use is restricted.
1.1250    raeburn  11685:     (b) startreserve: start date of reservation period.
                   11686:     (c) uniqueperiod: start,end dates when slot is to be uniquely
                   11687:         selected.
1.1040    raeburn  11688: 
                   11689: =back
                   11690: 
                   11691: =cut
                   11692: 
                   11693: sub get_future_slots {
                   11694:     my ($cnum,$cdom,$now,$symb) = @_;
1.1229    raeburn  11695:     my $map;
                   11696:     if ($symb) {
                   11697:         ($map) = &Apache::lonnet::decode_symb($symb);
                   11698:     }
1.1040    raeburn  11699:     my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
                   11700:     my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
                   11701:     foreach my $slot (keys(%slots)) {
                   11702:         next unless($slots{$slot}->{'type'} eq 'schedulable_student');
                   11703:         if ($symb) {
1.1229    raeburn  11704:             if ($slots{$slot}->{'symb'} ne '') {
                   11705:                 my $canuse;
                   11706:                 my %oksymbs;
                   11707:                 my @slotsymbs = split(/\s*,\s*/,$slots{$slot}->{'symb'});
                   11708:                 map { $oksymbs{$_} = 1; } @slotsymbs;
                   11709:                 if ($oksymbs{$symb}) {
                   11710:                     $canuse = 1;
                   11711:                 } else {
                   11712:                     foreach my $item (@slotsymbs) {
                   11713:                         if ($item =~ /\.(page|sequence)$/) {
                   11714:                             (undef,undef,my $sloturl) = &Apache::lonnet::decode_symb($item);
                   11715:                             if (($map ne '') && ($map eq $sloturl)) {
                   11716:                                 $canuse = 1;
                   11717:                                 last;
                   11718:                             }
                   11719:                         }
                   11720:                     }
                   11721:                 }
                   11722:                 next unless ($canuse);
                   11723:             }
1.1040    raeburn  11724:         }
                   11725:         if (($slots{$slot}->{'starttime'} > $now) &&
                   11726:             ($slots{$slot}->{'endtime'} > $now)) {
                   11727:             if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
                   11728:                 my $userallowed = 0;
                   11729:                 if ($slots{$slot}->{'allowedsections'}) {
                   11730:                     my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
                   11731:                     if (!defined($env{'request.role.sec'})
                   11732:                         && grep(/^No section assigned$/,@allowed_sec)) {
                   11733:                         $userallowed=1;
                   11734:                     } else {
                   11735:                         if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
                   11736:                             $userallowed=1;
                   11737:                         }
                   11738:                     }
                   11739:                     unless ($userallowed) {
                   11740:                         if (defined($env{'request.course.groups'})) {
                   11741:                             my @groups = split(/:/,$env{'request.course.groups'});
                   11742:                             foreach my $group (@groups) {
                   11743:                                 if (grep(/^\Q$group\E$/,@allowed_sec)) {
                   11744:                                     $userallowed=1;
                   11745:                                     last;
                   11746:                                 }
                   11747:                             }
                   11748:                         }
                   11749:                     }
                   11750:                 }
                   11751:                 if ($slots{$slot}->{'allowedusers'}) {
                   11752:                     my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
                   11753:                     my $user = $env{'user.name'}.':'.$env{'user.domain'};
                   11754:                     if (grep(/^\Q$user\E$/,@allowed_users)) {
                   11755:                         $userallowed = 1;
                   11756:                     }
                   11757:                 }
                   11758:                 next unless($userallowed);
                   11759:             }
                   11760:             my $startreserve = $slots{$slot}->{'startreserve'};
                   11761:             my $endreserve = $slots{$slot}->{'endreserve'};
                   11762:             my $symb = $slots{$slot}->{'symb'};
1.1250    raeburn  11763:             my $uniqueperiod;
                   11764:             if (ref($slots{$slot}->{'uniqueperiod'}) eq 'ARRAY') {
                   11765:                 $uniqueperiod = join(',',@{$slots{$slot}->{'uniqueperiod'}});
                   11766:             }
1.1040    raeburn  11767:             if (($startreserve < $now) &&
                   11768:                 (!$endreserve || $endreserve > $now)) {
                   11769:                 my $lastres = $endreserve;
                   11770:                 if (!$lastres) {
                   11771:                     $lastres = $slots{$slot}->{'starttime'};
                   11772:                 }
                   11773:                 $reservable_now{$slot} = {
                   11774:                                            symb       => $symb,
1.1250    raeburn  11775:                                            endreserve => $lastres,
                   11776:                                            uniqueperiod => $uniqueperiod,
1.1040    raeburn  11777:                                          };
                   11778:             } elsif (($startreserve > $now) &&
                   11779:                      (!$endreserve || $endreserve > $startreserve)) {
                   11780:                 $future_reservable{$slot} = {
                   11781:                                               symb         => $symb,
1.1250    raeburn  11782:                                               startreserve => $startreserve,
                   11783:                                               uniqueperiod => $uniqueperiod,
1.1040    raeburn  11784:                                             };
                   11785:             }
                   11786:         }
                   11787:     }
                   11788:     my @unsorted_reservable = keys(%reservable_now);
                   11789:     if (@unsorted_reservable > 0) {
                   11790:         @sorted_reservable = 
                   11791:             &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
                   11792:     }
                   11793:     my @unsorted_future = keys(%future_reservable);
                   11794:     if (@unsorted_future > 0) {
                   11795:         @sorted_future =
                   11796:             &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
                   11797:     }
                   11798:     return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
                   11799: }
1.780     raeburn  11800: 
                   11801: =pod
                   11802: 
1.1057    foxr     11803: =back
                   11804: 
1.549     albertel 11805: =head1 HTTP Helpers
                   11806: 
                   11807: =over 4
                   11808: 
1.648     raeburn  11809: =item * &get_unprocessed_cgi($query,$possible_names)
1.112     bowersj2 11810: 
1.258     albertel 11811: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112     bowersj2 11812: $query.  The parameters listed in $possible_names (an array reference),
1.258     albertel 11813: will be set in $env{'form.name'} if they do not already exist.
1.112     bowersj2 11814: 
                   11815: Typically called with $ENV{'QUERY_STRING'} as the first parameter.  
                   11816: $possible_names is an ref to an array of form element names.  As an example:
                   11817: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258     albertel 11818: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112     bowersj2 11819: 
                   11820: =cut
1.1       albertel 11821: 
1.6       albertel 11822: sub get_unprocessed_cgi {
1.25      albertel 11823:   my ($query,$possible_names)= @_;
1.26      matthew  11824:   # $Apache::lonxml::debug=1;
1.356     albertel 11825:   foreach my $pair (split(/&/,$query)) {
                   11826:     my ($name, $value) = split(/=/,$pair);
1.369     www      11827:     $name = &unescape($name);
1.25      albertel 11828:     if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
                   11829:       $value =~ tr/+/ /;
                   11830:       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258     albertel 11831:       unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25      albertel 11832:     }
1.16      harris41 11833:   }
1.6       albertel 11834: }
                   11835: 
1.112     bowersj2 11836: =pod
                   11837: 
1.648     raeburn  11838: =item * &cacheheader() 
1.112     bowersj2 11839: 
                   11840: returns cache-controlling header code
                   11841: 
                   11842: =cut
                   11843: 
1.7       albertel 11844: sub cacheheader {
1.258     albertel 11845:     unless ($env{'request.method'} eq 'GET') { return ''; }
1.216     albertel 11846:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
                   11847:     my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7       albertel 11848:                 <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
                   11849:                 <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216     albertel 11850:     return $output;
1.7       albertel 11851: }
                   11852: 
1.112     bowersj2 11853: =pod
                   11854: 
1.648     raeburn  11855: =item * &no_cache($r) 
1.112     bowersj2 11856: 
                   11857: specifies header code to not have cache
                   11858: 
                   11859: =cut
                   11860: 
1.9       albertel 11861: sub no_cache {
1.216     albertel 11862:     my ($r) = @_;
                   11863:     if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258     albertel 11864: 	$env{'request.method'} ne 'GET') { return ''; }
1.216     albertel 11865:     my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
                   11866:     $r->no_cache(1);
                   11867:     $r->header_out("Expires" => $date);
                   11868:     $r->header_out("Pragma" => "no-cache");
1.123     www      11869: }
                   11870: 
                   11871: sub content_type {
1.181     albertel 11872:     my ($r,$type,$charset) = @_;
1.299     foxr     11873:     if ($r) {
                   11874: 	#  Note that printout.pl calls this with undef for $r.
                   11875: 	&no_cache($r);
                   11876:     }
1.258     albertel 11877:     if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181     albertel 11878:     unless ($charset) {
                   11879: 	$charset=&Apache::lonlocal::current_encoding;
                   11880:     }
                   11881:     if ($charset) { $type.='; charset='.$charset; }
                   11882:     if ($r) {
                   11883: 	$r->content_type($type);
                   11884:     } else {
                   11885: 	print("Content-type: $type\n\n");
                   11886:     }
1.9       albertel 11887: }
1.25      albertel 11888: 
1.112     bowersj2 11889: =pod
                   11890: 
1.648     raeburn  11891: =item * &add_to_env($name,$value) 
1.112     bowersj2 11892: 
1.258     albertel 11893: adds $name to the %env hash with value
1.112     bowersj2 11894: $value, if $name already exists, the entry is converted to an array
                   11895: reference and $value is added to the array.
                   11896: 
                   11897: =cut
                   11898: 
1.25      albertel 11899: sub add_to_env {
                   11900:   my ($name,$value)=@_;
1.258     albertel 11901:   if (defined($env{$name})) {
                   11902:     if (ref($env{$name})) {
1.25      albertel 11903:       #already have multiple values
1.258     albertel 11904:       push(@{ $env{$name} },$value);
1.25      albertel 11905:     } else {
                   11906:       #first time seeing multiple values, convert hash entry to an arrayref
1.258     albertel 11907:       my $first=$env{$name};
                   11908:       undef($env{$name});
                   11909:       push(@{ $env{$name} },$first,$value);
1.25      albertel 11910:     }
                   11911:   } else {
1.258     albertel 11912:     $env{$name}=$value;
1.25      albertel 11913:   }
1.31      albertel 11914: }
1.149     albertel 11915: 
                   11916: =pod
                   11917: 
1.648     raeburn  11918: =item * &get_env_multiple($name) 
1.149     albertel 11919: 
1.258     albertel 11920: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149     albertel 11921: values may be defined and end up as an array ref.
                   11922: 
                   11923: returns an array of values
                   11924: 
                   11925: =cut
                   11926: 
                   11927: sub get_env_multiple {
                   11928:     my ($name) = @_;
                   11929:     my @values;
1.258     albertel 11930:     if (defined($env{$name})) {
1.149     albertel 11931:         # exists is it an array
1.258     albertel 11932:         if (ref($env{$name})) {
                   11933:             @values=@{ $env{$name} };
1.149     albertel 11934:         } else {
1.258     albertel 11935:             $values[0]=$env{$name};
1.149     albertel 11936:         }
                   11937:     }
                   11938:     return(@values);
                   11939: }
                   11940: 
1.1249    damieng  11941: # Looks at given dependencies, and returns something depending on the context.
                   11942: # For coursedocs paste, returns (undef, $counter, $numpathchg, \%existing).
                   11943: # For syllabus rewrites, returns (undef, $counter, $numpathchg, \%existing, \%mapping).
                   11944: # For all other contexts, returns ($output, $counter, $numpathchg).
                   11945: # $output: string with the HTML output. Can contain missing dependencies with an upload form, existing dependencies, and dependencies no longer in use.
                   11946: # $counter: integer with the number of existing dependencies when no HTML output is returned, and the number of missing dependencies when an HTML output is returned.
                   11947: # $numpathchg: integer with the number of cleaned up dependency paths.
                   11948: # \%existing: hash reference clean path -> 1 only for existing dependencies.
                   11949: # \%mapping: hash reference clean path -> original path for all dependencies.
                   11950: # @param {string} actionurl - The path to the handler, indicative of the context.
                   11951: # @param {string} state - Can contain HTML with hidden inputs that will be added to the output form.
                   11952: # @param {hash reference} allfiles - List of file info from lonnet::extract_embedded_items
                   11953: # @param {hash reference} codebase - undef, not modified by lonnet::extract_embedded_items ?
                   11954: # @param {hash reference} args - More parameters ! Possible keys: error_on_invalid_names (boolean), ignore_remote_references (boolean), current_path (string), docs_url (string), docs_title (string), context (string)
                   11955: # @return {Array} - array depending on the context (not a reference)
1.660     raeburn  11956: sub ask_for_embedded_content {
1.1249    damieng  11957:     # NOTE: documentation was added afterwards, it could be wrong
1.660     raeburn  11958:     my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
1.1071    raeburn  11959:     my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
1.1085    raeburn  11960:         %currsubfile,%unused,$rem);
1.1071    raeburn  11961:     my $counter = 0;
                   11962:     my $numnew = 0;
1.987     raeburn  11963:     my $numremref = 0;
                   11964:     my $numinvalid = 0;
                   11965:     my $numpathchg = 0;
                   11966:     my $numexisting = 0;
1.1071    raeburn  11967:     my $numunused = 0;
                   11968:     my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
1.1156    raeburn  11969:         $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap);
1.1071    raeburn  11970:     my $heading = &mt('Upload embedded files');
                   11971:     my $buttontext = &mt('Upload');
                   11972: 
1.1249    damieng  11973:     # fills these variables based on the context:
                   11974:     # $navmap, $cdom, $cnum, $udom, $uname, $url, $toplevel, $getpropath,
                   11975:     # $path, $fileloc, $title, $rem, $filename
1.1085    raeburn  11976:     if ($env{'request.course.id'}) {
1.1123    raeburn  11977:         if ($actionurl eq '/adm/dependencies') {
                   11978:             $navmap = Apache::lonnavmaps::navmap->new();
                   11979:         }
                   11980:         $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   11981:         $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1085    raeburn  11982:     }
1.1123    raeburn  11983:     if (($actionurl eq '/adm/portfolio') || 
                   11984:         ($actionurl eq '/adm/coursegrp_portfolio')) {
1.984     raeburn  11985:         my $current_path='/';
                   11986:         if ($env{'form.currentpath'}) {
                   11987:             $current_path = $env{'form.currentpath'};
                   11988:         }
                   11989:         if ($actionurl eq '/adm/coursegrp_portfolio') {
1.1123    raeburn  11990:             $udom = $cdom;
                   11991:             $uname = $cnum;
1.984     raeburn  11992:             $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
                   11993:         } else {
                   11994:             $udom = $env{'user.domain'};
                   11995:             $uname = $env{'user.name'};
                   11996:             $url = '/userfiles/portfolio';
                   11997:         }
1.987     raeburn  11998:         $toplevel = $url.'/';
1.984     raeburn  11999:         $url .= $current_path;
                   12000:         $getpropath = 1;
1.987     raeburn  12001:     } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
                   12002:              ($actionurl eq '/adm/imsimport')) { 
1.1022    www      12003:         my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
1.1026    raeburn  12004:         $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
1.987     raeburn  12005:         $toplevel = $url;
1.984     raeburn  12006:         if ($rest ne '') {
1.987     raeburn  12007:             $url .= $rest;
                   12008:         }
                   12009:     } elsif ($actionurl eq '/adm/coursedocs') {
                   12010:         if (ref($args) eq 'HASH') {
1.1071    raeburn  12011:             $url = $args->{'docs_url'};
                   12012:             $toplevel = $url;
1.1084    raeburn  12013:             if ($args->{'context'} eq 'paste') {
                   12014:                 ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
                   12015:                 ($path) = 
                   12016:                     ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
                   12017:                 $fileloc = &Apache::lonnet::filelocation('',$toplevel);
                   12018:                 $fileloc =~ s{^/}{};
                   12019:             }
1.1071    raeburn  12020:         }
1.1084    raeburn  12021:     } elsif ($actionurl eq '/adm/dependencies')  {
1.1071    raeburn  12022:         if ($env{'request.course.id'} ne '') {
                   12023:             if (ref($args) eq 'HASH') {
                   12024:                 $url = $args->{'docs_url'};
                   12025:                 $title = $args->{'docs_title'};
1.1126    raeburn  12026:                 $toplevel = $url; 
                   12027:                 unless ($toplevel =~ m{^/}) {
                   12028:                     $toplevel = "/$url";
                   12029:                 }
1.1085    raeburn  12030:                 ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
1.1126    raeburn  12031:                 if ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E)}) {
                   12032:                     $path = $1;
                   12033:                 } else {
                   12034:                     ($path) =
                   12035:                         ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
                   12036:                 }
1.1195    raeburn  12037:                 if ($toplevel=~/^\/*(uploaded|editupload)/) {
                   12038:                     $fileloc = $toplevel;
                   12039:                     $fileloc=~ s/^\s*(\S+)\s*$/$1/;
                   12040:                     my ($udom,$uname,$fname) =
                   12041:                         ($fileloc=~ m{^/+(?:uploaded|editupload)/+($match_domain)/+($match_name)/+(.*)$});
                   12042:                     $fileloc = propath($udom,$uname).'/userfiles/'.$fname;
                   12043:                 } else {
                   12044:                     $fileloc = &Apache::lonnet::filelocation('',$toplevel);
                   12045:                 }
1.1071    raeburn  12046:                 $fileloc =~ s{^/}{};
                   12047:                 ($filename) = ($fileloc =~ m{.+/([^/]+)$});
                   12048:                 $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
                   12049:             }
1.987     raeburn  12050:         }
1.1123    raeburn  12051:     } elsif ($actionurl eq "/public/$cdom/$cnum/syllabus") {
                   12052:         $udom = $cdom;
                   12053:         $uname = $cnum;
                   12054:         $url = "/uploaded/$cdom/$cnum/portfolio/syllabus";
                   12055:         $toplevel = $url;
                   12056:         $path = $url;
                   12057:         $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';
                   12058:         $fileloc =~ s{^/}{};
1.987     raeburn  12059:     }
1.1249    damieng  12060:     
                   12061:     # parses the dependency paths to get some info
                   12062:     # fills $newfiles, $mapping, $subdependencies, $dependencies
                   12063:     # $newfiles: hash URL -> 1 for new files or external URLs
                   12064:     # (will be completed later)
                   12065:     # $mapping:
                   12066:     #   for external URLs: external URL -> external URL
                   12067:     #   for relative paths: clean path -> original path
                   12068:     # $subdependencies: hash clean path -> clean file name -> 1 for relative paths in subdirectories
                   12069:     # $dependencies: hash clean or not file name -> 1 for relative paths not in subdirectories
1.1126    raeburn  12070:     foreach my $file (keys(%{$allfiles})) {
                   12071:         my $embed_file;
                   12072:         if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {
                   12073:             $embed_file = $1;
                   12074:         } else {
                   12075:             $embed_file = $file;
                   12076:         }
1.1158    raeburn  12077:         my ($absolutepath,$cleaned_file);
                   12078:         if ($embed_file =~ m{^\w+://}) {
                   12079:             $cleaned_file = $embed_file;
1.1147    raeburn  12080:             $newfiles{$cleaned_file} = 1;
                   12081:             $mapping{$cleaned_file} = $embed_file;
1.987     raeburn  12082:         } else {
1.1158    raeburn  12083:             $cleaned_file = &clean_path($embed_file);
1.987     raeburn  12084:             if ($embed_file =~ m{^/}) {
                   12085:                 $absolutepath = $embed_file;
                   12086:             }
1.1147    raeburn  12087:             if ($cleaned_file =~ m{/}) {
                   12088:                 my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$});
1.987     raeburn  12089:                 $path = &check_for_traversal($path,$url,$toplevel);
                   12090:                 my $item = $fname;
                   12091:                 if ($path ne '') {
                   12092:                     $item = $path.'/'.$fname;
                   12093:                     $subdependencies{$path}{$fname} = 1;
                   12094:                 } else {
                   12095:                     $dependencies{$item} = 1;
                   12096:                 }
                   12097:                 if ($absolutepath) {
                   12098:                     $mapping{$item} = $absolutepath;
                   12099:                 } else {
                   12100:                     $mapping{$item} = $embed_file;
                   12101:                 }
                   12102:             } else {
                   12103:                 $dependencies{$embed_file} = 1;
                   12104:                 if ($absolutepath) {
1.1147    raeburn  12105:                     $mapping{$cleaned_file} = $absolutepath;
1.987     raeburn  12106:                 } else {
1.1147    raeburn  12107:                     $mapping{$cleaned_file} = $embed_file;
1.987     raeburn  12108:                 }
                   12109:             }
1.984     raeburn  12110:         }
                   12111:     }
1.1249    damieng  12112:     
                   12113:     # looks for all existing files in dependency subdirectories (from $subdependencies filled above)
                   12114:     # and lists
                   12115:     # fills $currsubfile, $pathchanges, $existing, $numexisting, $newfiles, $unused
                   12116:     # $currsubfile: hash clean path -> file name -> 1 for all existing files in the path
                   12117:     # $pathchanges: hash clean path -> 1 if the file in subdirectory exists and
                   12118:     #                                    the path had to be cleaned up
                   12119:     # $existing: hash clean path -> 1 if the file exists
                   12120:     # $numexisting: number of keys in $existing
                   12121:     # $newfiles: updated with clean path -> 1 for files in subdirectories that do not exist
                   12122:     # $unused: only for /adm/dependencies, hash clean path -> 1 for existing files in
                   12123:     #                                      dependency subdirectories that are
                   12124:     #                                      not listed as dependencies, with some exceptions using $rem
1.1071    raeburn  12125:     my $dirptr = 16384;
1.984     raeburn  12126:     foreach my $path (keys(%subdependencies)) {
1.1071    raeburn  12127:         $currsubfile{$path} = {};
1.1123    raeburn  12128:         if (($actionurl eq '/adm/portfolio') || 
                   12129:             ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021    raeburn  12130:             my ($sublistref,$listerror) =
                   12131:                 &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
                   12132:             if (ref($sublistref) eq 'ARRAY') {
                   12133:                 foreach my $line (@{$sublistref}) {
                   12134:                     my ($file_name,$rest) = split(/\&/,$line,2);
1.1071    raeburn  12135:                     $currsubfile{$path}{$file_name} = 1;
1.1021    raeburn  12136:                 }
1.984     raeburn  12137:             }
1.987     raeburn  12138:         } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984     raeburn  12139:             if (opendir(my $dir,$url.'/'.$path)) {
                   12140:                 my @subdir_list = grep(!/^\./,readdir($dir));
1.1071    raeburn  12141:                 map {$currsubfile{$path}{$_} = 1;} @subdir_list;
                   12142:             }
1.1084    raeburn  12143:         } elsif (($actionurl eq '/adm/dependencies') ||
                   12144:                  (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123    raeburn  12145:                   ($args->{'context'} eq 'paste')) ||
                   12146:                  ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071    raeburn  12147:             if ($env{'request.course.id'} ne '') {
1.1123    raeburn  12148:                 my $dir;
                   12149:                 if ($actionurl eq "/public/$cdom/$cnum/syllabus") {
                   12150:                     $dir = $fileloc;
                   12151:                 } else {
                   12152:                     ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
                   12153:                 }
1.1071    raeburn  12154:                 if ($dir ne '') {
                   12155:                     my ($sublistref,$listerror) =
                   12156:                         &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
                   12157:                     if (ref($sublistref) eq 'ARRAY') {
                   12158:                         foreach my $line (@{$sublistref}) {
                   12159:                             my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
                   12160:                                 undef,$mtime)=split(/\&/,$line,12);
                   12161:                             unless (($testdir&$dirptr) ||
                   12162:                                     ($file_name =~ /^\.\.?$/)) {
                   12163:                                 $currsubfile{$path}{$file_name} = [$size,$mtime];
                   12164:                             }
                   12165:                         }
                   12166:                     }
                   12167:                 }
1.984     raeburn  12168:             }
                   12169:         }
                   12170:         foreach my $file (keys(%{$subdependencies{$path}})) {
1.1071    raeburn  12171:             if (exists($currsubfile{$path}{$file})) {
1.987     raeburn  12172:                 my $item = $path.'/'.$file;
                   12173:                 unless ($mapping{$item} eq $item) {
                   12174:                     $pathchanges{$item} = 1;
                   12175:                 }
                   12176:                 $existing{$item} = 1;
                   12177:                 $numexisting ++;
                   12178:             } else {
                   12179:                 $newfiles{$path.'/'.$file} = 1;
1.984     raeburn  12180:             }
                   12181:         }
1.1071    raeburn  12182:         if ($actionurl eq '/adm/dependencies') {
                   12183:             foreach my $path (keys(%currsubfile)) {
                   12184:                 if (ref($currsubfile{$path}) eq 'HASH') {
                   12185:                     foreach my $file (keys(%{$currsubfile{$path}})) {
                   12186:                          unless ($subdependencies{$path}{$file}) {
1.1085    raeburn  12187:                              next if (($rem ne '') &&
                   12188:                                       (($env{"httpref.$rem"."$path/$file"} ne '') ||
                   12189:                                        (ref($navmap) &&
                   12190:                                        (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
                   12191:                                         (($file =~ /^(.*\.s?html?)\.bak$/i) &&
                   12192:                                          ($navmap->getResourceByUrl($rem."$path/$1")))))));
1.1071    raeburn  12193:                              $unused{$path.'/'.$file} = 1; 
                   12194:                          }
                   12195:                     }
                   12196:                 }
                   12197:             }
                   12198:         }
1.984     raeburn  12199:     }
1.1249    damieng  12200:     
                   12201:     # fills $currfile, hash file name -> 1 or [$size,$mtime]
                   12202:     # for files in $url or $fileloc (target directory) in some contexts
1.987     raeburn  12203:     my %currfile;
1.1123    raeburn  12204:     if (($actionurl eq '/adm/portfolio') ||
                   12205:         ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021    raeburn  12206:         my ($dirlistref,$listerror) =
                   12207:             &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
                   12208:         if (ref($dirlistref) eq 'ARRAY') {
                   12209:             foreach my $line (@{$dirlistref}) {
                   12210:                 my ($file_name,$rest) = split(/\&/,$line,2);
                   12211:                 $currfile{$file_name} = 1;
                   12212:             }
1.984     raeburn  12213:         }
1.987     raeburn  12214:     } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984     raeburn  12215:         if (opendir(my $dir,$url)) {
1.987     raeburn  12216:             my @dir_list = grep(!/^\./,readdir($dir));
1.984     raeburn  12217:             map {$currfile{$_} = 1;} @dir_list;
                   12218:         }
1.1084    raeburn  12219:     } elsif (($actionurl eq '/adm/dependencies') ||
                   12220:              (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123    raeburn  12221:               ($args->{'context'} eq 'paste')) ||
                   12222:              ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071    raeburn  12223:         if ($env{'request.course.id'} ne '') {
                   12224:             my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
                   12225:             if ($dir ne '') {
                   12226:                 my ($dirlistref,$listerror) =
                   12227:                     &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
                   12228:                 if (ref($dirlistref) eq 'ARRAY') {
                   12229:                     foreach my $line (@{$dirlistref}) {
                   12230:                         my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
                   12231:                             $size,undef,$mtime)=split(/\&/,$line,12);
                   12232:                         unless (($testdir&$dirptr) ||
                   12233:                                 ($file_name =~ /^\.\.?$/)) {
                   12234:                             $currfile{$file_name} = [$size,$mtime];
                   12235:                         }
                   12236:                     }
                   12237:                 }
                   12238:             }
                   12239:         }
1.984     raeburn  12240:     }
1.1249    damieng  12241:     # updates $pathchanges, $existing, $numexisting, $newfiles and $unused for files that
                   12242:     # are not in subdirectories, using $currfile
1.984     raeburn  12243:     foreach my $file (keys(%dependencies)) {
1.1071    raeburn  12244:         if (exists($currfile{$file})) {
1.987     raeburn  12245:             unless ($mapping{$file} eq $file) {
                   12246:                 $pathchanges{$file} = 1;
                   12247:             }
                   12248:             $existing{$file} = 1;
                   12249:             $numexisting ++;
                   12250:         } else {
1.984     raeburn  12251:             $newfiles{$file} = 1;
                   12252:         }
                   12253:     }
1.1071    raeburn  12254:     foreach my $file (keys(%currfile)) {
                   12255:         unless (($file eq $filename) ||
                   12256:                 ($file eq $filename.'.bak') ||
                   12257:                 ($dependencies{$file})) {
1.1085    raeburn  12258:             if ($actionurl eq '/adm/dependencies') {
1.1126    raeburn  12259:                 unless ($toplevel =~ m{^\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E}) {
                   12260:                     next if (($rem ne '') &&
                   12261:                              (($env{"httpref.$rem".$file} ne '') ||
                   12262:                               (ref($navmap) &&
                   12263:                               (($navmap->getResourceByUrl($rem.$file) ne '') ||
                   12264:                                (($file =~ /^(.*\.s?html?)\.bak$/i) &&
                   12265:                                 ($navmap->getResourceByUrl($rem.$1)))))));
                   12266:                 }
1.1085    raeburn  12267:             }
1.1071    raeburn  12268:             $unused{$file} = 1;
                   12269:         }
                   12270:     }
1.1249    damieng  12271:     
                   12272:     # returns some results for coursedocs paste and syllabus rewrites ($output is undef)
1.1084    raeburn  12273:     if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
                   12274:         ($args->{'context'} eq 'paste')) {
                   12275:         $counter = scalar(keys(%existing));
                   12276:         $numpathchg = scalar(keys(%pathchanges));
1.1123    raeburn  12277:         return ($output,$counter,$numpathchg,\%existing);
                   12278:     } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") && 
                   12279:              (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {
                   12280:         $counter = scalar(keys(%existing));
                   12281:         $numpathchg = scalar(keys(%pathchanges));
                   12282:         return ($output,$counter,$numpathchg,\%existing,\%mapping);
1.1084    raeburn  12283:     }
1.1249    damieng  12284:     
                   12285:     # returns HTML otherwise, with dependency results and to ask for more uploads
                   12286:     
                   12287:     # $upload_output: missing dependencies (with upload form)
                   12288:     # $modify_output: uploaded dependencies (in use)
                   12289:     # $delete_output: files no longer in use (unused files are not listed for londocs, bug?)
1.984     raeburn  12290:     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
1.1071    raeburn  12291:         if ($actionurl eq '/adm/dependencies') {
                   12292:             next if ($embed_file =~ m{^\w+://});
                   12293:         }
1.660     raeburn  12294:         $upload_output .= &start_data_table_row().
1.1123    raeburn  12295:                           '<td valign="top"><img src="'.&icon($embed_file).'" />&nbsp;'.
1.1071    raeburn  12296:                           '<span class="LC_filename">'.$embed_file.'</span>';
1.987     raeburn  12297:         unless ($mapping{$embed_file} eq $embed_file) {
1.1123    raeburn  12298:             $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.
                   12299:                               &mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
1.987     raeburn  12300:         }
1.1123    raeburn  12301:         $upload_output .= '</td>';
1.1071    raeburn  12302:         if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) { 
1.1123    raeburn  12303:             $upload_output.='<td align="right">'.
                   12304:                             '<span class="LC_info LC_fontsize_medium">'.
                   12305:                             &mt("URL points to web address").'</span>';
1.987     raeburn  12306:             $numremref++;
1.660     raeburn  12307:         } elsif ($args->{'error_on_invalid_names'}
                   12308:             && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
1.1123    raeburn  12309:             $upload_output.='<td align="right"><span class="LC_warning">'.
                   12310:                             &mt('Invalid characters').'</span>';
1.987     raeburn  12311:             $numinvalid++;
1.660     raeburn  12312:         } else {
1.1123    raeburn  12313:             $upload_output .= '<td>'.
                   12314:                               &embedded_file_element('upload_embedded',$counter,
1.987     raeburn  12315:                                                      $embed_file,\%mapping,
1.1071    raeburn  12316:                                                      $allfiles,$codebase,'upload');
                   12317:             $counter ++;
                   12318:             $numnew ++;
1.987     raeburn  12319:         }
                   12320:         $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
                   12321:     }
                   12322:     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
1.1071    raeburn  12323:         if ($actionurl eq '/adm/dependencies') {
                   12324:             my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file);
                   12325:             $modify_output .= &start_data_table_row().
                   12326:                               '<td><a href="'.$path.'/'.$embed_file.'" style="text-decoration:none;">'.
                   12327:                               '<img src="'.&icon($embed_file).'" border="0" />'.
                   12328:                               '&nbsp;<span class="LC_filename">'.$embed_file.'</span></a></td>'.
                   12329:                               '<td>'.$size.'</td>'.
                   12330:                               '<td>'.$mtime.'</td>'.
                   12331:                               '<td><label><input type="checkbox" name="mod_upload_dep" '.
                   12332:                               'onclick="toggleBrowse('."'$counter'".')" id="mod_upload_dep_'.
                   12333:                               $counter.'" value="'.$counter.'" />'.&mt('Yes').'</label>'.
                   12334:                               '<div id="moduploaddep_'.$counter.'" style="display:none;">'.
                   12335:                               &embedded_file_element('upload_embedded',$counter,
                   12336:                                                      $embed_file,\%mapping,
                   12337:                                                      $allfiles,$codebase,'modify').
                   12338:                               '</div></td>'.
                   12339:                               &end_data_table_row()."\n";
                   12340:             $counter ++;
                   12341:         } else {
                   12342:             $upload_output .= &start_data_table_row().
1.1123    raeburn  12343:                               '<td valign="top"><img src="'.&icon($embed_file).'" />&nbsp;'.
                   12344:                               '<span class="LC_filename">'.$embed_file.'</span></td>'.
                   12345:                               '<td align="right"><span class="LC_info LC_fontsize_medium">'.&mt('Already exists').'</span></td>'.
1.1071    raeburn  12346:                               &Apache::loncommon::end_data_table_row()."\n";
                   12347:         }
                   12348:     }
                   12349:     my $delidx = $counter;
                   12350:     foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) {
                   12351:         my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile);
                   12352:         $delete_output .= &start_data_table_row().
                   12353:                           '<td><img src="'.&icon($oldfile).'" />'.
                   12354:                           '&nbsp;<span class="LC_filename">'.$oldfile.'</span></td>'.
                   12355:                           '<td>'.$size.'</td>'.
                   12356:                           '<td>'.$mtime.'</td>'.
                   12357:                           '<td><label><input type="checkbox" name="del_upload_dep" '.
                   12358:                           ' value="'.$delidx.'" />'.&mt('Yes').'</label>'.
                   12359:                           &embedded_file_element('upload_embedded',$delidx,
                   12360:                                                  $oldfile,\%mapping,$allfiles,
                   12361:                                                  $codebase,'delete').'</td>'.
                   12362:                           &end_data_table_row()."\n"; 
                   12363:         $numunused ++;
                   12364:         $delidx ++;
1.987     raeburn  12365:     }
                   12366:     if ($upload_output) {
                   12367:         $upload_output = &start_data_table().
                   12368:                          $upload_output.
                   12369:                          &end_data_table()."\n";
                   12370:     }
1.1071    raeburn  12371:     if ($modify_output) {
                   12372:         $modify_output = &start_data_table().
                   12373:                          &start_data_table_header_row().
                   12374:                          '<th>'.&mt('File').'</th>'.
                   12375:                          '<th>'.&mt('Size (KB)').'</th>'.
                   12376:                          '<th>'.&mt('Modified').'</th>'.
                   12377:                          '<th>'.&mt('Upload replacement?').'</th>'.
                   12378:                          &end_data_table_header_row().
                   12379:                          $modify_output.
                   12380:                          &end_data_table()."\n";
                   12381:     }
                   12382:     if ($delete_output) {
                   12383:         $delete_output = &start_data_table().
                   12384:                          &start_data_table_header_row().
                   12385:                          '<th>'.&mt('File').'</th>'.
                   12386:                          '<th>'.&mt('Size (KB)').'</th>'.
                   12387:                          '<th>'.&mt('Modified').'</th>'.
                   12388:                          '<th>'.&mt('Delete?').'</th>'.
                   12389:                          &end_data_table_header_row().
                   12390:                          $delete_output.
                   12391:                          &end_data_table()."\n";
                   12392:     }
1.987     raeburn  12393:     my $applies = 0;
                   12394:     if ($numremref) {
                   12395:         $applies ++;
                   12396:     }
                   12397:     if ($numinvalid) {
                   12398:         $applies ++;
                   12399:     }
                   12400:     if ($numexisting) {
                   12401:         $applies ++;
                   12402:     }
1.1071    raeburn  12403:     if ($counter || $numunused) {
1.987     raeburn  12404:         $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
                   12405:                   ' method="post" enctype="multipart/form-data">'."\n".
1.1071    raeburn  12406:                   $state.'<h3>'.$heading.'</h3>'; 
                   12407:         if ($actionurl eq '/adm/dependencies') {
                   12408:             if ($numnew) {
                   12409:                 $output .= '<h4>'.&mt('Missing dependencies').'</h4>'.
                   12410:                            '<p>'.&mt('The following files need to be uploaded.').'</p>'."\n".
                   12411:                            $upload_output.'<br />'."\n";
                   12412:             }
                   12413:             if ($numexisting) {
                   12414:                 $output .= '<h4>'.&mt('Uploaded dependencies (in use)').'</h4>'.
                   12415:                            '<p>'.&mt('Upload a new file to replace the one currently in use.').'</p>'."\n".
                   12416:                            $modify_output.'<br />'."\n";
                   12417:                            $buttontext = &mt('Save changes');
                   12418:             }
                   12419:             if ($numunused) {
                   12420:                 $output .= '<h4>'.&mt('Unused files').'</h4>'.
                   12421:                            '<p>'.&mt('The following uploaded files are no longer used.').'</p>'."\n".
                   12422:                            $delete_output.'<br />'."\n";
                   12423:                            $buttontext = &mt('Save changes');
                   12424:             }
                   12425:         } else {
                   12426:             $output .= $upload_output.'<br />'."\n";
                   12427:         }
                   12428:         $output .= '<input type ="hidden" name="number_embedded_items" value="'.
                   12429:                    $counter.'" />'."\n";
                   12430:         if ($actionurl eq '/adm/dependencies') { 
                   12431:             $output .= '<input type ="hidden" name="number_newemb_items" value="'.
                   12432:                        $numnew.'" />'."\n";
                   12433:         } elsif ($actionurl eq '') {
1.987     raeburn  12434:             $output .=  '<input type="hidden" name="phase" value="three" />';
                   12435:         }
                   12436:     } elsif ($applies) {
                   12437:         $output = '<b>'.&mt('Referenced files').'</b>:<br />';
                   12438:         if ($applies > 1) {
                   12439:             $output .=  
1.1123    raeburn  12440:                 &mt('No dependencies need to be uploaded, as one of the following applies to each reference:').'<ul>';
1.987     raeburn  12441:             if ($numremref) {
                   12442:                 $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
                   12443:             }
                   12444:             if ($numinvalid) {
                   12445:                 $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
                   12446:             }
                   12447:             if ($numexisting) {
                   12448:                 $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
                   12449:             }
                   12450:             $output .= '</ul><br />';
                   12451:         } elsif ($numremref) {
                   12452:             $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
                   12453:         } elsif ($numinvalid) {
                   12454:             $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
                   12455:         } elsif ($numexisting) {
                   12456:             $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
                   12457:         }
                   12458:         $output .= $upload_output.'<br />';
                   12459:     }
                   12460:     my ($pathchange_output,$chgcount);
1.1071    raeburn  12461:     $chgcount = $counter;
1.987     raeburn  12462:     if (keys(%pathchanges) > 0) {
                   12463:         foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
1.1071    raeburn  12464:             if ($counter) {
1.987     raeburn  12465:                 $output .= &embedded_file_element('pathchange',$chgcount,
                   12466:                                                   $embed_file,\%mapping,
1.1071    raeburn  12467:                                                   $allfiles,$codebase,'change');
1.987     raeburn  12468:             } else {
                   12469:                 $pathchange_output .= 
                   12470:                     &start_data_table_row().
                   12471:                     '<td><input type ="checkbox" name="namechange" value="'.
                   12472:                     $chgcount.'" checked="checked" /></td>'.
                   12473:                     '<td>'.$mapping{$embed_file}.'</td>'.
                   12474:                     '<td>'.$embed_file.
                   12475:                     &embedded_file_element('pathchange',$numpathchg,$embed_file,
1.1071    raeburn  12476:                                            \%mapping,$allfiles,$codebase,'change').
1.987     raeburn  12477:                     '</td>'.&end_data_table_row();
1.660     raeburn  12478:             }
1.987     raeburn  12479:             $numpathchg ++;
                   12480:             $chgcount ++;
1.660     raeburn  12481:         }
                   12482:     }
1.1127    raeburn  12483:     if (($counter) || ($numunused)) {
1.987     raeburn  12484:         if ($numpathchg) {
                   12485:             $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
                   12486:                        $numpathchg.'" />'."\n";
                   12487:         }
                   12488:         if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') || 
                   12489:             ($actionurl eq '/adm/imsimport')) {
                   12490:             $output .= '<input type="hidden" name="phase" value="three" />'."\n";
                   12491:         } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
                   12492:             $output .= '<input type="hidden" name="action" value="upload_embedded" />';
1.1071    raeburn  12493:         } elsif ($actionurl eq '/adm/dependencies') {
                   12494:             $output .= '<input type="hidden" name="action" value="process_changes" />';
1.987     raeburn  12495:         }
1.1123    raeburn  12496:         $output .= '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n";
1.987     raeburn  12497:     } elsif ($numpathchg) {
                   12498:         my %pathchange = ();
                   12499:         $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
                   12500:         if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
                   12501:             $output .= '<p>'.&mt('or').'</p>'; 
1.1123    raeburn  12502:         }
1.987     raeburn  12503:     }
1.1071    raeburn  12504:     return ($output,$counter,$numpathchg);
1.987     raeburn  12505: }
                   12506: 
1.1147    raeburn  12507: =pod
                   12508: 
                   12509: =item * clean_path($name)
                   12510: 
                   12511: Performs clean-up of directories, subdirectories and filename in an
                   12512: embedded object, referenced in an HTML file which is being uploaded
                   12513: to a course or portfolio, where 
                   12514: "Upload embedded images/multimedia files if HTML file" checkbox was
                   12515: checked.
                   12516: 
                   12517: Clean-up is similar to replacements in lonnet::clean_filename()
                   12518: except each / between sub-directory and next level is preserved.
                   12519: 
                   12520: =cut
                   12521: 
                   12522: sub clean_path {
                   12523:     my ($embed_file) = @_;
                   12524:     $embed_file =~s{^/+}{};
                   12525:     my @contents;
                   12526:     if ($embed_file =~ m{/}) {
                   12527:         @contents = split(/\//,$embed_file);
                   12528:     } else {
                   12529:         @contents = ($embed_file);
                   12530:     }
                   12531:     my $lastidx = scalar(@contents)-1;
                   12532:     for (my $i=0; $i<=$lastidx; $i++) { 
                   12533:         $contents[$i]=~s{\\}{/}g;
                   12534:         $contents[$i]=~s/\s+/\_/g;
                   12535:         $contents[$i]=~s{[^/\w\.\-]}{}g;
                   12536:         if ($i == $lastidx) {
                   12537:             $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g;
                   12538:         }
                   12539:     }
                   12540:     if ($lastidx > 0) {
                   12541:         return join('/',@contents);
                   12542:     } else {
                   12543:         return $contents[0];
                   12544:     }
                   12545: }
                   12546: 
1.987     raeburn  12547: sub embedded_file_element {
1.1071    raeburn  12548:     my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
1.987     raeburn  12549:     return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
                   12550:                    (ref($codebase) eq 'HASH'));
                   12551:     my $output;
1.1071    raeburn  12552:     if (($context eq 'upload_embedded') && ($type ne 'delete')) {
1.987     raeburn  12553:        $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
                   12554:     }
                   12555:     $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
                   12556:                &escape($embed_file).'" />';
                   12557:     unless (($context eq 'upload_embedded') && 
                   12558:             ($mapping->{$embed_file} eq $embed_file)) {
                   12559:         $output .='
                   12560:         <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
                   12561:     }
                   12562:     my $attrib;
                   12563:     if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
                   12564:         $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
                   12565:     }
                   12566:     $output .=
                   12567:         "\n\t\t".
                   12568:         '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
                   12569:         $attrib.'" />';
                   12570:     if (exists($codebase->{$mapping->{$embed_file}})) {
                   12571:         $output .=
                   12572:             "\n\t\t".
                   12573:             '<input name="codebase_'.$num.'" type="hidden" value="'.
                   12574:             &escape($codebase->{$mapping->{$embed_file}}).'" />';
1.984     raeburn  12575:     }
1.987     raeburn  12576:     return $output;
1.660     raeburn  12577: }
                   12578: 
1.1071    raeburn  12579: sub get_dependency_details {
                   12580:     my ($currfile,$currsubfile,$embed_file) = @_;
                   12581:     my ($size,$mtime,$showsize,$showmtime);
                   12582:     if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
                   12583:         if ($embed_file =~ m{/}) {
                   12584:             my ($path,$fname) = split(/\//,$embed_file);
                   12585:             if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
                   12586:                 ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
                   12587:             }
                   12588:         } else {
                   12589:             if (ref($currfile->{$embed_file}) eq 'ARRAY') {
                   12590:                 ($size,$mtime) = @{$currfile->{$embed_file}};
                   12591:             }
                   12592:         }
                   12593:         $showsize = $size/1024.0;
                   12594:         $showsize = sprintf("%.1f",$showsize);
                   12595:         if ($mtime > 0) {
                   12596:             $showmtime = &Apache::lonlocal::locallocaltime($mtime);
                   12597:         }
                   12598:     }
                   12599:     return ($showsize,$showmtime);
                   12600: }
                   12601: 
                   12602: sub ask_embedded_js {
                   12603:     return <<"END";
                   12604: <script type="text/javascript"">
                   12605: // <![CDATA[
                   12606: function toggleBrowse(counter) {
                   12607:     var chkboxid = document.getElementById('mod_upload_dep_'+counter);
                   12608:     var fileid = document.getElementById('embedded_item_'+counter);
                   12609:     var uploaddivid = document.getElementById('moduploaddep_'+counter);
                   12610:     if (chkboxid.checked == true) {
                   12611:         uploaddivid.style.display='block';
                   12612:     } else {
                   12613:         uploaddivid.style.display='none';
                   12614:         fileid.value = '';
                   12615:     }
                   12616: }
                   12617: // ]]>
                   12618: </script>
                   12619: 
                   12620: END
                   12621: }
                   12622: 
1.661     raeburn  12623: sub upload_embedded {
                   12624:     my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
1.987     raeburn  12625:         $current_disk_usage,$hiddenstate,$actionurl) = @_;
                   12626:     my (%pathchange,$output,$modifyform,$footer,$returnflag);
1.661     raeburn  12627:     for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
                   12628:         next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
                   12629:         my $orig_uploaded_filename =
                   12630:             $env{'form.embedded_item_'.$i.'.filename'};
1.987     raeburn  12631:         foreach my $type ('orig','ref','attrib','codebase') {
                   12632:             if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
                   12633:                 $env{'form.embedded_'.$type.'_'.$i} =
                   12634:                     &unescape($env{'form.embedded_'.$type.'_'.$i});
                   12635:             }
                   12636:         }
1.661     raeburn  12637:         my ($path,$fname) =
                   12638:             ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
                   12639:         # no path, whole string is fname
                   12640:         if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
                   12641:         $fname = &Apache::lonnet::clean_filename($fname);
                   12642:         # See if there is anything left
                   12643:         next if ($fname eq '');
                   12644: 
                   12645:         # Check if file already exists as a file or directory.
                   12646:         my ($state,$msg);
                   12647:         if ($context eq 'portfolio') {
                   12648:             my $port_path = $dirpath;
                   12649:             if ($group ne '') {
                   12650:                 $port_path = "groups/$group/$port_path";
                   12651:             }
1.987     raeburn  12652:             ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
                   12653:                                               $fname,$group,'embedded_item_'.$i,
1.661     raeburn  12654:                                               $dir_root,$port_path,$disk_quota,
                   12655:                                               $current_disk_usage,$uname,$udom);
                   12656:             if ($state eq 'will_exceed_quota'
1.984     raeburn  12657:                 || $state eq 'file_locked') {
1.661     raeburn  12658:                 $output .= $msg;
                   12659:                 next;
                   12660:             }
                   12661:         } elsif (($context eq 'author') || ($context eq 'testbank')) {
                   12662:             ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
                   12663:             if ($state eq 'exists') {
                   12664:                 $output .= $msg;
                   12665:                 next;
                   12666:             }
                   12667:         }
                   12668:         # Check if extension is valid
                   12669:         if (($fname =~ /\.(\w+)$/) &&
                   12670:             (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
1.1155    bisitz   12671:             $output .= &mt('Invalid file extension ([_1]) - reserved for internal use.',$1)
                   12672:                       .' '.&mt('Rename the file with a different extension and re-upload.').'<br />';
1.661     raeburn  12673:             next;
                   12674:         } elsif (($fname =~ /\.(\w+)$/) &&
                   12675:                  (!defined(&Apache::loncommon::fileembstyle($1)))) {
1.987     raeburn  12676:             $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
1.661     raeburn  12677:             next;
                   12678:         } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
1.1120    bisitz   12679:             $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  12680:             next;
                   12681:         }
                   12682:         $env{'form.embedded_item_'.$i.'.filename'}=$fname;
1.1123    raeburn  12683:         my $subdir = $path;
                   12684:         $subdir =~ s{/+$}{};
1.661     raeburn  12685:         if ($context eq 'portfolio') {
1.984     raeburn  12686:             my $result;
                   12687:             if ($state eq 'existingfile') {
                   12688:                 $result=
                   12689:                     &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
1.1123    raeburn  12690:                                                     $dirpath.$env{'form.currentpath'}.$subdir);
1.661     raeburn  12691:             } else {
1.984     raeburn  12692:                 $result=
                   12693:                     &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
1.987     raeburn  12694:                                                     $dirpath.
1.1123    raeburn  12695:                                                     $env{'form.currentpath'}.$subdir);
1.984     raeburn  12696:                 if ($result !~ m|^/uploaded/|) {
                   12697:                     $output .= '<span class="LC_error">'
                   12698:                                .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
                   12699:                                ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
                   12700:                                .'</span><br />';
                   12701:                     next;
                   12702:                 } else {
1.987     raeburn  12703:                     $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   12704:                                $path.$fname.'</span>').'<br />';     
1.984     raeburn  12705:                 }
1.661     raeburn  12706:             }
1.1123    raeburn  12707:         } elsif (($context eq 'coursedoc') || ($context eq 'syllabus')) {
1.1126    raeburn  12708:             my $extendedsubdir = $dirpath.'/'.$subdir;
                   12709:             $extendedsubdir =~ s{/+$}{};
1.987     raeburn  12710:             my $result =
1.1126    raeburn  12711:                 &Apache::lonnet::userfileupload('embedded_item_'.$i,$context,$extendedsubdir);
1.987     raeburn  12712:             if ($result !~ m|^/uploaded/|) {
                   12713:                 $output .= '<span class="LC_error">'
                   12714:                            .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
                   12715:                            ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
                   12716:                            .'</span><br />';
                   12717:                     next;
                   12718:             } else {
                   12719:                 $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   12720:                            $path.$fname.'</span>').'<br />';
1.1125    raeburn  12721:                 if ($context eq 'syllabus') {
                   12722:                     &Apache::lonnet::make_public_indefinitely($result);
                   12723:                 }
1.987     raeburn  12724:             }
1.661     raeburn  12725:         } else {
                   12726: # Save the file
                   12727:             my $target = $env{'form.embedded_item_'.$i};
                   12728:             my $fullpath = $dir_root.$dirpath.'/'.$path;
                   12729:             my $dest = $fullpath.$fname;
                   12730:             my $url = $url_root.$dirpath.'/'.$path.$fname;
1.1027    raeburn  12731:             my @parts=split(/\//,"$dirpath/$path");
1.661     raeburn  12732:             my $count;
                   12733:             my $filepath = $dir_root;
1.1027    raeburn  12734:             foreach my $subdir (@parts) {
                   12735:                 $filepath .= "/$subdir";
                   12736:                 if (!-e $filepath) {
1.661     raeburn  12737:                     mkdir($filepath,0770);
                   12738:                 }
                   12739:             }
                   12740:             my $fh;
                   12741:             if (!open($fh,'>'.$dest)) {
                   12742:                 &Apache::lonnet::logthis('Failed to create '.$dest);
                   12743:                 $output .= '<span class="LC_error">'.
1.1071    raeburn  12744:                            &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
                   12745:                                $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661     raeburn  12746:                            '</span><br />';
                   12747:             } else {
                   12748:                 if (!print $fh $env{'form.embedded_item_'.$i}) {
                   12749:                     &Apache::lonnet::logthis('Failed to write to '.$dest);
                   12750:                     $output .= '<span class="LC_error">'.
1.1071    raeburn  12751:                               &mt('An error occurred while writing the file [_1] for embedded element [_2].',
                   12752:                                   $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661     raeburn  12753:                               '</span><br />';
                   12754:                 } else {
1.987     raeburn  12755:                     $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
                   12756:                                $url.'</span>').'<br />';
                   12757:                     unless ($context eq 'testbank') {
                   12758:                         $footer .= &mt('View embedded file: [_1]',
                   12759:                                        '<a href="'.$url.'">'.$fname.'</a>').'<br />';
                   12760:                     }
                   12761:                 }
                   12762:                 close($fh);
                   12763:             }
                   12764:         }
                   12765:         if ($env{'form.embedded_ref_'.$i}) {
                   12766:             $pathchange{$i} = 1;
                   12767:         }
                   12768:     }
                   12769:     if ($output) {
                   12770:         $output = '<p>'.$output.'</p>';
                   12771:     }
                   12772:     $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
                   12773:     $returnflag = 'ok';
1.1071    raeburn  12774:     my $numpathchgs = scalar(keys(%pathchange));
                   12775:     if ($numpathchgs > 0) {
1.987     raeburn  12776:         if ($context eq 'portfolio') {
                   12777:             $output .= '<p>'.&mt('or').'</p>';
                   12778:         } elsif ($context eq 'testbank') {
1.1071    raeburn  12779:             $output .=  '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
                   12780:                                   '<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
1.987     raeburn  12781:             $returnflag = 'modify_orightml';
                   12782:         }
                   12783:     }
1.1071    raeburn  12784:     return ($output.$footer,$returnflag,$numpathchgs);
1.987     raeburn  12785: }
                   12786: 
                   12787: sub modify_html_form {
                   12788:     my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
                   12789:     my $end = 0;
                   12790:     my $modifyform;
                   12791:     if ($context eq 'upload_embedded') {
                   12792:         return unless (ref($pathchange) eq 'HASH');
                   12793:         if ($env{'form.number_embedded_items'}) {
                   12794:             $end += $env{'form.number_embedded_items'};
                   12795:         }
                   12796:         if ($env{'form.number_pathchange_items'}) {
                   12797:             $end += $env{'form.number_pathchange_items'};
                   12798:         }
                   12799:         if ($end) {
                   12800:             for (my $i=0; $i<$end; $i++) {
                   12801:                 if ($i < $env{'form.number_embedded_items'}) {
                   12802:                     next unless($pathchange->{$i});
                   12803:                 }
                   12804:                 $modifyform .=
                   12805:                     &start_data_table_row().
                   12806:                     '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
                   12807:                     'checked="checked" /></td>'.
                   12808:                     '<td>'.$env{'form.embedded_ref_'.$i}.
                   12809:                     '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
                   12810:                     &escape($env{'form.embedded_ref_'.$i}).'" />'.
                   12811:                     '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
                   12812:                     &escape($env{'form.embedded_codebase_'.$i}).'" />'.
                   12813:                     '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
                   12814:                     &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
                   12815:                     '<td>'.$env{'form.embedded_orig_'.$i}.
                   12816:                     '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
                   12817:                     &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
                   12818:                     &end_data_table_row();
1.1071    raeburn  12819:             }
1.987     raeburn  12820:         }
                   12821:     } else {
                   12822:         $modifyform = $pathchgtable;
                   12823:         if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
                   12824:             $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
                   12825:         } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
                   12826:             $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
                   12827:         }
                   12828:     }
                   12829:     if ($modifyform) {
1.1071    raeburn  12830:         if ($actionurl eq '/adm/dependencies') {
                   12831:             $hiddenstate .= '<input type="hidden" name="action" value="modifyhrefs" />';
                   12832:         }
1.987     raeburn  12833:         return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
                   12834:                '<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".
                   12835:                '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
                   12836:                '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
                   12837:                '</ol></p>'."\n".'<p>'.
                   12838:                &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
                   12839:                '<form method="post" name="refchanger" action="'.$actionurl.'">'.
                   12840:                &start_data_table()."\n".
                   12841:                &start_data_table_header_row().
                   12842:                '<th>'.&mt('Change?').'</th>'.
                   12843:                '<th>'.&mt('Current reference').'</th>'.
                   12844:                '<th>'.&mt('Required reference').'</th>'.
                   12845:                &end_data_table_header_row()."\n".
                   12846:                $modifyform.
                   12847:                &end_data_table().'<br />'."\n".$hiddenstate.
                   12848:                '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
                   12849:                '</form>'."\n";
                   12850:     }
                   12851:     return;
                   12852: }
                   12853: 
                   12854: sub modify_html_refs {
1.1123    raeburn  12855:     my ($context,$dirpath,$uname,$udom,$dir_root,$url) = @_;
1.987     raeburn  12856:     my $container;
                   12857:     if ($context eq 'portfolio') {
                   12858:         $container = $env{'form.container'};
                   12859:     } elsif ($context eq 'coursedoc') {
                   12860:         $container = $env{'form.primaryurl'};
1.1071    raeburn  12861:     } elsif ($context eq 'manage_dependencies') {
                   12862:         (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
                   12863:         $container = "/$container";
1.1123    raeburn  12864:     } elsif ($context eq 'syllabus') {
                   12865:         $container = $url;
1.987     raeburn  12866:     } else {
1.1027    raeburn  12867:         $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
1.987     raeburn  12868:     }
                   12869:     my (%allfiles,%codebase,$output,$content);
                   12870:     my @changes = &get_env_multiple('form.namechange');
1.1126    raeburn  12871:     unless ((@changes > 0) || ($context eq 'syllabus')) {
1.1071    raeburn  12872:         if (wantarray) {
                   12873:             return ('',0,0); 
                   12874:         } else {
                   12875:             return;
                   12876:         }
                   12877:     }
                   12878:     if (($context eq 'portfolio') || ($context eq 'coursedoc') || 
1.1123    raeburn  12879:         ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.1071    raeburn  12880:         unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
                   12881:             if (wantarray) {
                   12882:                 return ('',0,0);
                   12883:             } else {
                   12884:                 return;
                   12885:             }
                   12886:         } 
1.987     raeburn  12887:         $content = &Apache::lonnet::getfile($container);
1.1071    raeburn  12888:         if ($content eq '-1') {
                   12889:             if (wantarray) {
                   12890:                 return ('',0,0);
                   12891:             } else {
                   12892:                 return;
                   12893:             }
                   12894:         }
1.987     raeburn  12895:     } else {
1.1071    raeburn  12896:         unless ($container =~ /^\Q$dir_root\E/) {
                   12897:             if (wantarray) {
                   12898:                 return ('',0,0);
                   12899:             } else {
                   12900:                 return;
                   12901:             }
                   12902:         } 
1.1317    raeburn  12903:         if (open(my $fh,'<',$container)) {
1.987     raeburn  12904:             $content = join('', <$fh>);
                   12905:             close($fh);
                   12906:         } else {
1.1071    raeburn  12907:             if (wantarray) {
                   12908:                 return ('',0,0);
                   12909:             } else {
                   12910:                 return;
                   12911:             }
1.987     raeburn  12912:         }
                   12913:     }
                   12914:     my ($count,$codebasecount) = (0,0);
                   12915:     my $mm = new File::MMagic;
                   12916:     my $mime_type = $mm->checktype_contents($content);
                   12917:     if ($mime_type eq 'text/html') {
                   12918:         my $parse_result = 
                   12919:             &Apache::lonnet::extract_embedded_items($container,\%allfiles,
                   12920:                                                     \%codebase,\$content);
                   12921:         if ($parse_result eq 'ok') {
                   12922:             foreach my $i (@changes) {
                   12923:                 my $orig = &unescape($env{'form.embedded_orig_'.$i});
                   12924:                 my $ref = &unescape($env{'form.embedded_ref_'.$i});
                   12925:                 if ($allfiles{$ref}) {
                   12926:                     my $newname =  $orig;
                   12927:                     my ($attrib_regexp,$codebase);
1.1006    raeburn  12928:                     $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
1.987     raeburn  12929:                     if ($attrib_regexp =~ /:/) {
                   12930:                         $attrib_regexp =~ s/\:/|/g;
                   12931:                     }
                   12932:                     if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
                   12933:                         my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
                   12934:                         $count += $numchg;
1.1123    raeburn  12935:                         $allfiles{$newname} = $allfiles{$ref};
1.1148    raeburn  12936:                         delete($allfiles{$ref});
1.987     raeburn  12937:                     }
                   12938:                     if ($env{'form.embedded_codebase_'.$i} ne '') {
1.1006    raeburn  12939:                         $codebase = &unescape($env{'form.embedded_codebase_'.$i});
1.987     raeburn  12940:                         my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
                   12941:                         $codebasecount ++;
                   12942:                     }
                   12943:                 }
                   12944:             }
1.1123    raeburn  12945:             my $skiprewrites;
1.987     raeburn  12946:             if ($count || $codebasecount) {
                   12947:                 my $saveresult;
1.1071    raeburn  12948:                 if (($context eq 'portfolio') || ($context eq 'coursedoc') || 
1.1123    raeburn  12949:                     ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.987     raeburn  12950:                     my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
                   12951:                     if ($url eq $container) {
                   12952:                         my ($fname) = ($container =~ m{/([^/]+)$});
                   12953:                         $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
                   12954:                                             $count,'<span class="LC_filename">'.
1.1071    raeburn  12955:                                             $fname.'</span>').'</p>';
1.987     raeburn  12956:                     } else {
                   12957:                          $output = '<p class="LC_error">'.
                   12958:                                    &mt('Error: update failed for: [_1].',
                   12959:                                    '<span class="LC_filename">'.
                   12960:                                    $container.'</span>').'</p>';
                   12961:                     }
1.1123    raeburn  12962:                     if ($context eq 'syllabus') {
                   12963:                         unless ($saveresult eq 'ok') {
                   12964:                             $skiprewrites = 1;
                   12965:                         }
                   12966:                     }
1.987     raeburn  12967:                 } else {
1.1317    raeburn  12968:                     if (open(my $fh,'>',$container)) {
1.987     raeburn  12969:                         print $fh $content;
                   12970:                         close($fh);
                   12971:                         $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
                   12972:                                   $count,'<span class="LC_filename">'.
                   12973:                                   $container.'</span>').'</p>';
1.661     raeburn  12974:                     } else {
1.987     raeburn  12975:                          $output = '<p class="LC_error">'.
                   12976:                                    &mt('Error: could not update [_1].',
                   12977:                                    '<span class="LC_filename">'.
                   12978:                                    $container.'</span>').'</p>';
1.661     raeburn  12979:                     }
                   12980:                 }
                   12981:             }
1.1123    raeburn  12982:             if (($context eq 'syllabus') && (!$skiprewrites)) {
                   12983:                 my ($actionurl,$state);
                   12984:                 $actionurl = "/public/$udom/$uname/syllabus";
                   12985:                 my ($ignore,$num,$numpathchanges,$existing,$mapping) =
                   12986:                     &ask_for_embedded_content($actionurl,$state,\%allfiles,
                   12987:                                               \%codebase,
                   12988:                                               {'context' => 'rewrites',
                   12989:                                                'ignore_remote_references' => 1,});
                   12990:                 if (ref($mapping) eq 'HASH') {
                   12991:                     my $rewrites = 0;
                   12992:                     foreach my $key (keys(%{$mapping})) {
                   12993:                         next if ($key =~ m{^https?://});
                   12994:                         my $ref = $mapping->{$key};
                   12995:                         my $newname = "/uploaded/$udom/$uname/portfolio/syllabus/$key";
                   12996:                         my $attrib;
                   12997:                         if (ref($allfiles{$mapping->{$key}}) eq 'ARRAY') {
                   12998:                             $attrib = join('|',@{$allfiles{$mapping->{$key}}});
                   12999:                         }
                   13000:                         if ($content =~ m{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
                   13001:                             my $numchg = ($content =~ s{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
                   13002:                             $rewrites += $numchg;
                   13003:                         }
                   13004:                     }
                   13005:                     if ($rewrites) {
                   13006:                         my $saveresult; 
                   13007:                         my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
                   13008:                         if ($url eq $container) {
                   13009:                             my ($fname) = ($container =~ m{/([^/]+)$});
                   13010:                             $output .= '<p>'.&mt('Rewrote [quant,_1,link] as [quant,_1,absolute link] in [_2].',
                   13011:                                             $count,'<span class="LC_filename">'.
                   13012:                                             $fname.'</span>').'</p>';
                   13013:                         } else {
                   13014:                             $output .= '<p class="LC_error">'.
                   13015:                                        &mt('Error: could not update links in [_1].',
                   13016:                                        '<span class="LC_filename">'.
                   13017:                                        $container.'</span>').'</p>';
                   13018: 
                   13019:                         }
                   13020:                     }
                   13021:                 }
                   13022:             }
1.987     raeburn  13023:         } else {
                   13024:             &logthis('Failed to parse '.$container.
                   13025:                      ' to modify references: '.$parse_result);
1.661     raeburn  13026:         }
                   13027:     }
1.1071    raeburn  13028:     if (wantarray) {
                   13029:         return ($output,$count,$codebasecount);
                   13030:     } else {
                   13031:         return $output;
                   13032:     }
1.661     raeburn  13033: }
                   13034: 
                   13035: sub check_for_existing {
                   13036:     my ($path,$fname,$element) = @_;
                   13037:     my ($state,$msg);
                   13038:     if (-d $path.'/'.$fname) {
                   13039:         $state = 'exists';
                   13040:         $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   13041:     } elsif (-e $path.'/'.$fname) {
                   13042:         $state = 'exists';
                   13043:         $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
                   13044:     }
                   13045:     if ($state eq 'exists') {
                   13046:         $msg = '<span class="LC_error">'.$msg.'</span><br />';
                   13047:     }
                   13048:     return ($state,$msg);
                   13049: }
                   13050: 
                   13051: sub check_for_upload {
                   13052:     my ($path,$fname,$group,$element,$portfolio_root,$port_path,
                   13053:         $disk_quota,$current_disk_usage,$uname,$udom) = @_;
1.985     raeburn  13054:     my $filesize = length($env{'form.'.$element});
                   13055:     if (!$filesize) {
                   13056:         my $msg = '<span class="LC_error">'.
                   13057:                   &mt('Unable to upload [_1]. (size = [_2] bytes)', 
                   13058:                       '<span class="LC_filename">'.$fname.'</span>',
                   13059:                       $filesize).'<br />'.
1.1007    raeburn  13060:                   &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').'<br />'.
1.985     raeburn  13061:                   '</span>';
                   13062:         return ('zero_bytes',$msg);
                   13063:     }
                   13064:     $filesize =  $filesize/1000; #express in k (1024?)
1.661     raeburn  13065:     my $getpropath = 1;
1.1021    raeburn  13066:     my ($dirlistref,$listerror) =
                   13067:          &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
1.661     raeburn  13068:     my $found_file = 0;
                   13069:     my $locked_file = 0;
1.991     raeburn  13070:     my @lockers;
                   13071:     my $navmap;
                   13072:     if ($env{'request.course.id'}) {
                   13073:         $navmap = Apache::lonnavmaps::navmap->new();
                   13074:     }
1.1021    raeburn  13075:     if (ref($dirlistref) eq 'ARRAY') {
                   13076:         foreach my $line (@{$dirlistref}) {
                   13077:             my ($file_name,$rest)=split(/\&/,$line,2);
                   13078:             if ($file_name eq $fname){
                   13079:                 $file_name = $path.$file_name;
                   13080:                 if ($group ne '') {
                   13081:                     $file_name = $group.$file_name;
                   13082:                 }
                   13083:                 $found_file = 1;
                   13084:                 if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
                   13085:                     foreach my $lock (@lockers) {
                   13086:                         if (ref($lock) eq 'ARRAY') {
                   13087:                             my ($symb,$crsid) = @{$lock};
                   13088:                             if ($crsid eq $env{'request.course.id'}) {
                   13089:                                 if (ref($navmap)) {
                   13090:                                     my $res = $navmap->getBySymb($symb);
                   13091:                                     foreach my $part (@{$res->parts()}) { 
                   13092:                                         my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
                   13093:                                         unless (($slot_status == $res->RESERVED) ||
                   13094:                                                 ($slot_status == $res->RESERVED_LOCATION)) {
                   13095:                                             $locked_file = 1;
                   13096:                                         }
1.991     raeburn  13097:                                     }
1.1021    raeburn  13098:                                 } else {
                   13099:                                     $locked_file = 1;
1.991     raeburn  13100:                                 }
                   13101:                             } else {
                   13102:                                 $locked_file = 1;
                   13103:                             }
                   13104:                         }
1.1021    raeburn  13105:                    }
                   13106:                 } else {
                   13107:                     my @info = split(/\&/,$rest);
                   13108:                     my $currsize = $info[6]/1000;
                   13109:                     if ($currsize < $filesize) {
                   13110:                         my $extra = $filesize - $currsize;
                   13111:                         if (($current_disk_usage + $extra) > $disk_quota) {
1.1179    bisitz   13112:                             my $msg = '<p class="LC_warning">'.
1.1021    raeburn  13113:                                       &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   13114:                                           '<span class="LC_filename">'.$fname.'</span>',$filesize,$currsize).'</p>'.
                   13115:                                       '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
                   13116:                                                    $disk_quota,$current_disk_usage).'</p>';
1.1021    raeburn  13117:                             return ('will_exceed_quota',$msg);
                   13118:                         }
1.984     raeburn  13119:                     }
                   13120:                 }
1.661     raeburn  13121:             }
                   13122:         }
                   13123:     }
                   13124:     if (($current_disk_usage + $filesize) > $disk_quota){
1.1179    bisitz   13125:         my $msg = '<p class="LC_warning">'.
                   13126:                 &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</p>'.
1.1184    raeburn  13127:                   '<p>'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage).'</p>';
1.661     raeburn  13128:         return ('will_exceed_quota',$msg);
                   13129:     } elsif ($found_file) {
                   13130:         if ($locked_file) {
1.1179    bisitz   13131:             my $msg = '<p class="LC_warning">';
1.661     raeburn  13132:             $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   13133:             $msg .= '</p>';
1.661     raeburn  13134:             $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
                   13135:             return ('file_locked',$msg);
                   13136:         } else {
1.1179    bisitz   13137:             my $msg = '<p class="LC_error">';
1.984     raeburn  13138:             $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   13139:             $msg .= '</p>';
1.984     raeburn  13140:             return ('existingfile',$msg);
1.661     raeburn  13141:         }
                   13142:     }
                   13143: }
                   13144: 
1.987     raeburn  13145: sub check_for_traversal {
                   13146:     my ($path,$url,$toplevel) = @_;
                   13147:     my @parts=split(/\//,$path);
                   13148:     my $cleanpath;
                   13149:     my $fullpath = $url;
                   13150:     for (my $i=0;$i<@parts;$i++) {
                   13151:         next if ($parts[$i] eq '.');
                   13152:         if ($parts[$i] eq '..') {
                   13153:             $fullpath =~ s{([^/]+/)$}{};
                   13154:         } else {
                   13155:             $fullpath .= $parts[$i].'/';
                   13156:         }
                   13157:     }
                   13158:     if ($fullpath =~ /^\Q$url\E(.*)$/) {
                   13159:         $cleanpath = $1;
                   13160:     } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
                   13161:         my $curr_toprel = $1;
                   13162:         my @parts = split(/\//,$curr_toprel);
                   13163:         my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
                   13164:         my @urlparts = split(/\//,$url_toprel);
                   13165:         my $doubledots;
                   13166:         my $startdiff = -1;
                   13167:         for (my $i=0; $i<@urlparts; $i++) {
                   13168:             if ($startdiff == -1) {
                   13169:                 unless ($urlparts[$i] eq $parts[$i]) {
                   13170:                     $startdiff = $i;
                   13171:                     $doubledots .= '../';
                   13172:                 }
                   13173:             } else {
                   13174:                 $doubledots .= '../';
                   13175:             }
                   13176:         }
                   13177:         if ($startdiff > -1) {
                   13178:             $cleanpath = $doubledots;
                   13179:             for (my $i=$startdiff; $i<@parts; $i++) {
                   13180:                 $cleanpath .= $parts[$i].'/';
                   13181:             }
                   13182:         }
                   13183:     }
                   13184:     $cleanpath =~ s{(/)$}{};
                   13185:     return $cleanpath;
                   13186: }
1.31      albertel 13187: 
1.1053    raeburn  13188: sub is_archive_file {
                   13189:     my ($mimetype) = @_;
                   13190:     if (($mimetype eq 'application/octet-stream') ||
                   13191:         ($mimetype eq 'application/x-stuffit') ||
                   13192:         ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
                   13193:         return 1;
                   13194:     }
                   13195:     return;
                   13196: }
                   13197: 
                   13198: sub decompress_form {
1.1065    raeburn  13199:     my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
1.1053    raeburn  13200:     my %lt = &Apache::lonlocal::texthash (
                   13201:         this => 'This file is an archive file.',
1.1067    raeburn  13202:         camt => 'This file is a Camtasia archive file.',
1.1065    raeburn  13203:         itsc => 'Its contents are as follows:',
1.1053    raeburn  13204:         youm => 'You may wish to extract its contents.',
                   13205:         extr => 'Extract contents',
1.1067    raeburn  13206:         auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
                   13207:         proa => 'Process automatically?',
1.1053    raeburn  13208:         yes  => 'Yes',
                   13209:         no   => 'No',
1.1067    raeburn  13210:         fold => 'Title for folder containing movie',
                   13211:         movi => 'Title for page containing embedded movie', 
1.1053    raeburn  13212:     );
1.1065    raeburn  13213:     my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
1.1067    raeburn  13214:     my ($is_camtasia,$topdir,%toplevel,@paths);
1.1065    raeburn  13215:     my $info = &list_archive_contents($fileloc,\@paths);
                   13216:     if (@paths) {
                   13217:         foreach my $path (@paths) {
                   13218:             $path =~ s{^/}{};
1.1067    raeburn  13219:             if ($path =~ m{^([^/]+)/$}) {
                   13220:                 $topdir = $1;
                   13221:             }
1.1065    raeburn  13222:             if ($path =~ m{^([^/]+)/}) {
                   13223:                 $toplevel{$1} = $path;
                   13224:             } else {
                   13225:                 $toplevel{$path} = $path;
                   13226:             }
                   13227:         }
                   13228:     }
1.1067    raeburn  13229:     if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
1.1164    raeburn  13230:         my @camtasia6 = ("$topdir/","$topdir/index.html",
1.1067    raeburn  13231:                         "$topdir/media/",
                   13232:                         "$topdir/media/$topdir.mp4",
                   13233:                         "$topdir/media/FirstFrame.png",
                   13234:                         "$topdir/media/player.swf",
                   13235:                         "$topdir/media/swfobject.js",
                   13236:                         "$topdir/media/expressInstall.swf");
1.1197    raeburn  13237:         my @camtasia8_1 = ("$topdir/","$topdir/$topdir.html",
1.1164    raeburn  13238:                          "$topdir/$topdir.mp4",
                   13239:                          "$topdir/$topdir\_config.xml",
                   13240:                          "$topdir/$topdir\_controller.swf",
                   13241:                          "$topdir/$topdir\_embed.css",
                   13242:                          "$topdir/$topdir\_First_Frame.png",
                   13243:                          "$topdir/$topdir\_player.html",
                   13244:                          "$topdir/$topdir\_Thumbnails.png",
                   13245:                          "$topdir/playerProductInstall.swf",
                   13246:                          "$topdir/scripts/",
                   13247:                          "$topdir/scripts/config_xml.js",
                   13248:                          "$topdir/scripts/handlebars.js",
                   13249:                          "$topdir/scripts/jquery-1.7.1.min.js",
                   13250:                          "$topdir/scripts/jquery-ui-1.8.15.custom.min.js",
                   13251:                          "$topdir/scripts/modernizr.js",
                   13252:                          "$topdir/scripts/player-min.js",
                   13253:                          "$topdir/scripts/swfobject.js",
                   13254:                          "$topdir/skins/",
                   13255:                          "$topdir/skins/configuration_express.xml",
                   13256:                          "$topdir/skins/express_show/",
                   13257:                          "$topdir/skins/express_show/player-min.css",
                   13258:                          "$topdir/skins/express_show/spritesheet.png");
1.1197    raeburn  13259:         my @camtasia8_4 = ("$topdir/","$topdir/$topdir.html",
                   13260:                          "$topdir/$topdir.mp4",
                   13261:                          "$topdir/$topdir\_config.xml",
                   13262:                          "$topdir/$topdir\_controller.swf",
                   13263:                          "$topdir/$topdir\_embed.css",
                   13264:                          "$topdir/$topdir\_First_Frame.png",
                   13265:                          "$topdir/$topdir\_player.html",
                   13266:                          "$topdir/$topdir\_Thumbnails.png",
                   13267:                          "$topdir/playerProductInstall.swf",
                   13268:                          "$topdir/scripts/",
                   13269:                          "$topdir/scripts/config_xml.js",
                   13270:                          "$topdir/scripts/techsmith-smart-player.min.js",
                   13271:                          "$topdir/skins/",
                   13272:                          "$topdir/skins/configuration_express.xml",
                   13273:                          "$topdir/skins/express_show/",
                   13274:                          "$topdir/skins/express_show/spritesheet.min.css",
                   13275:                          "$topdir/skins/express_show/spritesheet.png",
                   13276:                          "$topdir/skins/express_show/techsmith-smart-player.min.css");
1.1164    raeburn  13277:         my @diffs = &compare_arrays(\@paths,\@camtasia6);
1.1067    raeburn  13278:         if (@diffs == 0) {
1.1164    raeburn  13279:             $is_camtasia = 6;
                   13280:         } else {
1.1197    raeburn  13281:             @diffs = &compare_arrays(\@paths,\@camtasia8_1);
1.1164    raeburn  13282:             if (@diffs == 0) {
                   13283:                 $is_camtasia = 8;
1.1197    raeburn  13284:             } else {
                   13285:                 @diffs = &compare_arrays(\@paths,\@camtasia8_4);
                   13286:                 if (@diffs == 0) {
                   13287:                     $is_camtasia = 8;
                   13288:                 }
1.1164    raeburn  13289:             }
1.1067    raeburn  13290:         }
                   13291:     }
                   13292:     my $output;
                   13293:     if ($is_camtasia) {
                   13294:         $output = <<"ENDCAM";
                   13295: <script type="text/javascript" language="Javascript">
                   13296: // <![CDATA[
                   13297: 
                   13298: function camtasiaToggle() {
                   13299:     for (var i=0; i<document.uploaded_decompress.autoextract_camtasia.length; i++) {
                   13300:         if (document.uploaded_decompress.autoextract_camtasia[i].checked) {
1.1164    raeburn  13301:             if (document.uploaded_decompress.autoextract_camtasia[i].value == $is_camtasia) {
1.1067    raeburn  13302:                 document.getElementById('camtasia_titles').style.display='block';
                   13303:             } else {
                   13304:                 document.getElementById('camtasia_titles').style.display='none';
                   13305:             }
                   13306:         }
                   13307:     }
                   13308:     return;
                   13309: }
                   13310: 
                   13311: // ]]>
                   13312: </script>
                   13313: <p>$lt{'camt'}</p>
                   13314: ENDCAM
1.1065    raeburn  13315:     } else {
1.1067    raeburn  13316:         $output = '<p>'.$lt{'this'};
                   13317:         if ($info eq '') {
                   13318:             $output .= ' '.$lt{'youm'}.'</p>'."\n";
                   13319:         } else {
                   13320:             $output .= ' '.$lt{'itsc'}.'</p>'."\n".
                   13321:                        '<div><pre>'.$info.'</pre></div>';
                   13322:         }
1.1065    raeburn  13323:     }
1.1067    raeburn  13324:     $output .= '<form name="uploaded_decompress" action="'.$action.'" method="post">'."\n";
1.1065    raeburn  13325:     my $duplicates;
                   13326:     my $num = 0;
                   13327:     if (ref($dirlist) eq 'ARRAY') {
                   13328:         foreach my $item (@{$dirlist}) {
                   13329:             if (ref($item) eq 'ARRAY') {
                   13330:                 if (exists($toplevel{$item->[0]})) {
                   13331:                     $duplicates .= 
                   13332:                         &start_data_table_row().
                   13333:                         '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
                   13334:                         'value="0" checked="checked" />'.&mt('No').'</label>'.
                   13335:                         '&nbsp;<label><input type="radio" name="archive_overwrite_'.$num.'" '.
                   13336:                         'value="1" />'.&mt('Yes').'</label>'.
                   13337:                         '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
                   13338:                         '<td>'.$item->[0].'</td>';
                   13339:                     if ($item->[2]) {
                   13340:                         $duplicates .= '<td>'.&mt('Directory').'</td>';
                   13341:                     } else {
                   13342:                         $duplicates .= '<td>'.&mt('File').'</td>';
                   13343:                     }
                   13344:                     $duplicates .= '<td>'.$item->[3].'</td>'.
                   13345:                                    '<td>'.
                   13346:                                    &Apache::lonlocal::locallocaltime($item->[4]).
                   13347:                                    '</td>'.
                   13348:                                    &end_data_table_row();
                   13349:                     $num ++;
                   13350:                 }
                   13351:             }
                   13352:         }
                   13353:     }
                   13354:     my $itemcount;
                   13355:     if (@paths > 0) {
                   13356:         $itemcount = scalar(@paths);
                   13357:     } else {
                   13358:         $itemcount = 1;
                   13359:     }
1.1067    raeburn  13360:     if ($is_camtasia) {
                   13361:         $output .= $lt{'auto'}.'<br />'.
                   13362:                    '<span class="LC_nobreak">'.$lt{'proa'}.'<label>'.
1.1164    raeburn  13363:                    '<input type="radio" name="autoextract_camtasia" value="'.$is_camtasia.'" onclick="javascript:camtasiaToggle();" checked="checked" />'.
1.1067    raeburn  13364:                    $lt{'yes'}.'</label>&nbsp;<label>'.
                   13365:                    '<input type="radio" name="autoextract_camtasia" value="0" onclick="javascript:camtasiaToggle();" />'.
                   13366:                    $lt{'no'}.'</label></span><br />'.
                   13367:                    '<div id="camtasia_titles" style="display:block">'.
                   13368:                    &Apache::lonhtmlcommon::start_pick_box().
                   13369:                    &Apache::lonhtmlcommon::row_title($lt{'fold'}).
                   13370:                    '<input type="textbox" name="camtasia_foldername" value="'.$env{'form.comment'}.'" />'."\n".
                   13371:                    &Apache::lonhtmlcommon::row_closure().
                   13372:                    &Apache::lonhtmlcommon::row_title($lt{'movi'}).
                   13373:                    '<input type="textbox" name="camtasia_moviename" value="" />'."\n".
                   13374:                    &Apache::lonhtmlcommon::row_closure(1).
                   13375:                    &Apache::lonhtmlcommon::end_pick_box().
                   13376:                    '</div>';
                   13377:     }
1.1065    raeburn  13378:     $output .= 
                   13379:         '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
1.1067    raeburn  13380:         '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'.
                   13381:         "\n";
1.1065    raeburn  13382:     if ($duplicates ne '') {
                   13383:         $output .= '<p><span class="LC_warning">'.
                   13384:                    &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.  
                   13385:                    &start_data_table().
                   13386:                    &start_data_table_header_row().
                   13387:                    '<th>'.&mt('Overwrite?').'</th>'.
                   13388:                    '<th>'.&mt('Name').'</th>'.
                   13389:                    '<th>'.&mt('Type').'</th>'.
                   13390:                    '<th>'.&mt('Size').'</th>'.
                   13391:                    '<th>'.&mt('Last modified').'</th>'.
                   13392:                    &end_data_table_header_row().
                   13393:                    $duplicates.
                   13394:                    &end_data_table().
                   13395:                    '</p>';
                   13396:     }
1.1067    raeburn  13397:     $output .= '<input type="hidden" name="archiveurl" value="'.$archiveurl.'" />'."\n";
1.1053    raeburn  13398:     if (ref($hiddenelements) eq 'HASH') {
                   13399:         foreach my $hidden (sort(keys(%{$hiddenelements}))) {
                   13400:             $output .= '<input type="hidden" name="'.$hidden.'" value="'.$hiddenelements->{$hidden}.'" />'."\n";
                   13401:         }
                   13402:     }
                   13403:     $output .= <<"END";
1.1067    raeburn  13404: <br />
1.1053    raeburn  13405: <input type="submit" name="decompress" value="$lt{'extr'}" />
                   13406: </form>
                   13407: $noextract
                   13408: END
                   13409:     return $output;
                   13410: }
                   13411: 
1.1065    raeburn  13412: sub decompression_utility {
                   13413:     my ($program) = @_;
                   13414:     my @utilities = ('tar','gunzip','bunzip2','unzip'); 
                   13415:     my $location;
                   13416:     if (grep(/^\Q$program\E$/,@utilities)) { 
                   13417:         foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
                   13418:                          '/usr/sbin/') {
                   13419:             if (-x $dir.$program) {
                   13420:                 $location = $dir.$program;
                   13421:                 last;
                   13422:             }
                   13423:         }
                   13424:     }
                   13425:     return $location;
                   13426: }
                   13427: 
                   13428: sub list_archive_contents {
                   13429:     my ($file,$pathsref) = @_;
                   13430:     my (@cmd,$output);
                   13431:     my $needsregexp;
                   13432:     if ($file =~ /\.zip$/) {
                   13433:         @cmd = (&decompression_utility('unzip'),"-l");
                   13434:         $needsregexp = 1;
                   13435:     } elsif (($file =~ m/\.tar\.gz$/) ||
                   13436:              ($file =~ /\.tgz$/)) {
                   13437:         @cmd = (&decompression_utility('tar'),"-ztf");
                   13438:     } elsif ($file =~ /\.tar\.bz2$/) {
                   13439:         @cmd = (&decompression_utility('tar'),"-jtf");
                   13440:     } elsif ($file =~ m|\.tar$|) {
                   13441:         @cmd = (&decompression_utility('tar'),"-tf");
                   13442:     }
                   13443:     if (@cmd) {
                   13444:         undef($!);
                   13445:         undef($@);
                   13446:         if (open(my $fh,"-|", @cmd, $file)) {
                   13447:             while (my $line = <$fh>) {
                   13448:                 $output .= $line;
                   13449:                 chomp($line);
                   13450:                 my $item;
                   13451:                 if ($needsregexp) {
                   13452:                     ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/); 
                   13453:                 } else {
                   13454:                     $item = $line;
                   13455:                 }
                   13456:                 if ($item ne '') {
                   13457:                     unless (grep(/^\Q$item\E$/,@{$pathsref})) {
                   13458:                         push(@{$pathsref},$item);
                   13459:                     } 
                   13460:                 }
                   13461:             }
                   13462:             close($fh);
                   13463:         }
                   13464:     }
                   13465:     return $output;
                   13466: }
                   13467: 
1.1053    raeburn  13468: sub decompress_uploaded_file {
                   13469:     my ($file,$dir) = @_;
                   13470:     &Apache::lonnet::appenv({'cgi.file' => $file});
                   13471:     &Apache::lonnet::appenv({'cgi.dir' => $dir});
                   13472:     my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
                   13473:     my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
                   13474:     my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
                   13475:     &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
                   13476:     my $decompressed = $env{'cgi.decompressed'};
                   13477:     &Apache::lonnet::delenv('cgi.file');
                   13478:     &Apache::lonnet::delenv('cgi.dir');
                   13479:     &Apache::lonnet::delenv('cgi.decompressed');
                   13480:     return ($decompressed,$result);
                   13481: }
                   13482: 
1.1055    raeburn  13483: sub process_decompression {
                   13484:     my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
1.1292    raeburn  13485:     unless (($dir_root eq '/userfiles') && ($destination =~ m{^(docs|supplemental)/(default|\d+)/\d+$})) {
                   13486:         return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   13487:                &mt('Unexpected file path.').'</p>'."\n";
                   13488:     }
                   13489:     unless (($docudom =~ /^$match_domain$/) && ($docuname =~ /^$match_courseid$/)) {
                   13490:         return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   13491:                &mt('Unexpected course context.').'</p>'."\n";
                   13492:     }
1.1293    raeburn  13493:     unless ($file eq &Apache::lonnet::clean_filename($file)) {
1.1292    raeburn  13494:         return '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   13495:                &mt('Filename contained unexpected characters.').'</p>'."\n";
                   13496:     }
1.1055    raeburn  13497:     my ($dir,$error,$warning,$output);
1.1180    raeburn  13498:     if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/i) {
1.1120    bisitz   13499:         $error = &mt('Filename not a supported archive file type.').
                   13500:                  '<br />'.&mt('Filename should end with one of: [_1].',
1.1055    raeburn  13501:                               '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
                   13502:     } else {
                   13503:         my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
                   13504:         if ($docuhome eq 'no_host') {
                   13505:             $error = &mt('Could not determine home server for course.');
                   13506:         } else {
                   13507:             my @ids=&Apache::lonnet::current_machine_ids();
                   13508:             my $currdir = "$dir_root/$destination";
                   13509:             if (grep(/^\Q$docuhome\E$/,@ids)) {
                   13510:                 $dir = &LONCAPA::propath($docudom,$docuname).
                   13511:                        "$dir_root/$destination";
                   13512:             } else {
                   13513:                 $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
                   13514:                        "$dir_root/$docudom/$docuname/$destination";
                   13515:                 unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
                   13516:                     $error = &mt('Archive file not found.');
                   13517:                 }
                   13518:             }
1.1065    raeburn  13519:             my (@to_overwrite,@to_skip);
                   13520:             if ($env{'form.archive_overwrite_total'} > 0) {
                   13521:                 my $total = $env{'form.archive_overwrite_total'};
                   13522:                 for (my $i=0; $i<$total; $i++) {
                   13523:                     if ($env{'form.archive_overwrite_'.$i} == 1) {
                   13524:                         push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
                   13525:                     } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
                   13526:                         push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
                   13527:                     }
                   13528:                 }
                   13529:             }
                   13530:             my $numskip = scalar(@to_skip);
1.1292    raeburn  13531:             my $numoverwrite = scalar(@to_overwrite);
                   13532:             if (($numskip) && (!$numoverwrite)) { 
1.1065    raeburn  13533:                 $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');         
                   13534:             } elsif ($dir eq '') {
1.1055    raeburn  13535:                 $error = &mt('Directory containing archive file unavailable.');
                   13536:             } elsif (!$error) {
1.1065    raeburn  13537:                 my ($decompressed,$display);
1.1292    raeburn  13538:                 if (($numskip) || ($numoverwrite)) {
1.1065    raeburn  13539:                     my $tempdir = time.'_'.$$.int(rand(10000));
                   13540:                     mkdir("$dir/$tempdir",0755);
1.1292    raeburn  13541:                     if (&File::Copy::move("$dir/$file","$dir/$tempdir/$file")) {
                   13542:                         ($decompressed,$display) = 
                   13543:                             &decompress_uploaded_file($file,"$dir/$tempdir");
                   13544:                         foreach my $item (@to_skip) {
                   13545:                             if (($item ne '') && ($item !~ /\.\./)) {
                   13546:                                 if (-f "$dir/$tempdir/$item") { 
                   13547:                                     unlink("$dir/$tempdir/$item");
                   13548:                                 } elsif (-d "$dir/$tempdir/$item") {
1.1300    raeburn  13549:                                     &File::Path::remove_tree("$dir/$tempdir/$item",{ safe => 1 });
1.1292    raeburn  13550:                                 }
                   13551:                             }
                   13552:                         }
                   13553:                         foreach my $item (@to_overwrite) {
                   13554:                             if ((-e "$dir/$tempdir/$item") && (-e "$dir/$item")) {
                   13555:                                 if (($item ne '') && ($item !~ /\.\./)) {
                   13556:                                     if (-f "$dir/$item") {
                   13557:                                         unlink("$dir/$item");
                   13558:                                     } elsif (-d "$dir/$item") {
1.1300    raeburn  13559:                                         &File::Path::remove_tree("$dir/$item",{ safe => 1 });
1.1292    raeburn  13560:                                     }
                   13561:                                     &File::Copy::move("$dir/$tempdir/$item","$dir/$item");
                   13562:                                 }
1.1065    raeburn  13563:                             }
                   13564:                         }
1.1292    raeburn  13565:                         if (&File::Copy::move("$dir/$tempdir/$file","$dir/$file")) {
1.1300    raeburn  13566:                             &File::Path::remove_tree("$dir/$tempdir",{ safe => 1 });
1.1292    raeburn  13567:                         }
1.1065    raeburn  13568:                     }
                   13569:                 } else {
                   13570:                     ($decompressed,$display) = 
                   13571:                         &decompress_uploaded_file($file,$dir);
                   13572:                 }
1.1055    raeburn  13573:                 if ($decompressed eq 'ok') {
1.1065    raeburn  13574:                     $output = '<p class="LC_info">'.
                   13575:                               &mt('Files extracted successfully from archive.').
                   13576:                               '</p>'."\n";
1.1055    raeburn  13577:                     my ($warning,$result,@contents);
                   13578:                     my ($newdirlistref,$newlisterror) =
                   13579:                         &Apache::lonnet::dirlist($currdir,$docudom,
                   13580:                                                  $docuname,1);
                   13581:                     my (%is_dir,%changes,@newitems);
                   13582:                     my $dirptr = 16384;
1.1065    raeburn  13583:                     if (ref($newdirlistref) eq 'ARRAY') {
1.1055    raeburn  13584:                         foreach my $dir_line (@{$newdirlistref}) {
                   13585:                             my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
1.1292    raeburn  13586:                             unless (($item =~ /^\.+$/) || ($item eq $file)) {
1.1055    raeburn  13587:                                 push(@newitems,$item);
                   13588:                                 if ($dirptr&$testdir) {
                   13589:                                     $is_dir{$item} = 1;
                   13590:                                 }
                   13591:                                 $changes{$item} = 1;
                   13592:                             }
                   13593:                         }
                   13594:                     }
                   13595:                     if (keys(%changes) > 0) {
                   13596:                         foreach my $item (sort(@newitems)) {
                   13597:                             if ($changes{$item}) {
                   13598:                                 push(@contents,$item);
                   13599:                             }
                   13600:                         }
                   13601:                     }
                   13602:                     if (@contents > 0) {
1.1067    raeburn  13603:                         my $wantform;
                   13604:                         unless ($env{'form.autoextract_camtasia'}) {
                   13605:                             $wantform = 1;
                   13606:                         }
1.1056    raeburn  13607:                         my (%children,%parent,%dirorder,%titles);
1.1055    raeburn  13608:                         my ($count,$datatable) = &get_extracted($docudom,$docuname,
                   13609:                                                                 $currdir,\%is_dir,
                   13610:                                                                 \%children,\%parent,
1.1056    raeburn  13611:                                                                 \@contents,\%dirorder,
                   13612:                                                                 \%titles,$wantform);
1.1055    raeburn  13613:                         if ($datatable ne '') {
                   13614:                             $output .= &archive_options_form('decompressed',$datatable,
                   13615:                                                              $count,$hiddenelem);
1.1065    raeburn  13616:                             my $startcount = 6;
1.1055    raeburn  13617:                             $output .= &archive_javascript($startcount,$count,
1.1056    raeburn  13618:                                                            \%titles,\%children);
1.1055    raeburn  13619:                         }
1.1067    raeburn  13620:                         if ($env{'form.autoextract_camtasia'}) {
1.1164    raeburn  13621:                             my $version = $env{'form.autoextract_camtasia'};
1.1067    raeburn  13622:                             my %displayed;
                   13623:                             my $total = 1;
                   13624:                             $env{'form.archive_directory'} = [];
                   13625:                             foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
                   13626:                                 my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
                   13627:                                 $path =~ s{/$}{};
                   13628:                                 my $item;
                   13629:                                 if ($path ne '') {
                   13630:                                     $item = "$path/$titles{$i}";
                   13631:                                 } else {
                   13632:                                     $item = $titles{$i};
                   13633:                                 }
                   13634:                                 $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
                   13635:                                 if ($item eq $contents[0]) {
                   13636:                                     push(@{$env{'form.archive_directory'}},$i);
                   13637:                                     $env{'form.archive_'.$i} = 'display';
                   13638:                                     $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
                   13639:                                     $displayed{'folder'} = $i;
1.1164    raeburn  13640:                                 } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) ||
                   13641:                                          (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) { 
1.1067    raeburn  13642:                                     $env{'form.archive_'.$i} = 'display';
                   13643:                                     $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
                   13644:                                     $displayed{'web'} = $i;
                   13645:                                 } else {
1.1164    raeburn  13646:                                     if ((($item eq "$contents[0]/media") && ($version == 6)) ||
                   13647:                                         ((($item eq "$contents[0]/scripts") || ($item eq "$contents[0]/skins") ||
                   13648:                                              ($item eq "$contents[0]/skins/express_show")) && ($version == 8))) {
1.1067    raeburn  13649:                                         push(@{$env{'form.archive_directory'}},$i);
                   13650:                                     }
                   13651:                                     $env{'form.archive_'.$i} = 'dependency';
                   13652:                                 }
                   13653:                                 $total ++;
                   13654:                             }
                   13655:                             for (my $i=1; $i<$total; $i++) {
                   13656:                                 next if ($i == $displayed{'web'});
                   13657:                                 next if ($i == $displayed{'folder'});
                   13658:                                 $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
                   13659:                             }
                   13660:                             $env{'form.phase'} = 'decompress_cleanup';
                   13661:                             $env{'form.archivedelete'} = 1;
                   13662:                             $env{'form.archive_count'} = $total-1;
                   13663:                             $output .=
                   13664:                                 &process_extracted_files('coursedocs',$docudom,
                   13665:                                                          $docuname,$destination,
                   13666:                                                          $dir_root,$hiddenelem);
                   13667:                         }
1.1055    raeburn  13668:                     } else {
                   13669:                         $warning = &mt('No new items extracted from archive file.');
                   13670:                     }
                   13671:                 } else {
                   13672:                     $output = $display;
                   13673:                     $error = &mt('An error occurred during extraction from the archive file.');
                   13674:                 }
                   13675:             }
                   13676:         }
                   13677:     }
                   13678:     if ($error) {
                   13679:         $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   13680:                    $error.'</p>'."\n";
                   13681:     }
                   13682:     if ($warning) {
                   13683:         $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
                   13684:     }
                   13685:     return $output;
                   13686: }
                   13687: 
                   13688: sub get_extracted {
1.1056    raeburn  13689:     my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
                   13690:         $titles,$wantform) = @_;
1.1055    raeburn  13691:     my $count = 0;
                   13692:     my $depth = 0;
                   13693:     my $datatable;
1.1056    raeburn  13694:     my @hierarchy;
1.1055    raeburn  13695:     return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
1.1056    raeburn  13696:                    (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
                   13697:                    (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
1.1055    raeburn  13698:     foreach my $item (@{$contents}) {
                   13699:         $count ++;
1.1056    raeburn  13700:         @{$dirorder->{$count}} = @hierarchy;
                   13701:         $titles->{$count} = $item;
1.1055    raeburn  13702:         &archive_hierarchy($depth,$count,$parent,$children);
                   13703:         if ($wantform) {
                   13704:             $datatable .= &archive_row($is_dir->{$item},$item,
                   13705:                                        $currdir,$depth,$count);
                   13706:         }
                   13707:         if ($is_dir->{$item}) {
                   13708:             $depth ++;
1.1056    raeburn  13709:             push(@hierarchy,$count);
                   13710:             $parent->{$depth} = $count;
1.1055    raeburn  13711:             $datatable .=
                   13712:                 &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
1.1056    raeburn  13713:                                            \$depth,\$count,\@hierarchy,$dirorder,
                   13714:                                            $children,$parent,$titles,$wantform);
1.1055    raeburn  13715:             $depth --;
1.1056    raeburn  13716:             pop(@hierarchy);
1.1055    raeburn  13717:         }
                   13718:     }
                   13719:     return ($count,$datatable);
                   13720: }
                   13721: 
                   13722: sub recurse_extracted_archive {
1.1056    raeburn  13723:     my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
                   13724:         $children,$parent,$titles,$wantform) = @_;
1.1055    raeburn  13725:     my $result='';
1.1056    raeburn  13726:     unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
                   13727:             (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
                   13728:             (ref($dirorder) eq 'HASH')) {
1.1055    raeburn  13729:         return $result;
                   13730:     }
                   13731:     my $dirptr = 16384;
                   13732:     my ($newdirlistref,$newlisterror) =
                   13733:         &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
                   13734:     if (ref($newdirlistref) eq 'ARRAY') {
                   13735:         foreach my $dir_line (@{$newdirlistref}) {
                   13736:             my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
                   13737:             unless ($item =~ /^\.+$/) {
                   13738:                 $$count ++;
1.1056    raeburn  13739:                 @{$dirorder->{$$count}} = @{$hierarchy};
                   13740:                 $titles->{$$count} = $item;
1.1055    raeburn  13741:                 &archive_hierarchy($$depth,$$count,$parent,$children);
1.1056    raeburn  13742: 
1.1055    raeburn  13743:                 my $is_dir;
                   13744:                 if ($dirptr&$testdir) {
                   13745:                     $is_dir = 1;
                   13746:                 }
                   13747:                 if ($wantform) {
                   13748:                     $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
                   13749:                 }
                   13750:                 if ($is_dir) {
                   13751:                     $$depth ++;
1.1056    raeburn  13752:                     push(@{$hierarchy},$$count);
                   13753:                     $parent->{$$depth} = $$count;
1.1055    raeburn  13754:                     $result .=
                   13755:                         &recurse_extracted_archive("$currdir/$item",$docudom,
                   13756:                                                    $docuname,$depth,$count,
1.1056    raeburn  13757:                                                    $hierarchy,$dirorder,$children,
                   13758:                                                    $parent,$titles,$wantform);
1.1055    raeburn  13759:                     $$depth --;
1.1056    raeburn  13760:                     pop(@{$hierarchy});
1.1055    raeburn  13761:                 }
                   13762:             }
                   13763:         }
                   13764:     }
                   13765:     return $result;
                   13766: }
                   13767: 
                   13768: sub archive_hierarchy {
                   13769:     my ($depth,$count,$parent,$children) =@_;
                   13770:     if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
                   13771:         if (exists($parent->{$depth})) {
                   13772:              $children->{$parent->{$depth}} .= $count.':';
                   13773:         }
                   13774:     }
                   13775:     return;
                   13776: }
                   13777: 
                   13778: sub archive_row {
                   13779:     my ($is_dir,$item,$currdir,$depth,$count) = @_;
                   13780:     my ($name) = ($item =~ m{([^/]+)$});
                   13781:     my %choices = &Apache::lonlocal::texthash (
1.1059    raeburn  13782:                                        'display'    => 'Add as file',
1.1055    raeburn  13783:                                        'dependency' => 'Include as dependency',
                   13784:                                        'discard'    => 'Discard',
                   13785:                                       );
                   13786:     if ($is_dir) {
1.1059    raeburn  13787:         $choices{'display'} = &mt('Add as folder'); 
1.1055    raeburn  13788:     }
1.1056    raeburn  13789:     my $output = &start_data_table_row().'<td align="right">'.$count.'</td>'."\n";
                   13790:     my $offset = 0;
1.1055    raeburn  13791:     foreach my $action ('display','dependency','discard') {
1.1056    raeburn  13792:         $offset ++;
1.1065    raeburn  13793:         if ($action ne 'display') {
                   13794:             $offset ++;
                   13795:         }  
1.1055    raeburn  13796:         $output .= '<td><span class="LC_nobreak">'.
                   13797:                    '<label><input type="radio" name="archive_'.$count.
                   13798:                    '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
                   13799:         my $text = $choices{$action};
                   13800:         if ($is_dir) {
                   13801:             $output .= ' onclick="javascript:propagateCheck(this.form,'."'$count'".');"';
                   13802:             if ($action eq 'display') {
1.1059    raeburn  13803:                 $text = &mt('Add as folder');
1.1055    raeburn  13804:             }
1.1056    raeburn  13805:         } else {
                   13806:             $output .= ' onclick="javascript:dependencyCheck(this.form,'."$count,$offset".');"';
                   13807: 
                   13808:         }
                   13809:         $output .= ' />&nbsp;'.$choices{$action}.'</label></span>';
                   13810:         if ($action eq 'dependency') {
                   13811:             $output .= '<div id="arc_depon_'.$count.'" style="display:none;">'."\n".
                   13812:                        &mt('Used by:').'&nbsp;<select name="archive_dependent_on_'.$count.'" '.
                   13813:                        'onchange="propagateSelect(this.form,'."$count,$offset".')">'."\n".
                   13814:                        '<option value=""></option>'."\n".
                   13815:                        '</select>'."\n".
                   13816:                        '</div>';
1.1059    raeburn  13817:         } elsif ($action eq 'display') {
                   13818:             $output .= '<div id="arc_title_'.$count.'" style="display:none;">'."\n".
                   13819:                        &mt('Title:').'&nbsp;<input type="text" name="archive_title_'.$count.'" id="archive_title_'.$count.'" />'."\n".
                   13820:                        '</div>';
1.1055    raeburn  13821:         }
1.1056    raeburn  13822:         $output .= '</td>';
1.1055    raeburn  13823:     }
                   13824:     $output .= '<td><input type="hidden" name="archive_content_'.$count.'" value="'.
                   13825:                &HTML::Entities::encode("$currdir/$item",'"<>&').'" />'.('&nbsp;' x 2);
                   13826:     for (my $i=0; $i<$depth; $i++) {
                   13827:         $output .= ('<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" alt="" />' x2)."\n";
                   13828:     }
                   13829:     if ($is_dir) {
                   13830:         $output .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" />&nbsp;'."\n".
                   13831:                    '<input type="hidden" name="archive_directory" value="'.$count.'" />'."\n";
                   13832:     } else {
                   13833:         $output .= '<input type="hidden" name="archive_file" value="'.$count.'" />'."\n";
                   13834:     }
                   13835:     $output .= '&nbsp;'.$name.'</td>'."\n".
                   13836:                &end_data_table_row();
                   13837:     return $output;
                   13838: }
                   13839: 
                   13840: sub archive_options_form {
1.1065    raeburn  13841:     my ($form,$display,$count,$hiddenelem) = @_;
                   13842:     my %lt = &Apache::lonlocal::texthash(
                   13843:                perm => 'Permanently remove archive file?',
                   13844:                hows => 'How should each extracted item be incorporated in the course?',
                   13845:                cont => 'Content actions for all',
                   13846:                addf => 'Add as folder/file',
                   13847:                incd => 'Include as dependency for a displayed file',
                   13848:                disc => 'Discard',
                   13849:                no   => 'No',
                   13850:                yes  => 'Yes',
                   13851:                save => 'Save',
                   13852:     );
                   13853:     my $output = <<"END";
                   13854: <form name="$form" method="post" action="">
                   13855: <p><span class="LC_nobreak">$lt{'perm'}&nbsp;
                   13856: <label>
                   13857:   <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
                   13858: </label>
                   13859: &nbsp;
                   13860: <label>
                   13861:   <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
                   13862: </span>
                   13863: </p>
                   13864: <input type="hidden" name="phase" value="decompress_cleanup" />
                   13865: <br />$lt{'hows'}
                   13866: <div class="LC_columnSection">
                   13867:   <fieldset>
                   13868:     <legend>$lt{'cont'}</legend>
                   13869:     <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" /> 
                   13870:     &nbsp;&nbsp;<input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
                   13871:     &nbsp;&nbsp;<input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
                   13872:   </fieldset>
                   13873: </div>
                   13874: END
                   13875:     return $output.
1.1055    raeburn  13876:            &start_data_table()."\n".
1.1065    raeburn  13877:            $display."\n".
1.1055    raeburn  13878:            &end_data_table()."\n".
                   13879:            '<input type="hidden" name="archive_count" value="'.$count.'" />'.
                   13880:            $hiddenelem.
1.1065    raeburn  13881:            '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
1.1055    raeburn  13882:            '</form>';
                   13883: }
                   13884: 
                   13885: sub archive_javascript {
1.1056    raeburn  13886:     my ($startcount,$numitems,$titles,$children) = @_;
                   13887:     return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
1.1059    raeburn  13888:     my $maintitle = $env{'form.comment'};
1.1055    raeburn  13889:     my $scripttag = <<START;
                   13890: <script type="text/javascript">
                   13891: // <![CDATA[
                   13892: 
                   13893: function checkAll(form,prefix) {
                   13894:     var idstr =  new RegExp("^archive_"+prefix+"_\\\\d+\$");
                   13895:     for (var i=0; i < form.elements.length; i++) {
                   13896:         var id = form.elements[i].id;
                   13897:         if ((id != '') && (id != undefined)) {
                   13898:             if (idstr.test(id)) {
                   13899:                 if (form.elements[i].type == 'radio') {
                   13900:                     form.elements[i].checked = true;
1.1056    raeburn  13901:                     var nostart = i-$startcount;
1.1059    raeburn  13902:                     var offset = nostart%7;
                   13903:                     var count = (nostart-offset)/7;    
1.1056    raeburn  13904:                     dependencyCheck(form,count,offset);
1.1055    raeburn  13905:                 }
                   13906:             }
                   13907:         }
                   13908:     }
                   13909: }
                   13910: 
                   13911: function propagateCheck(form,count) {
                   13912:     if (count > 0) {
1.1059    raeburn  13913:         var startelement = $startcount + ((count-1) * 7);
                   13914:         for (var j=1; j<6; j++) {
                   13915:             if ((j != 2) && (j != 4)) {
1.1056    raeburn  13916:                 var item = startelement + j; 
                   13917:                 if (form.elements[item].type == 'radio') {
                   13918:                     if (form.elements[item].checked) {
                   13919:                         containerCheck(form,count,j);
                   13920:                         break;
                   13921:                     }
1.1055    raeburn  13922:                 }
                   13923:             }
                   13924:         }
                   13925:     }
                   13926: }
                   13927: 
                   13928: numitems = $numitems
1.1056    raeburn  13929: var titles = new Array(numitems);
                   13930: var parents = new Array(numitems);
1.1055    raeburn  13931: for (var i=0; i<numitems; i++) {
1.1056    raeburn  13932:     parents[i] = new Array;
1.1055    raeburn  13933: }
1.1059    raeburn  13934: var maintitle = '$maintitle';
1.1055    raeburn  13935: 
                   13936: START
                   13937: 
1.1056    raeburn  13938:     foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
                   13939:         my @contents = split(/:/,$children->{$container});
1.1055    raeburn  13940:         for (my $i=0; $i<@contents; $i ++) {
                   13941:             $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
                   13942:         }
                   13943:     }
                   13944: 
1.1056    raeburn  13945:     foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
                   13946:         $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
                   13947:     }
                   13948: 
1.1055    raeburn  13949:     $scripttag .= <<END;
                   13950: 
                   13951: function containerCheck(form,count,offset) {
                   13952:     if (count > 0) {
1.1056    raeburn  13953:         dependencyCheck(form,count,offset);
1.1059    raeburn  13954:         var item = (offset+$startcount)+7*(count-1);
1.1055    raeburn  13955:         form.elements[item].checked = true;
                   13956:         if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   13957:             if (parents[count].length > 0) {
                   13958:                 for (var j=0; j<parents[count].length; j++) {
1.1056    raeburn  13959:                     containerCheck(form,parents[count][j],offset);
                   13960:                 }
                   13961:             }
                   13962:         }
                   13963:     }
                   13964: }
                   13965: 
                   13966: function dependencyCheck(form,count,offset) {
                   13967:     if (count > 0) {
1.1059    raeburn  13968:         var chosen = (offset+$startcount)+7*(count-1);
                   13969:         var depitem = $startcount + ((count-1) * 7) + 4;
1.1056    raeburn  13970:         var currtype = form.elements[depitem].type;
                   13971:         if (form.elements[chosen].value == 'dependency') {
                   13972:             document.getElementById('arc_depon_'+count).style.display='block'; 
                   13973:             form.elements[depitem].options.length = 0;
                   13974:             form.elements[depitem].options[0] = new Option('Select','',true,true);
1.1085    raeburn  13975:             for (var i=1; i<=numitems; i++) {
                   13976:                 if (i == count) {
                   13977:                     continue;
                   13978:                 }
1.1059    raeburn  13979:                 var startelement = $startcount + (i-1) * 7;
                   13980:                 for (var j=1; j<6; j++) {
                   13981:                     if ((j != 2) && (j!= 4)) {
1.1056    raeburn  13982:                         var item = startelement + j;
                   13983:                         if (form.elements[item].type == 'radio') {
                   13984:                             if (form.elements[item].checked) {
                   13985:                                 if (form.elements[item].value == 'display') {
                   13986:                                     var n = form.elements[depitem].options.length;
                   13987:                                     form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
                   13988:                                 }
                   13989:                             }
                   13990:                         }
                   13991:                     }
                   13992:                 }
                   13993:             }
                   13994:         } else {
                   13995:             document.getElementById('arc_depon_'+count).style.display='none';
                   13996:             form.elements[depitem].options.length = 0;
                   13997:             form.elements[depitem].options[0] = new Option('Select','',true,true);
                   13998:         }
1.1059    raeburn  13999:         titleCheck(form,count,offset);
1.1056    raeburn  14000:     }
                   14001: }
                   14002: 
                   14003: function propagateSelect(form,count,offset) {
                   14004:     if (count > 0) {
1.1065    raeburn  14005:         var item = (1+offset+$startcount)+7*(count-1);
1.1056    raeburn  14006:         var picked = form.elements[item].options[form.elements[item].selectedIndex].value; 
                   14007:         if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   14008:             if (parents[count].length > 0) {
                   14009:                 for (var j=0; j<parents[count].length; j++) {
                   14010:                     containerSelect(form,parents[count][j],offset,picked);
1.1055    raeburn  14011:                 }
                   14012:             }
                   14013:         }
                   14014:     }
                   14015: }
1.1056    raeburn  14016: 
                   14017: function containerSelect(form,count,offset,picked) {
                   14018:     if (count > 0) {
1.1065    raeburn  14019:         var item = (offset+$startcount)+7*(count-1);
1.1056    raeburn  14020:         if (form.elements[item].type == 'radio') {
                   14021:             if (form.elements[item].value == 'dependency') {
                   14022:                 if (form.elements[item+1].type == 'select-one') {
                   14023:                     for (var i=0; i<form.elements[item+1].options.length; i++) {
                   14024:                         if (form.elements[item+1].options[i].value == picked) {
                   14025:                             form.elements[item+1].selectedIndex = i;
                   14026:                             break;
                   14027:                         }
                   14028:                     }
                   14029:                 }
                   14030:                 if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
                   14031:                     if (parents[count].length > 0) {
                   14032:                         for (var j=0; j<parents[count].length; j++) {
                   14033:                             containerSelect(form,parents[count][j],offset,picked);
                   14034:                         }
                   14035:                     }
                   14036:                 }
                   14037:             }
                   14038:         }
                   14039:     }
                   14040: }
                   14041: 
1.1059    raeburn  14042: function titleCheck(form,count,offset) {
                   14043:     if (count > 0) {
                   14044:         var chosen = (offset+$startcount)+7*(count-1);
                   14045:         var depitem = $startcount + ((count-1) * 7) + 2;
                   14046:         var currtype = form.elements[depitem].type;
                   14047:         if (form.elements[chosen].value == 'display') {
                   14048:             document.getElementById('arc_title_'+count).style.display='block';
                   14049:             if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
                   14050:                 document.getElementById('archive_title_'+count).value=maintitle;
                   14051:             }
                   14052:         } else {
                   14053:             document.getElementById('arc_title_'+count).style.display='none';
                   14054:             if (currtype == 'text') { 
                   14055:                 document.getElementById('archive_title_'+count).value='';
                   14056:             }
                   14057:         }
                   14058:     }
                   14059:     return;
                   14060: }
                   14061: 
1.1055    raeburn  14062: // ]]>
                   14063: </script>
                   14064: END
                   14065:     return $scripttag;
                   14066: }
                   14067: 
                   14068: sub process_extracted_files {
1.1067    raeburn  14069:     my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
1.1055    raeburn  14070:     my $numitems = $env{'form.archive_count'};
1.1294    raeburn  14071:     return if ((!$numitems) || ($numitems =~ /\D/));
1.1055    raeburn  14072:     my @ids=&Apache::lonnet::current_machine_ids();
                   14073:     my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
1.1067    raeburn  14074:         %folders,%containers,%mapinner,%prompttofetch);
1.1055    raeburn  14075:     my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
                   14076:     if (grep(/^\Q$docuhome\E$/,@ids)) {
                   14077:         $prefix = &LONCAPA::propath($docudom,$docuname);
                   14078:         $pathtocheck = "$dir_root/$destination";
                   14079:         $dir = $dir_root;
                   14080:         $ishome = 1;
                   14081:     } else {
                   14082:         $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
                   14083:         $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
1.1294    raeburn  14084:         $dir = "$dir_root/$docudom/$docuname";
1.1055    raeburn  14085:     }
                   14086:     my $currdir = "$dir_root/$destination";
                   14087:     (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
                   14088:     if ($env{'form.folderpath'}) {
                   14089:         my @items = split('&',$env{'form.folderpath'});
                   14090:         $folders{'0'} = $items[-2];
1.1099    raeburn  14091:         if ($env{'form.folderpath'} =~ /\:1$/) {
                   14092:             $containers{'0'}='page';
                   14093:         } else {  
                   14094:             $containers{'0'}='sequence';
                   14095:         }
1.1055    raeburn  14096:     }
                   14097:     my @archdirs = &get_env_multiple('form.archive_directory');
                   14098:     if ($numitems) {
                   14099:         for (my $i=1; $i<=$numitems; $i++) {
                   14100:             my $path = $env{'form.archive_content_'.$i};
                   14101:             if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
                   14102:                 my $item = $1;
                   14103:                 $toplevelitems{$item} = $i;
                   14104:                 if (grep(/^\Q$i\E$/,@archdirs)) {
                   14105:                     $is_dir{$item} = 1;
                   14106:                 }
                   14107:             }
                   14108:         }
                   14109:     }
1.1067    raeburn  14110:     my ($output,%children,%parent,%titles,%dirorder,$result);
1.1055    raeburn  14111:     if (keys(%toplevelitems) > 0) {
                   14112:         my @contents = sort(keys(%toplevelitems));
1.1056    raeburn  14113:         (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
                   14114:                                            \%parent,\@contents,\%dirorder,\%titles);
1.1055    raeburn  14115:     }
1.1066    raeburn  14116:     my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
1.1055    raeburn  14117:     if ($numitems) {
                   14118:         for (my $i=1; $i<=$numitems; $i++) {
1.1086    raeburn  14119:             next if ($env{'form.archive_'.$i} eq 'dependency');
1.1055    raeburn  14120:             my $path = $env{'form.archive_content_'.$i};
                   14121:             if ($path =~ /^\Q$pathtocheck\E/) {
                   14122:                 if ($env{'form.archive_'.$i} eq 'discard') {
                   14123:                     if ($prefix ne '' && $path ne '') {
                   14124:                         if (-e $prefix.$path) {
1.1066    raeburn  14125:                             if ((@archdirs > 0) && 
                   14126:                                 (grep(/^\Q$i\E$/,@archdirs))) {
                   14127:                                 $todeletedir{$prefix.$path} = 1;
                   14128:                             } else {
                   14129:                                 $todelete{$prefix.$path} = 1;
                   14130:                             }
1.1055    raeburn  14131:                         }
                   14132:                     }
                   14133:                 } elsif ($env{'form.archive_'.$i} eq 'display') {
1.1059    raeburn  14134:                     my ($docstitle,$title,$url,$outer);
1.1055    raeburn  14135:                     ($title) = ($path =~ m{/([^/]+)$});
1.1059    raeburn  14136:                     $docstitle = $env{'form.archive_title_'.$i};
                   14137:                     if ($docstitle eq '') {
                   14138:                         $docstitle = $title;
                   14139:                     }
1.1055    raeburn  14140:                     $outer = 0;
1.1056    raeburn  14141:                     if (ref($dirorder{$i}) eq 'ARRAY') {
                   14142:                         if (@{$dirorder{$i}} > 0) {
                   14143:                             foreach my $item (reverse(@{$dirorder{$i}})) {
1.1055    raeburn  14144:                                 if ($env{'form.archive_'.$item} eq 'display') {
                   14145:                                     $outer = $item;
                   14146:                                     last;
                   14147:                                 }
                   14148:                             }
                   14149:                         }
                   14150:                     }
                   14151:                     my ($errtext,$fatal) = 
                   14152:                         &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
                   14153:                                                '/'.$folders{$outer}.'.'.
                   14154:                                                $containers{$outer});
                   14155:                     next if ($fatal);
                   14156:                     if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
                   14157:                         if ($context eq 'coursedocs') {
1.1056    raeburn  14158:                             $mapinner{$i} = time;
1.1055    raeburn  14159:                             $folders{$i} = 'default_'.$mapinner{$i};
                   14160:                             $containers{$i} = 'sequence';
                   14161:                             my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
                   14162:                                       $folders{$i}.'.'.$containers{$i};
                   14163:                             my $newidx = &LONCAPA::map::getresidx();
                   14164:                             $LONCAPA::map::resources[$newidx]=
1.1059    raeburn  14165:                                 $docstitle.':'.$url.':false:normal:res';
1.1055    raeburn  14166:                             push(@LONCAPA::map::order,$newidx);
                   14167:                             my ($outtext,$errtext) =
                   14168:                                 &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
                   14169:                                                         $docuname.'/'.$folders{$outer}.
1.1087    raeburn  14170:                                                         '.'.$containers{$outer},1,1);
1.1056    raeburn  14171:                             $newseqid{$i} = $newidx;
1.1067    raeburn  14172:                             unless ($errtext) {
1.1294    raeburn  14173:                                 $result .=  '<li>'.&mt('Folder: [_1] added to course',
                   14174:                                                        &HTML::Entities::encode($docstitle,'<>&"')).
                   14175:                                             '</li>'."\n";
1.1067    raeburn  14176:                             }
1.1055    raeburn  14177:                         }
                   14178:                     } else {
                   14179:                         if ($context eq 'coursedocs') {
                   14180:                             my $newidx=&LONCAPA::map::getresidx();
                   14181:                             my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
                   14182:                                       $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
                   14183:                                       $title;
1.1294    raeburn  14184:                             if (($outer !~ /\D/) && ($mapinner{$outer} !~ /\D/) && ($newidx !~ /\D/)) {
                   14185:                                 if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
                   14186:                                     mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
                   14187:                                 }
                   14188:                                 if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
                   14189:                                     mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx");
                   14190:                                 }
                   14191:                                 if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
                   14192:                                     if (rename("$prefix$path","$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title")) {
                   14193:                                         $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx";
                   14194:                                         unless ($ishome) {
                   14195:                                             my $fetch = "$newdest{$i}/$title";
                   14196:                                             $fetch =~ s/^\Q$prefix$dir\E//;
                   14197:                                             $prompttofetch{$fetch} = 1;
                   14198:                                         }
1.1292    raeburn  14199:                                     }
1.1067    raeburn  14200:                                 }
1.1294    raeburn  14201:                                 $LONCAPA::map::resources[$newidx]=
                   14202:                                     $docstitle.':'.$url.':false:normal:res';
                   14203:                                 push(@LONCAPA::map::order, $newidx);
                   14204:                                 my ($outtext,$errtext)=
                   14205:                                     &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
                   14206:                                                             $docuname.'/'.$folders{$outer}.
                   14207:                                                             '.'.$containers{$outer},1,1);
                   14208:                                 unless ($errtext) {
                   14209:                                     if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") {
                   14210:                                         $result .= '<li>'.&mt('File: [_1] added to course',
                   14211:                                                               &HTML::Entities::encode($docstitle,'<>&"')).
                   14212:                                                    '</li>'."\n";
                   14213:                                     }
1.1067    raeburn  14214:                                 }
1.1294    raeburn  14215:                             } else {
                   14216:                                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
                   14217:                                                 &HTML::Entities::encode($path,'<>&"')).'<br />';
1.1296    raeburn  14218:                             }
1.1055    raeburn  14219:                         }
                   14220:                     }
1.1086    raeburn  14221:                 }
                   14222:             } else {
1.1294    raeburn  14223:                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
                   14224:                                 &HTML::Entities::encode($path,'<>&"')).'<br />'; 
1.1086    raeburn  14225:             }
                   14226:         }
                   14227:         for (my $i=1; $i<=$numitems; $i++) {
                   14228:             next unless ($env{'form.archive_'.$i} eq 'dependency');
                   14229:             my $path = $env{'form.archive_content_'.$i};
                   14230:             if ($path =~ /^\Q$pathtocheck\E/) {
                   14231:                 my ($title) = ($path =~ m{/([^/]+)$});
                   14232:                 $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
                   14233:                 if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
                   14234:                     if (ref($dirorder{$i}) eq 'ARRAY') {
                   14235:                         my ($itemidx,$fullpath,$relpath);
                   14236:                         if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
                   14237:                             my $container = $dirorder{$referrer{$i}}->[-1];
1.1056    raeburn  14238:                             for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
1.1086    raeburn  14239:                                 if ($dirorder{$i}->[$j] eq $container) {
                   14240:                                     $itemidx = $j;
1.1056    raeburn  14241:                                 }
                   14242:                             }
1.1086    raeburn  14243:                         }
                   14244:                         if ($itemidx eq '') {
                   14245:                             $itemidx =  0;
                   14246:                         } 
                   14247:                         if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
                   14248:                             if ($mapinner{$referrer{$i}}) {
                   14249:                                 $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
                   14250:                                 for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
                   14251:                                     if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
                   14252:                                         unless (defined($newseqid{$dirorder{$i}->[$j]})) {
                   14253:                                             $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   14254:                                             $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   14255:                                             if (!-e $fullpath) {
                   14256:                                                 mkdir($fullpath,0755);
1.1056    raeburn  14257:                                             }
                   14258:                                         }
1.1086    raeburn  14259:                                     } else {
                   14260:                                         last;
1.1056    raeburn  14261:                                     }
1.1086    raeburn  14262:                                 }
                   14263:                             }
                   14264:                         } elsif ($newdest{$referrer{$i}}) {
                   14265:                             $fullpath = $newdest{$referrer{$i}};
                   14266:                             for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
                   14267:                                 if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
                   14268:                                     $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
                   14269:                                     last;
                   14270:                                 } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
                   14271:                                     unless (defined($newseqid{$dirorder{$i}->[$j]})) {
                   14272:                                         $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   14273:                                         $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
                   14274:                                         if (!-e $fullpath) {
                   14275:                                             mkdir($fullpath,0755);
1.1056    raeburn  14276:                                         }
                   14277:                                     }
1.1086    raeburn  14278:                                 } else {
                   14279:                                     last;
1.1056    raeburn  14280:                                 }
1.1055    raeburn  14281:                             }
                   14282:                         }
1.1086    raeburn  14283:                         if ($fullpath ne '') {
                   14284:                             if (-e "$prefix$path") {
1.1292    raeburn  14285:                                 unless (rename("$prefix$path","$fullpath/$title")) {
                   14286:                                      $warning .= &mt('Failed to rename dependency').'<br />';
                   14287:                                 }
1.1086    raeburn  14288:                             }
                   14289:                             if (-e "$fullpath/$title") {
                   14290:                                 my $showpath;
                   14291:                                 if ($relpath ne '') {
                   14292:                                     $showpath = "$relpath/$title";
                   14293:                                 } else {
                   14294:                                     $showpath = "/$title";
                   14295:                                 } 
1.1294    raeburn  14296:                                 $result .= '<li>'.&mt('[_1] included as a dependency',
                   14297:                                                       &HTML::Entities::encode($showpath,'<>&"')).
                   14298:                                            '</li>'."\n";
1.1292    raeburn  14299:                                 unless ($ishome) {
                   14300:                                     my $fetch = "$fullpath/$title";
                   14301:                                     $fetch =~ s/^\Q$prefix$dir\E//; 
                   14302:                                     $prompttofetch{$fetch} = 1;
                   14303:                                 }
1.1086    raeburn  14304:                             }
                   14305:                         }
1.1055    raeburn  14306:                     }
1.1086    raeburn  14307:                 } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') {
                   14308:                     $warning .= &mt('[_1] is a dependency of [_2], which was discarded.',
1.1294    raeburn  14309:                                     &HTML::Entities::encode($path,'<>&"'),
                   14310:                                     &HTML::Entities::encode($env{'form.archive_content_'.$referrer{$i}},'<>&"')).
                   14311:                                 '<br />';
1.1055    raeburn  14312:                 }
                   14313:             } else {
1.1294    raeburn  14314:                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
1.1296    raeburn  14315:                                 &HTML::Entities::encode($path)).'<br />';
1.1055    raeburn  14316:             }
                   14317:         }
                   14318:         if (keys(%todelete)) {
                   14319:             foreach my $key (keys(%todelete)) {
                   14320:                 unlink($key);
1.1066    raeburn  14321:             }
                   14322:         }
                   14323:         if (keys(%todeletedir)) {
                   14324:             foreach my $key (keys(%todeletedir)) {
                   14325:                 rmdir($key);
                   14326:             }
                   14327:         }
                   14328:         foreach my $dir (sort(keys(%is_dir))) {
                   14329:             if (($pathtocheck ne '') && ($dir ne ''))  {
                   14330:                 &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
1.1055    raeburn  14331:             }
                   14332:         }
1.1067    raeburn  14333:         if ($result ne '') {
                   14334:             $output .= '<ul>'."\n".
                   14335:                        $result."\n".
                   14336:                        '</ul>';
                   14337:         }
                   14338:         unless ($ishome) {
                   14339:             my $replicationfail;
                   14340:             foreach my $item (keys(%prompttofetch)) {
                   14341:                 my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
                   14342:                 unless ($fetchresult eq 'ok') {
                   14343:                     $replicationfail .= '<li>'.$item.'</li>'."\n";
                   14344:                 }
                   14345:             }
                   14346:             if ($replicationfail) {
                   14347:                 $output .= '<p class="LC_error">'.
                   14348:                            &mt('Course home server failed to retrieve:').'<ul>'.
                   14349:                            $replicationfail.
                   14350:                            '</ul></p>';
                   14351:             }
                   14352:         }
1.1055    raeburn  14353:     } else {
                   14354:         $warning = &mt('No items found in archive.');
                   14355:     }
                   14356:     if ($error) {
                   14357:         $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
                   14358:                    $error.'</p>'."\n";
                   14359:     }
                   14360:     if ($warning) {
                   14361:         $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
                   14362:     }
                   14363:     return $output;
                   14364: }
                   14365: 
1.1066    raeburn  14366: sub cleanup_empty_dirs {
                   14367:     my ($path) = @_;
                   14368:     if (($path ne '') && (-d $path)) {
                   14369:         if (opendir(my $dirh,$path)) {
                   14370:             my @dircontents = grep(!/^\./,readdir($dirh));
                   14371:             my $numitems = 0;
                   14372:             foreach my $item (@dircontents) {
                   14373:                 if (-d "$path/$item") {
1.1111    raeburn  14374:                     &cleanup_empty_dirs("$path/$item");
1.1066    raeburn  14375:                     if (-e "$path/$item") {
                   14376:                         $numitems ++;
                   14377:                     }
                   14378:                 } else {
                   14379:                     $numitems ++;
                   14380:                 }
                   14381:             }
                   14382:             if ($numitems == 0) {
                   14383:                 rmdir($path);
                   14384:             }
                   14385:             closedir($dirh);
                   14386:         }
                   14387:     }
                   14388:     return;
                   14389: }
                   14390: 
1.41      ng       14391: =pod
1.45      matthew  14392: 
1.1162    raeburn  14393: =item * &get_folder_hierarchy()
1.1068    raeburn  14394: 
                   14395: Provides hierarchy of names of folders/sub-folders containing the current
                   14396: item,
                   14397: 
                   14398: Inputs: 3
                   14399:      - $navmap - navmaps object
                   14400: 
                   14401:      - $map - url for map (either the trigger itself, or map containing
                   14402:                            the resource, which is the trigger).
                   14403: 
                   14404:      - $showitem - 1 => show title for map itself; 0 => do not show.
                   14405: 
                   14406: Outputs: 1 @pathitems - array of folder/subfolder names.
                   14407: 
                   14408: =cut
                   14409: 
                   14410: sub get_folder_hierarchy {
                   14411:     my ($navmap,$map,$showitem) = @_;
                   14412:     my @pathitems;
                   14413:     if (ref($navmap)) {
                   14414:         my $mapres = $navmap->getResourceByUrl($map);
                   14415:         if (ref($mapres)) {
                   14416:             my $pcslist = $mapres->map_hierarchy();
                   14417:             if ($pcslist ne '') {
                   14418:                 my @pcs = split(/,/,$pcslist);
                   14419:                 foreach my $pc (@pcs) {
                   14420:                     if ($pc == 1) {
1.1129    raeburn  14421:                         push(@pathitems,&mt('Main Content'));
1.1068    raeburn  14422:                     } else {
                   14423:                         my $res = $navmap->getByMapPc($pc);
                   14424:                         if (ref($res)) {
                   14425:                             my $title = $res->compTitle();
                   14426:                             $title =~ s/\W+/_/g;
                   14427:                             if ($title ne '') {
                   14428:                                 push(@pathitems,$title);
                   14429:                             }
                   14430:                         }
                   14431:                     }
                   14432:                 }
                   14433:             }
1.1071    raeburn  14434:             if ($showitem) {
                   14435:                 if ($mapres->{ID} eq '0.0') {
1.1129    raeburn  14436:                     push(@pathitems,&mt('Main Content'));
1.1071    raeburn  14437:                 } else {
                   14438:                     my $maptitle = $mapres->compTitle();
                   14439:                     $maptitle =~ s/\W+/_/g;
                   14440:                     if ($maptitle ne '') {
                   14441:                         push(@pathitems,$maptitle);
                   14442:                     }
1.1068    raeburn  14443:                 }
                   14444:             }
                   14445:         }
                   14446:     }
                   14447:     return @pathitems;
                   14448: }
                   14449: 
                   14450: =pod
                   14451: 
1.1015    raeburn  14452: =item * &get_turnedin_filepath()
                   14453: 
                   14454: Determines path in a user's portfolio file for storage of files uploaded
                   14455: to a specific essayresponse or dropbox item.
                   14456: 
                   14457: Inputs: 3 required + 1 optional.
                   14458: $symb is symb for resource, $uname and $udom are for current user (required).
                   14459: $caller is optional (can be "submission", if routine is called when storing
                   14460: an upoaded file when "Submit Answer" button was pressed).
                   14461: 
                   14462: Returns array containing $path and $multiresp. 
                   14463: $path is path in portfolio.  $multiresp is 1 if this resource contains more
                   14464: than one file upload item.  Callers of routine should append partid as a 
                   14465: subdirectory to $path in cases where $multiresp is 1.
                   14466: 
                   14467: Called by: homework/essayresponse.pm and homework/structuretags.pm
                   14468: 
                   14469: =cut
                   14470: 
                   14471: sub get_turnedin_filepath {
                   14472:     my ($symb,$uname,$udom,$caller) = @_;
                   14473:     my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
                   14474:     my $turnindir;
                   14475:     my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
                   14476:     $turnindir = $userhash{'turnindir'};
                   14477:     my ($path,$multiresp);
                   14478:     if ($turnindir eq '') {
                   14479:         if ($caller eq 'submission') {
                   14480:             $turnindir = &mt('turned in');
                   14481:             $turnindir =~ s/\W+/_/g;
                   14482:             my %newhash = (
                   14483:                             'turnindir' => $turnindir,
                   14484:                           );
                   14485:             &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
                   14486:         }
                   14487:     }
                   14488:     if ($turnindir ne '') {
                   14489:         $path = '/'.$turnindir.'/';
                   14490:         my ($multipart,$turnin,@pathitems);
                   14491:         my $navmap = Apache::lonnavmaps::navmap->new();
                   14492:         if (defined($navmap)) {
                   14493:             my $mapres = $navmap->getResourceByUrl($map);
                   14494:             if (ref($mapres)) {
                   14495:                 my $pcslist = $mapres->map_hierarchy();
                   14496:                 if ($pcslist ne '') {
                   14497:                     foreach my $pc (split(/,/,$pcslist)) {
                   14498:                         my $res = $navmap->getByMapPc($pc);
                   14499:                         if (ref($res)) {
                   14500:                             my $title = $res->compTitle();
                   14501:                             $title =~ s/\W+/_/g;
                   14502:                             if ($title ne '') {
1.1149    raeburn  14503:                                 if (($pc > 1) && (length($title) > 12)) {
                   14504:                                     $title = substr($title,0,12);
                   14505:                                 }
1.1015    raeburn  14506:                                 push(@pathitems,$title);
                   14507:                             }
                   14508:                         }
                   14509:                     }
                   14510:                 }
                   14511:                 my $maptitle = $mapres->compTitle();
                   14512:                 $maptitle =~ s/\W+/_/g;
                   14513:                 if ($maptitle ne '') {
1.1149    raeburn  14514:                     if (length($maptitle) > 12) {
                   14515:                         $maptitle = substr($maptitle,0,12);
                   14516:                     }
1.1015    raeburn  14517:                     push(@pathitems,$maptitle);
                   14518:                 }
                   14519:                 unless ($env{'request.state'} eq 'construct') {
                   14520:                     my $res = $navmap->getBySymb($symb);
                   14521:                     if (ref($res)) {
                   14522:                         my $partlist = $res->parts();
                   14523:                         my $totaluploads = 0;
                   14524:                         if (ref($partlist) eq 'ARRAY') {
                   14525:                             foreach my $part (@{$partlist}) {
                   14526:                                 my @types = $res->responseType($part);
                   14527:                                 my @ids = $res->responseIds($part);
                   14528:                                 for (my $i=0; $i < scalar(@ids); $i++) {
                   14529:                                     if ($types[$i] eq 'essay') {
                   14530:                                         my $partid = $part.'_'.$ids[$i];
                   14531:                                         if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
                   14532:                                             $totaluploads ++;
                   14533:                                         }
                   14534:                                     }
                   14535:                                 }
                   14536:                             }
                   14537:                             if ($totaluploads > 1) {
                   14538:                                 $multiresp = 1;
                   14539:                             }
                   14540:                         }
                   14541:                     }
                   14542:                 }
                   14543:             } else {
                   14544:                 return;
                   14545:             }
                   14546:         } else {
                   14547:             return;
                   14548:         }
                   14549:         my $restitle=&Apache::lonnet::gettitle($symb);
                   14550:         $restitle =~ s/\W+/_/g;
                   14551:         if ($restitle eq '') {
                   14552:             $restitle = ($resurl =~ m{/[^/]+$});
                   14553:             if ($restitle eq '') {
                   14554:                 $restitle = time;
                   14555:             }
                   14556:         }
1.1149    raeburn  14557:         if (length($restitle) > 12) {
                   14558:             $restitle = substr($restitle,0,12);
                   14559:         }
1.1015    raeburn  14560:         push(@pathitems,$restitle);
                   14561:         $path .= join('/',@pathitems);
                   14562:     }
                   14563:     return ($path,$multiresp);
                   14564: }
                   14565: 
                   14566: =pod
                   14567: 
1.464     albertel 14568: =back
1.41      ng       14569: 
1.112     bowersj2 14570: =head1 CSV Upload/Handling functions
1.38      albertel 14571: 
1.41      ng       14572: =over 4
                   14573: 
1.648     raeburn  14574: =item * &upfile_store($r)
1.41      ng       14575: 
                   14576: Store uploaded file, $r should be the HTTP Request object,
1.258     albertel 14577: needs $env{'form.upfile'}
1.41      ng       14578: returns $datatoken to be put into hidden field
                   14579: 
                   14580: =cut
1.31      albertel 14581: 
                   14582: sub upfile_store {
                   14583:     my $r=shift;
1.258     albertel 14584:     $env{'form.upfile'}=~s/\r/\n/gs;
                   14585:     $env{'form.upfile'}=~s/\f/\n/gs;
                   14586:     $env{'form.upfile'}=~s/\n+/\n/gs;
                   14587:     $env{'form.upfile'}=~s/\n+$//gs;
1.31      albertel 14588: 
1.1299    raeburn  14589:     my $datatoken = &valid_datatoken($env{'user.name'}.'_'.$env{'user.domain'}.
                   14590:                                      '_enroll_'.$env{'request.course.id'}.'_'.
                   14591:                                      time.'_'.$$);
                   14592:     return if ($datatoken eq '');
                   14593: 
1.31      albertel 14594:     {
1.158     raeburn  14595:         my $datafile = $r->dir_config('lonDaemons').
                   14596:                            '/tmp/'.$datatoken.'.tmp';
1.1317    raeburn  14597:         if ( open(my $fh,'>',$datafile) ) {
1.258     albertel 14598:             print $fh $env{'form.upfile'};
1.158     raeburn  14599:             close($fh);
                   14600:         }
1.31      albertel 14601:     }
                   14602:     return $datatoken;
                   14603: }
                   14604: 
1.56      matthew  14605: =pod
                   14606: 
1.1290    raeburn  14607: =item * &load_tmp_file($r,$datatoken)
1.41      ng       14608: 
                   14609: Load uploaded file from tmp, $r should be the HTTP Request object,
1.1290    raeburn  14610: $datatoken is the name to assign to the temporary file.
1.258     albertel 14611: sets $env{'form.upfile'} to the contents of the file
1.41      ng       14612: 
                   14613: =cut
1.31      albertel 14614: 
                   14615: sub load_tmp_file {
1.1290    raeburn  14616:     my ($r,$datatoken) = @_;
                   14617:     return if ($datatoken eq '');
1.31      albertel 14618:     my @studentdata=();
                   14619:     {
1.158     raeburn  14620:         my $studentfile = $r->dir_config('lonDaemons').
1.1290    raeburn  14621:                               '/tmp/'.$datatoken.'.tmp';
1.1317    raeburn  14622:         if ( open(my $fh,'<',$studentfile) ) {
1.158     raeburn  14623:             @studentdata=<$fh>;
                   14624:             close($fh);
                   14625:         }
1.31      albertel 14626:     }
1.258     albertel 14627:     $env{'form.upfile'}=join('',@studentdata);
1.31      albertel 14628: }
                   14629: 
1.1290    raeburn  14630: sub valid_datatoken {
                   14631:     my ($datatoken) = @_;
1.1325    raeburn  14632:     if ($datatoken =~ /^$match_username\_$match_domain\_enroll_(|$match_domain\_$match_courseid)\_\d+_\d+$/) {
1.1290    raeburn  14633:         return $datatoken;
                   14634:     }
                   14635:     return;
                   14636: }
                   14637: 
1.56      matthew  14638: =pod
                   14639: 
1.648     raeburn  14640: =item * &upfile_record_sep()
1.41      ng       14641: 
                   14642: Separate uploaded file into records
                   14643: returns array of records,
1.258     albertel 14644: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41      ng       14645: 
                   14646: =cut
1.31      albertel 14647: 
                   14648: sub upfile_record_sep {
1.258     albertel 14649:     if ($env{'form.upfiletype'} eq 'xml') {
1.31      albertel 14650:     } else {
1.248     albertel 14651: 	my @records;
1.258     albertel 14652: 	foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248     albertel 14653: 	    if ($line=~/^\s*$/) { next; }
                   14654: 	    push(@records,$line);
                   14655: 	}
                   14656: 	return @records;
1.31      albertel 14657:     }
                   14658: }
                   14659: 
1.56      matthew  14660: =pod
                   14661: 
1.648     raeburn  14662: =item * &record_sep($record)
1.41      ng       14663: 
1.258     albertel 14664: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41      ng       14665: 
                   14666: =cut
                   14667: 
1.263     www      14668: sub takeleft {
                   14669:     my $index=shift;
                   14670:     return substr('0000'.$index,-4,4);
                   14671: }
                   14672: 
1.31      albertel 14673: sub record_sep {
                   14674:     my $record=shift;
                   14675:     my %components=();
1.258     albertel 14676:     if ($env{'form.upfiletype'} eq 'xml') {
                   14677:     } elsif ($env{'form.upfiletype'} eq 'space') {
1.31      albertel 14678:         my $i=0;
1.356     albertel 14679:         foreach my $field (split(/\s+/,$record)) {
1.31      albertel 14680:             $field=~s/^(\"|\')//;
                   14681:             $field=~s/(\"|\')$//;
1.263     www      14682:             $components{&takeleft($i)}=$field;
1.31      albertel 14683:             $i++;
                   14684:         }
1.258     albertel 14685:     } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31      albertel 14686:         my $i=0;
1.356     albertel 14687:         foreach my $field (split(/\t/,$record)) {
1.31      albertel 14688:             $field=~s/^(\"|\')//;
                   14689:             $field=~s/(\"|\')$//;
1.263     www      14690:             $components{&takeleft($i)}=$field;
1.31      albertel 14691:             $i++;
                   14692:         }
                   14693:     } else {
1.561     www      14694:         my $separator=',';
1.480     banghart 14695:         if ($env{'form.upfiletype'} eq 'semisv') {
1.561     www      14696:             $separator=';';
1.480     banghart 14697:         }
1.31      albertel 14698:         my $i=0;
1.561     www      14699: # the character we are looking for to indicate the end of a quote or a record 
                   14700:         my $looking_for=$separator;
                   14701: # do not add the characters to the fields
                   14702:         my $ignore=0;
                   14703: # we just encountered a separator (or the beginning of the record)
                   14704:         my $just_found_separator=1;
                   14705: # store the field we are working on here
                   14706:         my $field='';
                   14707: # work our way through all characters in record
                   14708:         foreach my $character ($record=~/(.)/g) {
                   14709:             if ($character eq $looking_for) {
                   14710:                if ($character ne $separator) {
                   14711: # Found the end of a quote, again looking for separator
                   14712:                   $looking_for=$separator;
                   14713:                   $ignore=1;
                   14714:                } else {
                   14715: # Found a separator, store away what we got
                   14716:                   $components{&takeleft($i)}=$field;
                   14717: 	          $i++;
                   14718:                   $just_found_separator=1;
                   14719:                   $ignore=0;
                   14720:                   $field='';
                   14721:                }
                   14722:                next;
                   14723:             }
                   14724: # single or double quotation marks after a separator indicate beginning of a quote
                   14725: # we are now looking for the end of the quote and need to ignore separators
                   14726:             if ((($character eq '"') || ($character eq "'")) && ($just_found_separator))  {
                   14727:                $looking_for=$character;
                   14728:                next;
                   14729:             }
                   14730: # ignore would be true after we reached the end of a quote
                   14731:             if ($ignore) { next; }
                   14732:             if (($just_found_separator) && ($character=~/\s/)) { next; }
                   14733:             $field.=$character;
                   14734:             $just_found_separator=0; 
1.31      albertel 14735:         }
1.561     www      14736: # catch the very last entry, since we never encountered the separator
                   14737:         $components{&takeleft($i)}=$field;
1.31      albertel 14738:     }
                   14739:     return %components;
                   14740: }
                   14741: 
1.144     matthew  14742: ######################################################
                   14743: ######################################################
                   14744: 
1.56      matthew  14745: =pod
                   14746: 
1.648     raeburn  14747: =item * &upfile_select_html()
1.41      ng       14748: 
1.144     matthew  14749: Return HTML code to select a file from the users machine and specify 
                   14750: the file type.
1.41      ng       14751: 
                   14752: =cut
                   14753: 
1.144     matthew  14754: ######################################################
                   14755: ######################################################
1.31      albertel 14756: sub upfile_select_html {
1.144     matthew  14757:     my %Types = (
                   14758:                  csv   => &mt('CSV (comma separated values, spreadsheet)'),
1.480     banghart 14759:                  semisv => &mt('Semicolon separated values'),
1.144     matthew  14760:                  space => &mt('Space separated'),
                   14761:                  tab   => &mt('Tabulator separated'),
                   14762: #                 xml   => &mt('HTML/XML'),
                   14763:                  );
                   14764:     my $Str = '<input type="file" name="upfile" size="50" />'.
1.727     riegler  14765:         '<br />'.&mt('Type').': <select name="upfiletype">';
1.144     matthew  14766:     foreach my $type (sort(keys(%Types))) {
                   14767:         $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
                   14768:     }
                   14769:     $Str .= "</select>\n";
                   14770:     return $Str;
1.31      albertel 14771: }
                   14772: 
1.301     albertel 14773: sub get_samples {
                   14774:     my ($records,$toget) = @_;
                   14775:     my @samples=({});
                   14776:     my $got=0;
                   14777:     foreach my $rec (@$records) {
                   14778: 	my %temp = &record_sep($rec);
                   14779: 	if (! grep(/\S/, values(%temp))) { next; }
                   14780: 	if (%temp) {
                   14781: 	    $samples[$got]=\%temp;
                   14782: 	    $got++;
                   14783: 	    if ($got == $toget) { last; }
                   14784: 	}
                   14785:     }
                   14786:     return \@samples;
                   14787: }
                   14788: 
1.144     matthew  14789: ######################################################
                   14790: ######################################################
                   14791: 
1.56      matthew  14792: =pod
                   14793: 
1.648     raeburn  14794: =item * &csv_print_samples($r,$records)
1.41      ng       14795: 
                   14796: Prints a table of sample values from each column uploaded $r is an
                   14797: Apache Request ref, $records is an arrayref from
                   14798: &Apache::loncommon::upfile_record_sep
                   14799: 
                   14800: =cut
                   14801: 
1.144     matthew  14802: ######################################################
                   14803: ######################################################
1.31      albertel 14804: sub csv_print_samples {
                   14805:     my ($r,$records) = @_;
1.662     bisitz   14806:     my $samples = &get_samples($records,5);
1.301     albertel 14807: 
1.594     raeburn  14808:     $r->print(&mt('Samples').'<br />'.&start_data_table().
                   14809:               &start_data_table_header_row());
1.356     albertel 14810:     foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { 
1.845     bisitz   14811:         $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.594     raeburn  14812:     $r->print(&end_data_table_header_row());
1.301     albertel 14813:     foreach my $hash (@$samples) {
1.594     raeburn  14814: 	$r->print(&start_data_table_row());
1.356     albertel 14815: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31      albertel 14816: 	    $r->print('<td>');
1.356     albertel 14817: 	    if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31      albertel 14818: 	    $r->print('</td>');
                   14819: 	}
1.594     raeburn  14820: 	$r->print(&end_data_table_row());
1.31      albertel 14821:     }
1.594     raeburn  14822:     $r->print(&end_data_table().'<br />'."\n");
1.31      albertel 14823: }
                   14824: 
1.144     matthew  14825: ######################################################
                   14826: ######################################################
                   14827: 
1.56      matthew  14828: =pod
                   14829: 
1.648     raeburn  14830: =item * &csv_print_select_table($r,$records,$d)
1.41      ng       14831: 
                   14832: Prints a table to create associations between values and table columns.
1.144     matthew  14833: 
1.41      ng       14834: $r is an Apache Request ref,
                   14835: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174     matthew  14836: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41      ng       14837: 
                   14838: =cut
                   14839: 
1.144     matthew  14840: ######################################################
                   14841: ######################################################
1.31      albertel 14842: sub csv_print_select_table {
                   14843:     my ($r,$records,$d) = @_;
1.301     albertel 14844:     my $i=0;
                   14845:     my $samples = &get_samples($records,1);
1.144     matthew  14846:     $r->print(&mt('Associate columns with student attributes.')."\n".
1.594     raeburn  14847: 	      &start_data_table().&start_data_table_header_row().
1.144     matthew  14848:               '<th>'.&mt('Attribute').'</th>'.
1.594     raeburn  14849:               '<th>'.&mt('Column').'</th>'.
                   14850:               &end_data_table_header_row()."\n");
1.356     albertel 14851:     foreach my $array_ref (@$d) {
                   14852: 	my ($value,$display,$defaultcol)=@{ $array_ref };
1.729     raeburn  14853: 	$r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31      albertel 14854: 
1.875     bisitz   14855: 	$r->print('<td><select name="f'.$i.'"'.
1.32      matthew  14856: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.31      albertel 14857: 	$r->print('<option value="none"></option>');
1.356     albertel 14858: 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
                   14859: 	    $r->print('<option value="'.$sample.'"'.
                   14860:                       ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662     bisitz   14861:                       '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31      albertel 14862: 	}
1.594     raeburn  14863: 	$r->print('</select></td>'.&end_data_table_row()."\n");
1.31      albertel 14864: 	$i++;
                   14865:     }
1.594     raeburn  14866:     $r->print(&end_data_table());
1.31      albertel 14867:     $i--;
                   14868:     return $i;
                   14869: }
1.56      matthew  14870: 
1.144     matthew  14871: ######################################################
                   14872: ######################################################
                   14873: 
1.56      matthew  14874: =pod
1.31      albertel 14875: 
1.648     raeburn  14876: =item * &csv_samples_select_table($r,$records,$d)
1.41      ng       14877: 
                   14878: Prints a table of sample values from the upload and can make associate samples to internal names.
                   14879: 
                   14880: $r is an Apache Request ref,
                   14881: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
                   14882: $d is an array of 2 element arrays (internal name, displayed name)
                   14883: 
                   14884: =cut
                   14885: 
1.144     matthew  14886: ######################################################
                   14887: ######################################################
1.31      albertel 14888: sub csv_samples_select_table {
                   14889:     my ($r,$records,$d) = @_;
                   14890:     my $i=0;
1.144     matthew  14891:     #
1.662     bisitz   14892:     my $max_samples = 5;
                   14893:     my $samples = &get_samples($records,$max_samples);
1.594     raeburn  14894:     $r->print(&start_data_table().
                   14895:               &start_data_table_header_row().'<th>'.
                   14896:               &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
                   14897:               &end_data_table_header_row());
1.301     albertel 14898: 
                   14899:     foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594     raeburn  14900: 	$r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32      matthew  14901: 		  ' onchange="javascript:flip(this.form,'.$i.');">');
1.301     albertel 14902: 	foreach my $option (@$d) {
                   14903: 	    my ($value,$display,$defaultcol)=@{ $option };
1.174     matthew  14904: 	    $r->print('<option value="'.$value.'"'.
1.253     albertel 14905:                       ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174     matthew  14906:                       $display.'</option>');
1.31      albertel 14907: 	}
                   14908: 	$r->print('</select></td><td>');
1.662     bisitz   14909: 	foreach my $line (0..($max_samples-1)) {
1.301     albertel 14910: 	    if (defined($samples->[$line]{$key})) { 
                   14911: 		$r->print($samples->[$line]{$key}."<br />\n"); 
                   14912: 	    }
                   14913: 	}
1.594     raeburn  14914: 	$r->print('</td>'.&end_data_table_row());
1.31      albertel 14915: 	$i++;
                   14916:     }
1.594     raeburn  14917:     $r->print(&end_data_table());
1.31      albertel 14918:     $i--;
                   14919:     return($i);
1.115     matthew  14920: }
                   14921: 
1.144     matthew  14922: ######################################################
                   14923: ######################################################
                   14924: 
1.115     matthew  14925: =pod
                   14926: 
1.648     raeburn  14927: =item * &clean_excel_name($name)
1.115     matthew  14928: 
                   14929: Returns a replacement for $name which does not contain any illegal characters.
                   14930: 
                   14931: =cut
                   14932: 
1.144     matthew  14933: ######################################################
                   14934: ######################################################
1.115     matthew  14935: sub clean_excel_name {
                   14936:     my ($name) = @_;
                   14937:     $name =~ s/[:\*\?\/\\]//g;
                   14938:     if (length($name) > 31) {
                   14939:         $name = substr($name,0,31);
                   14940:     }
                   14941:     return $name;
1.25      albertel 14942: }
1.84      albertel 14943: 
1.85      albertel 14944: =pod
                   14945: 
1.648     raeburn  14946: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85      albertel 14947: 
                   14948: Returns either 1 or undef
                   14949: 
                   14950: 1 if the part is to be hidden, undef if it is to be shown
                   14951: 
                   14952: Arguments are:
                   14953: 
                   14954: $id the id of the part to be checked
                   14955: $symb, optional the symb of the resource to check
                   14956: $udom, optional the domain of the user to check for
                   14957: $uname, optional the username of the user to check for
                   14958: 
                   14959: =cut
1.84      albertel 14960: 
                   14961: sub check_if_partid_hidden {
                   14962:     my ($id,$symb,$udom,$uname) = @_;
1.133     albertel 14963:     my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84      albertel 14964: 					 $symb,$udom,$uname);
1.141     albertel 14965:     my $truth=1;
                   14966:     #if the string starts with !, then the list is the list to show not hide
                   14967:     if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84      albertel 14968:     my @hiddenlist=split(/,/,$hiddenparts);
                   14969:     foreach my $checkid (@hiddenlist) {
1.141     albertel 14970: 	if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84      albertel 14971:     }
1.141     albertel 14972:     return !$truth;
1.84      albertel 14973: }
1.127     matthew  14974: 
1.138     matthew  14975: 
                   14976: ############################################################
                   14977: ############################################################
                   14978: 
                   14979: =pod
                   14980: 
1.157     matthew  14981: =back 
                   14982: 
1.138     matthew  14983: =head1 cgi-bin script and graphing routines
                   14984: 
1.157     matthew  14985: =over 4
                   14986: 
1.648     raeburn  14987: =item * &get_cgi_id()
1.138     matthew  14988: 
                   14989: Inputs: none
                   14990: 
                   14991: Returns an id which can be used to pass environment variables
                   14992: to various cgi-bin scripts.  These environment variables will
                   14993: be removed from the users environment after a given time by
                   14994: the routine &Apache::lonnet::transfer_profile_to_env.
                   14995: 
                   14996: =cut
                   14997: 
                   14998: ############################################################
                   14999: ############################################################
1.152     albertel 15000: my $uniq=0;
1.136     matthew  15001: sub get_cgi_id {
1.154     albertel 15002:     $uniq=($uniq+1)%100000;
1.280     albertel 15003:     return (time.'_'.$$.'_'.$uniq);
1.136     matthew  15004: }
                   15005: 
1.127     matthew  15006: ############################################################
                   15007: ############################################################
                   15008: 
                   15009: =pod
                   15010: 
1.648     raeburn  15011: =item * &DrawBarGraph()
1.127     matthew  15012: 
1.138     matthew  15013: Facilitates the plotting of data in a (stacked) bar graph.
                   15014: Puts plot definition data into the users environment in order for 
                   15015: graph.png to plot it.  Returns an <img> tag for the plot.
                   15016: The bars on the plot are labeled '1','2',...,'n'.
                   15017: 
                   15018: Inputs:
                   15019: 
                   15020: =over 4
                   15021: 
                   15022: =item $Title: string, the title of the plot
                   15023: 
                   15024: =item $xlabel: string, text describing the X-axis of the plot
                   15025: 
                   15026: =item $ylabel: string, text describing the Y-axis of the plot
                   15027: 
                   15028: =item $Max: scalar, the maximum Y value to use in the plot
                   15029: If $Max is < any data point, the graph will not be rendered.
                   15030: 
1.140     matthew  15031: =item $colors: array ref holding the colors to be used for the data sets when
1.138     matthew  15032: they are plotted.  If undefined, default values will be used.
                   15033: 
1.178     matthew  15034: =item $labels: array ref holding the labels to use on the x-axis for the bars.
                   15035: 
1.138     matthew  15036: =item @Values: An array of array references.  Each array reference holds data
                   15037: to be plotted in a stacked bar chart.
                   15038: 
1.239     matthew  15039: =item If the final element of @Values is a hash reference the key/value
                   15040: pairs will be added to the graph definition.
                   15041: 
1.138     matthew  15042: =back
                   15043: 
                   15044: Returns:
                   15045: 
                   15046: An <img> tag which references graph.png and the appropriate identifying
                   15047: information for the plot.
                   15048: 
1.127     matthew  15049: =cut
                   15050: 
                   15051: ############################################################
                   15052: ############################################################
1.134     matthew  15053: sub DrawBarGraph {
1.178     matthew  15054:     my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134     matthew  15055:     #
                   15056:     if (! defined($colors)) {
                   15057:         $colors = ['#33ff00', 
                   15058:                   '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
                   15059:                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                   15060:                   ]; 
                   15061:     }
1.228     matthew  15062:     my $extra_settings = {};
                   15063:     if (ref($Values[-1]) eq 'HASH') {
                   15064:         $extra_settings = pop(@Values);
                   15065:     }
1.127     matthew  15066:     #
1.136     matthew  15067:     my $identifier = &get_cgi_id();
                   15068:     my $id = 'cgi.'.$identifier;        
1.129     matthew  15069:     if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127     matthew  15070:         return '';
                   15071:     }
1.225     matthew  15072:     #
                   15073:     my @Labels;
                   15074:     if (defined($labels)) {
                   15075:         @Labels = @$labels;
                   15076:     } else {
                   15077:         for (my $i=0;$i<@{$Values[0]};$i++) {
1.1263    raeburn  15078:             push(@Labels,$i+1);
1.225     matthew  15079:         }
                   15080:     }
                   15081:     #
1.129     matthew  15082:     my $NumBars = scalar(@{$Values[0]});
1.225     matthew  15083:     if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129     matthew  15084:     my %ValuesHash;
                   15085:     my $NumSets=1;
                   15086:     foreach my $array (@Values) {
                   15087:         next if (! ref($array));
1.136     matthew  15088:         $ValuesHash{$id.'.data.'.$NumSets++} = 
1.132     matthew  15089:             join(',',@$array);
1.129     matthew  15090:     }
1.127     matthew  15091:     #
1.136     matthew  15092:     my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225     matthew  15093:     if ($NumBars < 3) {
                   15094:         $width = 120+$NumBars*32;
1.220     matthew  15095:         $xskip = 1;
1.225     matthew  15096:         $bar_width = 30;
                   15097:     } elsif ($NumBars < 5) {
                   15098:         $width = 120+$NumBars*20;
                   15099:         $xskip = 1;
                   15100:         $bar_width = 20;
1.220     matthew  15101:     } elsif ($NumBars < 10) {
1.136     matthew  15102:         $width = 120+$NumBars*15;
                   15103:         $xskip = 1;
                   15104:         $bar_width = 15;
                   15105:     } elsif ($NumBars <= 25) {
                   15106:         $width = 120+$NumBars*11;
                   15107:         $xskip = 5;
                   15108:         $bar_width = 8;
                   15109:     } elsif ($NumBars <= 50) {
                   15110:         $width = 120+$NumBars*8;
                   15111:         $xskip = 5;
                   15112:         $bar_width = 4;
                   15113:     } else {
                   15114:         $width = 120+$NumBars*8;
                   15115:         $xskip = 5;
                   15116:         $bar_width = 4;
                   15117:     }
                   15118:     #
1.137     matthew  15119:     $Max = 1 if ($Max < 1);
                   15120:     if ( int($Max) < $Max ) {
                   15121:         $Max++;
                   15122:         $Max = int($Max);
                   15123:     }
1.127     matthew  15124:     $Title  = '' if (! defined($Title));
                   15125:     $xlabel = '' if (! defined($xlabel));
                   15126:     $ylabel = '' if (! defined($ylabel));
1.369     www      15127:     $ValuesHash{$id.'.title'}    = &escape($Title);
                   15128:     $ValuesHash{$id.'.xlabel'}   = &escape($xlabel);
                   15129:     $ValuesHash{$id.'.ylabel'}   = &escape($ylabel);
1.137     matthew  15130:     $ValuesHash{$id.'.y_max_value'} = $Max;
1.136     matthew  15131:     $ValuesHash{$id.'.NumBars'}  = $NumBars;
                   15132:     $ValuesHash{$id.'.NumSets'}  = $NumSets;
                   15133:     $ValuesHash{$id.'.PlotType'} = 'bar';
                   15134:     $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   15135:     $ValuesHash{$id.'.height'}   = $height;
                   15136:     $ValuesHash{$id.'.width'}    = $width;
                   15137:     $ValuesHash{$id.'.xskip'}    = $xskip;
                   15138:     $ValuesHash{$id.'.bar_width'} = $bar_width;
                   15139:     $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127     matthew  15140:     #
1.228     matthew  15141:     # Deal with other parameters
                   15142:     while (my ($key,$value) = each(%$extra_settings)) {
                   15143:         $ValuesHash{$id.'.'.$key} = $value;
                   15144:     }
                   15145:     #
1.646     raeburn  15146:     &Apache::lonnet::appenv(\%ValuesHash);
1.137     matthew  15147:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   15148: }
                   15149: 
                   15150: ############################################################
                   15151: ############################################################
                   15152: 
                   15153: =pod
                   15154: 
1.648     raeburn  15155: =item * &DrawXYGraph()
1.137     matthew  15156: 
1.138     matthew  15157: Facilitates the plotting of data in an XY graph.
                   15158: Puts plot definition data into the users environment in order for 
                   15159: graph.png to plot it.  Returns an <img> tag for the plot.
                   15160: 
                   15161: Inputs:
                   15162: 
                   15163: =over 4
                   15164: 
                   15165: =item $Title: string, the title of the plot
                   15166: 
                   15167: =item $xlabel: string, text describing the X-axis of the plot
                   15168: 
                   15169: =item $ylabel: string, text describing the Y-axis of the plot
                   15170: 
                   15171: =item $Max: scalar, the maximum Y value to use in the plot
                   15172: If $Max is < any data point, the graph will not be rendered.
                   15173: 
                   15174: =item $colors: Array ref containing the hex color codes for the data to be 
                   15175: plotted in.  If undefined, default values will be used.
                   15176: 
                   15177: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   15178: 
                   15179: =item $Ydata: Array ref containing Array refs.  
1.185     www      15180: Each of the contained arrays will be plotted as a separate curve.
1.138     matthew  15181: 
                   15182: =item %Values: hash indicating or overriding any default values which are 
                   15183: passed to graph.png.  
                   15184: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   15185: 
                   15186: =back
                   15187: 
                   15188: Returns:
                   15189: 
                   15190: An <img> tag which references graph.png and the appropriate identifying
                   15191: information for the plot.
                   15192: 
1.137     matthew  15193: =cut
                   15194: 
                   15195: ############################################################
                   15196: ############################################################
                   15197: sub DrawXYGraph {
                   15198:     my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
                   15199:     #
                   15200:     # Create the identifier for the graph
                   15201:     my $identifier = &get_cgi_id();
                   15202:     my $id = 'cgi.'.$identifier;
                   15203:     #
                   15204:     $Title  = '' if (! defined($Title));
                   15205:     $xlabel = '' if (! defined($xlabel));
                   15206:     $ylabel = '' if (! defined($ylabel));
                   15207:     my %ValuesHash = 
                   15208:         (
1.369     www      15209:          $id.'.title'  => &escape($Title),
                   15210:          $id.'.xlabel' => &escape($xlabel),
                   15211:          $id.'.ylabel' => &escape($ylabel),
1.137     matthew  15212:          $id.'.y_max_value'=> $Max,
                   15213:          $id.'.labels'     => join(',',@$Xlabels),
                   15214:          $id.'.PlotType'   => 'XY',
                   15215:          );
                   15216:     #
                   15217:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   15218:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   15219:     }
                   15220:     #
                   15221:     if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
                   15222:         return '';
                   15223:     }
                   15224:     my $NumSets=1;
1.138     matthew  15225:     foreach my $array (@{$Ydata}){
1.137     matthew  15226:         next if (! ref($array));
                   15227:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
                   15228:     }
1.138     matthew  15229:     $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137     matthew  15230:     #
                   15231:     # Deal with other parameters
                   15232:     while (my ($key,$value) = each(%Values)) {
                   15233:         $ValuesHash{$id.'.'.$key} = $value;
1.127     matthew  15234:     }
                   15235:     #
1.646     raeburn  15236:     &Apache::lonnet::appenv(\%ValuesHash);
1.136     matthew  15237:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
                   15238: }
                   15239: 
                   15240: ############################################################
                   15241: ############################################################
                   15242: 
                   15243: =pod
                   15244: 
1.648     raeburn  15245: =item * &DrawXYYGraph()
1.138     matthew  15246: 
                   15247: Facilitates the plotting of data in an XY graph with two Y axes.
                   15248: Puts plot definition data into the users environment in order for 
                   15249: graph.png to plot it.  Returns an <img> tag for the plot.
                   15250: 
                   15251: Inputs:
                   15252: 
                   15253: =over 4
                   15254: 
                   15255: =item $Title: string, the title of the plot
                   15256: 
                   15257: =item $xlabel: string, text describing the X-axis of the plot
                   15258: 
                   15259: =item $ylabel: string, text describing the Y-axis of the plot
                   15260: 
                   15261: =item $colors: Array ref containing the hex color codes for the data to be 
                   15262: plotted in.  If undefined, default values will be used.
                   15263: 
                   15264: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
                   15265: 
                   15266: =item $Ydata1: The first data set
                   15267: 
                   15268: =item $Min1: The minimum value of the left Y-axis
                   15269: 
                   15270: =item $Max1: The maximum value of the left Y-axis
                   15271: 
                   15272: =item $Ydata2: The second data set
                   15273: 
                   15274: =item $Min2: The minimum value of the right Y-axis
                   15275: 
                   15276: =item $Max2: The maximum value of the left Y-axis
                   15277: 
                   15278: =item %Values: hash indicating or overriding any default values which are 
                   15279: passed to graph.png.  
                   15280: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
                   15281: 
                   15282: =back
                   15283: 
                   15284: Returns:
                   15285: 
                   15286: An <img> tag which references graph.png and the appropriate identifying
                   15287: information for the plot.
1.136     matthew  15288: 
                   15289: =cut
                   15290: 
                   15291: ############################################################
                   15292: ############################################################
1.137     matthew  15293: sub DrawXYYGraph {
                   15294:     my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
                   15295:                                         $Ydata2,$Min2,$Max2,%Values)=@_;
1.136     matthew  15296:     #
                   15297:     # Create the identifier for the graph
                   15298:     my $identifier = &get_cgi_id();
                   15299:     my $id = 'cgi.'.$identifier;
                   15300:     #
                   15301:     $Title  = '' if (! defined($Title));
                   15302:     $xlabel = '' if (! defined($xlabel));
                   15303:     $ylabel = '' if (! defined($ylabel));
                   15304:     my %ValuesHash = 
                   15305:         (
1.369     www      15306:          $id.'.title'  => &escape($Title),
                   15307:          $id.'.xlabel' => &escape($xlabel),
                   15308:          $id.'.ylabel' => &escape($ylabel),
1.136     matthew  15309:          $id.'.labels' => join(',',@$Xlabels),
                   15310:          $id.'.PlotType' => 'XY',
                   15311:          $id.'.NumSets' => 2,
1.137     matthew  15312:          $id.'.two_axes' => 1,
                   15313:          $id.'.y1_max_value' => $Max1,
                   15314:          $id.'.y1_min_value' => $Min1,
                   15315:          $id.'.y2_max_value' => $Max2,
                   15316:          $id.'.y2_min_value' => $Min2,
1.136     matthew  15317:          );
                   15318:     #
1.137     matthew  15319:     if (defined($colors) && ref($colors) eq 'ARRAY') {
                   15320:         $ValuesHash{$id.'.Colors'}   = join(',',@{$colors});
                   15321:     }
                   15322:     #
                   15323:     if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
                   15324:         ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136     matthew  15325:         return '';
                   15326:     }
                   15327:     my $NumSets=1;
1.137     matthew  15328:     foreach my $array ($Ydata1,$Ydata2){
1.136     matthew  15329:         next if (! ref($array));
                   15330:         $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137     matthew  15331:     }
                   15332:     #
                   15333:     # Deal with other parameters
                   15334:     while (my ($key,$value) = each(%Values)) {
                   15335:         $ValuesHash{$id.'.'.$key} = $value;
1.136     matthew  15336:     }
                   15337:     #
1.646     raeburn  15338:     &Apache::lonnet::appenv(\%ValuesHash);
1.130     albertel 15339:     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139     matthew  15340: }
                   15341: 
                   15342: ############################################################
                   15343: ############################################################
                   15344: 
                   15345: =pod
                   15346: 
1.157     matthew  15347: =back 
                   15348: 
1.139     matthew  15349: =head1 Statistics helper routines?  
                   15350: 
                   15351: Bad place for them but what the hell.
                   15352: 
1.157     matthew  15353: =over 4
                   15354: 
1.648     raeburn  15355: =item * &chartlink()
1.139     matthew  15356: 
                   15357: Returns a link to the chart for a specific student.  
                   15358: 
                   15359: Inputs:
                   15360: 
                   15361: =over 4
                   15362: 
                   15363: =item $linktext: The text of the link
                   15364: 
                   15365: =item $sname: The students username
                   15366: 
                   15367: =item $sdomain: The students domain
                   15368: 
                   15369: =back
                   15370: 
1.157     matthew  15371: =back
                   15372: 
1.139     matthew  15373: =cut
                   15374: 
                   15375: ############################################################
                   15376: ############################################################
                   15377: sub chartlink {
                   15378:     my ($linktext, $sname, $sdomain) = @_;
                   15379:     my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369     www      15380:         '&amp;SelectedStudent='.&escape($sname.':'.$sdomain).
1.219     albertel 15381:         '&amp;chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139     matthew  15382:        '">'.$linktext.'</a>';
1.153     matthew  15383: }
                   15384: 
                   15385: #######################################################
                   15386: #######################################################
                   15387: 
                   15388: =pod
                   15389: 
                   15390: =head1 Course Environment Routines
1.157     matthew  15391: 
                   15392: =over 4
1.153     matthew  15393: 
1.648     raeburn  15394: =item * &restore_course_settings()
1.153     matthew  15395: 
1.648     raeburn  15396: =item * &store_course_settings()
1.153     matthew  15397: 
                   15398: Restores/Store indicated form parameters from the course environment.
                   15399: Will not overwrite existing values of the form parameters.
                   15400: 
                   15401: Inputs: 
                   15402: a scalar describing the data (e.g. 'chart', 'problem_analysis')
                   15403: 
                   15404: a hash ref describing the data to be stored.  For example:
                   15405:    
                   15406: %Save_Parameters = ('Status' => 'scalar',
                   15407:     'chartoutputmode' => 'scalar',
                   15408:     'chartoutputdata' => 'scalar',
                   15409:     'Section' => 'array',
1.373     raeburn  15410:     'Group' => 'array',
1.153     matthew  15411:     'StudentData' => 'array',
                   15412:     'Maps' => 'array');
                   15413: 
                   15414: Returns: both routines return nothing
                   15415: 
1.631     raeburn  15416: =back
                   15417: 
1.153     matthew  15418: =cut
                   15419: 
                   15420: #######################################################
                   15421: #######################################################
                   15422: sub store_course_settings {
1.496     albertel 15423:     return &store_settings($env{'request.course.id'},@_);
                   15424: }
                   15425: 
                   15426: sub store_settings {
1.153     matthew  15427:     # save to the environment
                   15428:     # appenv the same items, just to be safe
1.300     albertel 15429:     my $udom  = $env{'user.domain'};
                   15430:     my $uname = $env{'user.name'};
1.496     albertel 15431:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  15432:     my %SaveHash;
                   15433:     my %AppHash;
                   15434:     while (my ($setting,$type) = each(%$Settings)) {
1.496     albertel 15435:         my $basename = join('.','internal',$context,$prefix,$setting);
1.300     albertel 15436:         my $envname = 'environment.'.$basename;
1.258     albertel 15437:         if (exists($env{'form.'.$setting})) {
1.153     matthew  15438:             # Save this value away
                   15439:             if ($type eq 'scalar' &&
1.258     albertel 15440:                 (! exists($env{$envname}) || 
                   15441:                  $env{$envname} ne $env{'form.'.$setting})) {
                   15442:                 $SaveHash{$basename} = $env{'form.'.$setting};
                   15443:                 $AppHash{$envname}   = $env{'form.'.$setting};
1.153     matthew  15444:             } elsif ($type eq 'array') {
                   15445:                 my $stored_form;
1.258     albertel 15446:                 if (ref($env{'form.'.$setting})) {
1.153     matthew  15447:                     $stored_form = join(',',
                   15448:                                         map {
1.369     www      15449:                                             &escape($_);
1.258     albertel 15450:                                         } sort(@{$env{'form.'.$setting}}));
1.153     matthew  15451:                 } else {
                   15452:                     $stored_form = 
1.369     www      15453:                         &escape($env{'form.'.$setting});
1.153     matthew  15454:                 }
                   15455:                 # Determine if the array contents are the same.
1.258     albertel 15456:                 if ($stored_form ne $env{$envname}) {
1.153     matthew  15457:                     $SaveHash{$basename} = $stored_form;
                   15458:                     $AppHash{$envname}   = $stored_form;
                   15459:                 }
                   15460:             }
                   15461:         }
                   15462:     }
                   15463:     my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300     albertel 15464:                                           $udom,$uname);
1.153     matthew  15465:     if ($put_result !~ /^(ok|delayed)/) {
                   15466:         &Apache::lonnet::logthis('unable to save form parameters, '.
                   15467:                                  'got error:'.$put_result);
                   15468:     }
                   15469:     # Make sure these settings stick around in this session, too
1.646     raeburn  15470:     &Apache::lonnet::appenv(\%AppHash);
1.153     matthew  15471:     return;
                   15472: }
                   15473: 
                   15474: sub restore_course_settings {
1.499     albertel 15475:     return &restore_settings($env{'request.course.id'},@_);
1.496     albertel 15476: }
                   15477: 
                   15478: sub restore_settings {
                   15479:     my ($context,$prefix,$Settings) = @_;
1.153     matthew  15480:     while (my ($setting,$type) = each(%$Settings)) {
1.258     albertel 15481:         next if (exists($env{'form.'.$setting}));
1.496     albertel 15482:         my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153     matthew  15483:             '.'.$setting;
1.258     albertel 15484:         if (exists($env{$envname})) {
1.153     matthew  15485:             if ($type eq 'scalar') {
1.258     albertel 15486:                 $env{'form.'.$setting} = $env{$envname};
1.153     matthew  15487:             } elsif ($type eq 'array') {
1.258     albertel 15488:                 $env{'form.'.$setting} = [ 
1.153     matthew  15489:                                            map { 
1.369     www      15490:                                                &unescape($_); 
1.258     albertel 15491:                                            } split(',',$env{$envname})
1.153     matthew  15492:                                            ];
                   15493:             }
                   15494:         }
                   15495:     }
1.127     matthew  15496: }
                   15497: 
1.618     raeburn  15498: #######################################################
                   15499: #######################################################
                   15500: 
                   15501: =pod
                   15502: 
                   15503: =head1 Domain E-mail Routines  
                   15504: 
                   15505: =over 4
                   15506: 
1.648     raeburn  15507: =item * &build_recipient_list()
1.618     raeburn  15508: 
1.1144    raeburn  15509: Build recipient lists for following types of e-mail:
1.766     raeburn  15510: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.1144    raeburn  15511: (d) Help requests, (e) Course requests needing approval, (f) loncapa
                   15512: module change checking, student/employee ID conflict checks, as
                   15513: generated by lonerrorhandler.pm, CHECKRPMS, loncron,
                   15514: lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
1.618     raeburn  15515: 
                   15516: Inputs:
1.619     raeburn  15517: defmail (scalar - email address of default recipient), 
1.1144    raeburn  15518: mailing type (scalar: errormail, packagesmail, helpdeskmail,
                   15519: requestsmail, updatesmail, or idconflictsmail).
                   15520: 
1.619     raeburn  15521: defdom (domain for which to retrieve configuration settings),
1.1144    raeburn  15522: 
1.619     raeburn  15523: origmail (scalar - email address of recipient from loncapa.conf, 
1.1297    raeburn  15524: i.e., predates configuration by DC via domainprefs.pm
                   15525: 
                   15526: $requname username of requester (if mailing type is helpdeskmail)
                   15527: 
                   15528: $requdom domain of requester (if mailing type is helpdeskmail)
                   15529: 
                   15530: $reqemail e-mail address of requester (if mailing type is helpdeskmail)
                   15531: 
1.618     raeburn  15532: 
1.655     raeburn  15533: Returns: comma separated list of addresses to which to send e-mail.
                   15534: 
                   15535: =back
1.618     raeburn  15536: 
                   15537: =cut
                   15538: 
                   15539: ############################################################
                   15540: ############################################################
                   15541: sub build_recipient_list {
1.1297    raeburn  15542:     my ($defmail,$mailing,$defdom,$origmail,$requname,$requdom,$reqemail) = @_;
1.618     raeburn  15543:     my @recipients;
1.1270    raeburn  15544:     my ($otheremails,$lastresort,$allbcc,$addtext);
1.618     raeburn  15545:     my %domconfig =
1.1270    raeburn  15546:         &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
1.618     raeburn  15547:     if (ref($domconfig{'contacts'}) eq 'HASH') {
1.766     raeburn  15548:         if (exists($domconfig{'contacts'}{$mailing})) {
                   15549:             if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
                   15550:                 my @contacts = ('adminemail','supportemail');
                   15551:                 foreach my $item (@contacts) {
                   15552:                     if ($domconfig{'contacts'}{$mailing}{$item}) {
                   15553:                         my $addr = $domconfig{'contacts'}{$item}; 
                   15554:                         if (!grep(/^\Q$addr\E$/,@recipients)) {
                   15555:                             push(@recipients,$addr);
                   15556:                         }
1.619     raeburn  15557:                     }
1.1270    raeburn  15558:                 }
                   15559:                 $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
                   15560:                 if ($mailing eq 'helpdeskmail') {
                   15561:                     if ($domconfig{'contacts'}{$mailing}{'bcc'}) {
                   15562:                         my @bccs = split(/,/,$domconfig{'contacts'}{$mailing}{'bcc'});
                   15563:                         my @ok_bccs;
                   15564:                         foreach my $bcc (@bccs) {
                   15565:                             $bcc =~ s/^\s+//g;
                   15566:                             $bcc =~ s/\s+$//g;
                   15567:                             if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
                   15568:                                 if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
                   15569:                                     push(@ok_bccs,$bcc);
                   15570:                                 }
                   15571:                             }
                   15572:                         }
                   15573:                         if (@ok_bccs > 0) {
                   15574:                             $allbcc = join(', ',@ok_bccs);
                   15575:                         }
                   15576:                     }
                   15577:                     $addtext = $domconfig{'contacts'}{$mailing}{'include'};
1.618     raeburn  15578:                 }
                   15579:             }
1.766     raeburn  15580:         } elsif ($origmail ne '') {
1.1270    raeburn  15581:             $lastresort = $origmail;
1.618     raeburn  15582:         }
1.1297    raeburn  15583:         if ($mailing eq 'helpdeskmail') {
                   15584:             if ((ref($domconfig{'contacts'}{'overrides'}) eq 'HASH') &&
                   15585:                 (keys(%{$domconfig{'contacts'}{'overrides'}}))) {
                   15586:                 my ($inststatus,$inststatus_checked);
                   15587:                 if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '') &&
                   15588:                     ($env{'user.domain'} ne 'public')) {
                   15589:                     $inststatus_checked = 1;
                   15590:                     $inststatus = $env{'environment.inststatus'};
                   15591:                 }
                   15592:                 unless ($inststatus_checked) {
                   15593:                     if (($requname ne '') && ($requdom ne '')) {
                   15594:                         if (($requname =~ /^$match_username$/) &&
                   15595:                             ($requdom =~ /^$match_domain$/) &&
                   15596:                             (&Apache::lonnet::domain($requdom))) {
                   15597:                             my $requhome = &Apache::lonnet::homeserver($requname,
                   15598:                                                                       $requdom);
                   15599:                             unless ($requhome eq 'no_host') {
                   15600:                                 my %userenv = &Apache::lonnet::userenvironment($requdom,$requname,'inststatus');
                   15601:                                 $inststatus = $userenv{'inststatus'};
                   15602:                                 $inststatus_checked = 1;
                   15603:                             }
                   15604:                         }
                   15605:                     }
                   15606:                 }
                   15607:                 unless ($inststatus_checked) {
                   15608:                     if ($reqemail =~ /^[^\@]+\@[^\@]+$/) {
                   15609:                         my %srch = (srchby     => 'email',
                   15610:                                     srchdomain => $defdom,
                   15611:                                     srchterm   => $reqemail,
                   15612:                                     srchtype   => 'exact');
                   15613:                         my %srch_results = &Apache::lonnet::usersearch(\%srch);
                   15614:                         foreach my $uname (keys(%srch_results)) {
                   15615:                             if (ref($srch_results{$uname}{'inststatus'}) eq 'ARRAY') {
                   15616:                                 $inststatus = join(',',@{$srch_results{$uname}{'inststatus'}});
                   15617:                                 $inststatus_checked = 1;
                   15618:                                 last;
                   15619:                             }
                   15620:                         }
                   15621:                         unless ($inststatus_checked) {
                   15622:                             my ($dirsrchres,%srch_results) = &Apache::lonnet::inst_directory_query(\%srch);
                   15623:                             if ($dirsrchres eq 'ok') {
                   15624:                                 foreach my $uname (keys(%srch_results)) {
                   15625:                                     if (ref($srch_results{$uname}{'inststatus'}) eq 'ARRAY') {
                   15626:                                         $inststatus = join(',',@{$srch_results{$uname}{'inststatus'}});
                   15627:                                         $inststatus_checked = 1;
                   15628:                                         last;
                   15629:                                     }
                   15630:                                 }
                   15631:                             }
                   15632:                         }
                   15633:                     }
                   15634:                 }
                   15635:                 if ($inststatus ne '') {
                   15636:                     foreach my $status (split(/\:/,$inststatus)) {
                   15637:                         if (ref($domconfig{'contacts'}{'overrides'}{$status}) eq 'HASH') {
                   15638:                             my @contacts = ('adminemail','supportemail');
                   15639:                             foreach my $item (@contacts) {
                   15640:                                 if ($domconfig{'contacts'}{'overrides'}{$status}{$item}) {
                   15641:                                     my $addr = $domconfig{'contacts'}{'overrides'}{$status};
                   15642:                                     if (!grep(/^\Q$addr\E$/,@recipients)) {
                   15643:                                         push(@recipients,$addr);
                   15644:                                     }
                   15645:                                 }
                   15646:                             }
                   15647:                             $otheremails = $domconfig{'contacts'}{'overrides'}{$status}{'others'};
                   15648:                             if ($domconfig{'contacts'}{'overrides'}{$status}{'bcc'}) {
                   15649:                                 my @bccs = split(/,/,$domconfig{'contacts'}{'overrides'}{$status}{'bcc'});
                   15650:                                 my @ok_bccs;
                   15651:                                 foreach my $bcc (@bccs) {
                   15652:                                     $bcc =~ s/^\s+//g;
                   15653:                                     $bcc =~ s/\s+$//g;
                   15654:                                     if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
                   15655:                                         if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
                   15656:                                             push(@ok_bccs,$bcc);
                   15657:                                         }
                   15658:                                     }
                   15659:                                 }
                   15660:                                 if (@ok_bccs > 0) {
                   15661:                                     $allbcc = join(', ',@ok_bccs);
                   15662:                                 }
                   15663:                             }
                   15664:                             $addtext = $domconfig{'contacts'}{'overrides'}{$status}{'include'};
                   15665:                             last;
                   15666:                         }
                   15667:                     }
                   15668:                 }
                   15669:             }
                   15670:         }
1.619     raeburn  15671:     } elsif ($origmail ne '') {
1.1270    raeburn  15672:         $lastresort = $origmail;
                   15673:     }
1.1297    raeburn  15674:     if (($mailing eq 'helpdeskmail') && ($lastresort ne '')) {
1.1270    raeburn  15675:         unless (grep(/^\Q$defdom\E$/,&Apache::lonnet::current_machine_domains())) {
                   15676:             my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
                   15677:             my $machinedom = $Apache::lonnet::perlvar{'lonDefDomain'};
                   15678:             my %what = (
                   15679:                           perlvar => 1,
                   15680:                        );
                   15681:             my $primary = &Apache::lonnet::domain($defdom,'primary');
                   15682:             if ($primary) {
                   15683:                 my $gotaddr;
                   15684:                 my ($result,$returnhash) =
                   15685:                     &Apache::lonnet::get_remote_globals($primary,{ perlvar => 1 });
                   15686:                 if (($result eq 'ok') && (ref($returnhash) eq 'HASH')) {
                   15687:                     if ($returnhash->{'lonSupportEMail'} =~ /^[^\@]+\@[^\@]+$/) {
                   15688:                         $lastresort = $returnhash->{'lonSupportEMail'};
                   15689:                         $gotaddr = 1;
                   15690:                     }
                   15691:                 }
                   15692:                 unless ($gotaddr) {
                   15693:                     my $uintdom = &Apache::lonnet::internet_dom($primary);
                   15694:                     my $intdom = &Apache::lonnet::internet_dom($lonhost);
                   15695:                     unless ($uintdom eq $intdom) {
                   15696:                         my %domconfig =
                   15697:                             &Apache::lonnet::get_dom('configuration',['contacts'],$machinedom);
                   15698:                         if (ref($domconfig{'contacts'}) eq 'HASH') {
                   15699:                             if (ref($domconfig{'contacts'}{'otherdomsmail'}) eq 'HASH') {
                   15700:                                 my @contacts = ('adminemail','supportemail');
                   15701:                                 foreach my $item (@contacts) {
                   15702:                                     if ($domconfig{'contacts'}{'otherdomsmail'}{$item}) {
                   15703:                                         my $addr = $domconfig{'contacts'}{$item};
                   15704:                                         if (!grep(/^\Q$addr\E$/,@recipients)) {
                   15705:                                             push(@recipients,$addr);
                   15706:                                         }
                   15707:                                     }
                   15708:                                 }
                   15709:                                 if ($domconfig{'contacts'}{'otherdomsmail'}{'others'}) {
                   15710:                                     $otheremails = $domconfig{'contacts'}{'otherdomsmail'}{'others'};
                   15711:                                 }
                   15712:                                 if ($domconfig{'contacts'}{'otherdomsmail'}{'bcc'}) {
                   15713:                                     my @bccs = split(/,/,$domconfig{'contacts'}{'otherdomsmail'}{'bcc'});
                   15714:                                     my @ok_bccs;
                   15715:                                     foreach my $bcc (@bccs) {
                   15716:                                         $bcc =~ s/^\s+//g;
                   15717:                                         $bcc =~ s/\s+$//g;
                   15718:                                         if ($bcc =~ m/^[^\@]+\@[^\@]+$/) {
                   15719:                                             if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) {
                   15720:                                                 push(@ok_bccs,$bcc);
                   15721:                                             }
                   15722:                                         }
                   15723:                                     }
                   15724:                                     if (@ok_bccs > 0) {
                   15725:                                         $allbcc = join(', ',@ok_bccs);
                   15726:                                     }
                   15727:                                 }
                   15728:                                 $addtext = $domconfig{'contacts'}{'otherdomsmail'}{'include'};
                   15729:                             }
                   15730:                         }
                   15731:                     }
                   15732:                 }
                   15733:             }
                   15734:         }
1.618     raeburn  15735:     }
1.688     raeburn  15736:     if (defined($defmail)) {
                   15737:         if ($defmail ne '') {
                   15738:             push(@recipients,$defmail);
                   15739:         }
1.618     raeburn  15740:     }
                   15741:     if ($otheremails) {
1.619     raeburn  15742:         my @others;
                   15743:         if ($otheremails =~ /,/) {
                   15744:             @others = split(/,/,$otheremails);
1.618     raeburn  15745:         } else {
1.619     raeburn  15746:             push(@others,$otheremails);
                   15747:         }
                   15748:         foreach my $addr (@others) {
                   15749:             if (!grep(/^\Q$addr\E$/,@recipients)) {
                   15750:                 push(@recipients,$addr);
                   15751:             }
1.618     raeburn  15752:         }
                   15753:     }
1.1298    raeburn  15754:     if ($mailing eq 'helpdeskmail') {
1.1270    raeburn  15755:         if ((!@recipients) && ($lastresort ne '')) {
                   15756:             push(@recipients,$lastresort);
                   15757:         }
                   15758:     } elsif ($lastresort ne '') {
                   15759:         if (!grep(/^\Q$lastresort\E$/,@recipients)) {
                   15760:             push(@recipients,$lastresort);
                   15761:         }
                   15762:     }
1.1271    raeburn  15763:     my $recipientlist = join(',',@recipients);
1.1270    raeburn  15764:     if (wantarray) {
                   15765:         return ($recipientlist,$allbcc,$addtext);
                   15766:     } else {
                   15767:         return $recipientlist;
                   15768:     }
1.618     raeburn  15769: }
                   15770: 
1.127     matthew  15771: ############################################################
                   15772: ############################################################
1.154     albertel 15773: 
1.655     raeburn  15774: =pod
                   15775: 
1.1224    musolffc 15776: =over 4
                   15777: 
1.1223    musolffc 15778: =item * &mime_email()
                   15779: 
                   15780: Sends an email with a possible attachment
                   15781: 
                   15782: Inputs:
                   15783: 
                   15784: =over 4
                   15785: 
                   15786: from -              Sender's email address
                   15787: 
1.1343    raeburn  15788: replyto -           Reply-To email address
                   15789: 
1.1223    musolffc 15790: to -                Email address of recipient
                   15791: 
                   15792: subject -           Subject of email
                   15793: 
                   15794: body -              Body of email
                   15795: 
                   15796: cc_string -         Carbon copy email address
                   15797: 
                   15798: bcc -               Blind carbon copy email address
                   15799: 
                   15800: attachment_path -   Path of file to be attached
                   15801: 
                   15802: file_name -         Name of file to be attached
                   15803: 
                   15804: attachment_text -   The body of an attachment of type "TEXT"
                   15805: 
                   15806: =back
                   15807: 
                   15808: =back
                   15809: 
                   15810: =cut
                   15811: 
                   15812: ############################################################
                   15813: ############################################################
                   15814: 
                   15815: sub mime_email {
1.1343    raeburn  15816:     my ($from,$replyto,$to,$subject,$body,$cc_string,$bcc,$attachment_path, 
                   15817:         $file_name,$attachment_text) = @_;
                   15818:  
1.1223    musolffc 15819:     my $msg = MIME::Lite->new(
                   15820:              From    => $from,
                   15821:              To      => $to,
                   15822:              Subject => $subject,
                   15823:              Type    =>'TEXT',
                   15824:              Data    => $body,
                   15825:              );
1.1343    raeburn  15826:     if ($replyto ne '') {
                   15827:         $msg->add("Reply-To" => $replyto);
                   15828:     }
1.1223    musolffc 15829:     if ($cc_string ne '') {
                   15830:         $msg->add("Cc" => $cc_string);
                   15831:     }
                   15832:     if ($bcc ne '') {
                   15833:         $msg->add("Bcc" => $bcc);
                   15834:     }
                   15835:     $msg->attr("content-type"         => "text/plain");
                   15836:     $msg->attr("content-type.charset" => "UTF-8");
                   15837:     # Attach file if given
                   15838:     if ($attachment_path) {
                   15839:         unless ($file_name) {
                   15840:             if ($attachment_path =~ m-/([^/]+)$-) { $file_name = $1; }
                   15841:         }
                   15842:         my ($type, $encoding) = MIME::Types::by_suffix($attachment_path);
                   15843:         $msg->attach(Type     => $type,
                   15844:                      Path     => $attachment_path,
                   15845:                      Filename => $file_name
                   15846:                      );
                   15847:     # Otherwise attach text if given
                   15848:     } elsif ($attachment_text) {
                   15849:         $msg->attach(Type => 'TEXT',
                   15850:                      Data => $attachment_text);
                   15851:     }
                   15852:     # Send it
                   15853:     $msg->send('sendmail');
                   15854: }
                   15855: 
                   15856: ############################################################
                   15857: ############################################################
                   15858: 
                   15859: =pod
                   15860: 
1.655     raeburn  15861: =head1 Course Catalog Routines
                   15862: 
                   15863: =over 4
                   15864: 
                   15865: =item * &gather_categories()
                   15866: 
                   15867: Converts category definitions - keys of categories hash stored in  
                   15868: coursecategories in configuration.db on the primary library server in a 
                   15869: domain - to an array.  Also generates javascript and idx hash used to 
                   15870: generate Domain Coordinator interface for editing Course Categories.
                   15871: 
                   15872: Inputs:
1.663     raeburn  15873: 
1.655     raeburn  15874: categories (reference to hash of category definitions).
1.663     raeburn  15875: 
1.655     raeburn  15876: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   15877:       categories and subcategories).
1.663     raeburn  15878: 
1.655     raeburn  15879: idx (reference to hash of counters used in Domain Coordinator interface for 
                   15880:       editing Course Categories).
1.663     raeburn  15881: 
1.655     raeburn  15882: jsarray (reference to array of categories used to create Javascript arrays for
                   15883:          Domain Coordinator interface for editing Course Categories).
                   15884: 
                   15885: Returns: nothing
                   15886: 
                   15887: Side effects: populates cats, idx and jsarray. 
                   15888: 
                   15889: =cut
                   15890: 
                   15891: sub gather_categories {
                   15892:     my ($categories,$cats,$idx,$jsarray) = @_;
                   15893:     my %counters;
                   15894:     my $num = 0;
                   15895:     foreach my $item (keys(%{$categories})) {
                   15896:         my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
                   15897:         if ($container eq '' && $depth == 0) {
                   15898:             $cats->[$depth][$categories->{$item}] = $cat;
                   15899:         } else {
                   15900:             $cats->[$depth]{$container}[$categories->{$item}] = $cat;
                   15901:         }
                   15902:         my ($escitem,$tail) = split(/:/,$item,2);
                   15903:         if ($counters{$tail} eq '') {
                   15904:             $counters{$tail} = $num;
                   15905:             $num ++;
                   15906:         }
                   15907:         if (ref($idx) eq 'HASH') {
                   15908:             $idx->{$item} = $counters{$tail};
                   15909:         }
                   15910:         if (ref($jsarray) eq 'ARRAY') {
                   15911:             push(@{$jsarray->[$counters{$tail}]},$item);
                   15912:         }
                   15913:     }
                   15914:     return;
                   15915: }
                   15916: 
                   15917: =pod
                   15918: 
                   15919: =item * &extract_categories()
                   15920: 
                   15921: Used to generate breadcrumb trails for course categories.
                   15922: 
                   15923: Inputs:
1.663     raeburn  15924: 
1.655     raeburn  15925: categories (reference to hash of category definitions).
1.663     raeburn  15926: 
1.655     raeburn  15927: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   15928:       categories and subcategories).
1.663     raeburn  15929: 
1.655     raeburn  15930: trails (reference to array of breacrumb trails for each category).
1.663     raeburn  15931: 
1.655     raeburn  15932: allitems (reference to hash - key is category key 
                   15933:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  15934: 
1.655     raeburn  15935: idx (reference to hash of counters used in Domain Coordinator interface for
                   15936:       editing Course Categories).
1.663     raeburn  15937: 
1.655     raeburn  15938: jsarray (reference to array of categories used to create Javascript arrays for
                   15939:          Domain Coordinator interface for editing Course Categories).
                   15940: 
1.665     raeburn  15941: subcats (reference to hash of arrays containing all subcategories within each 
                   15942:          category, -recursive)
                   15943: 
1.1321    raeburn  15944: maxd (reference to hash used to hold max depth for all top-level categories).
                   15945: 
1.655     raeburn  15946: Returns: nothing
                   15947: 
                   15948: Side effects: populates trails and allitems hash references.
                   15949: 
                   15950: =cut
                   15951: 
                   15952: sub extract_categories {
1.1321    raeburn  15953:     my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats,$maxd) = @_;
1.655     raeburn  15954:     if (ref($categories) eq 'HASH') {
                   15955:         &gather_categories($categories,$cats,$idx,$jsarray);
                   15956:         if (ref($cats->[0]) eq 'ARRAY') {
                   15957:             for (my $i=0; $i<@{$cats->[0]}; $i++) {
                   15958:                 my $name = $cats->[0][$i];
                   15959:                 my $item = &escape($name).'::0';
                   15960:                 my $trailstr;
                   15961:                 if ($name eq 'instcode') {
                   15962:                     $trailstr = &mt('Official courses (with institutional codes)');
1.919     raeburn  15963:                 } elsif ($name eq 'communities') {
                   15964:                     $trailstr = &mt('Communities');
1.1239    raeburn  15965:                 } elsif ($name eq 'placement') {
                   15966:                     $trailstr = &mt('Placement Tests');
1.655     raeburn  15967:                 } else {
                   15968:                     $trailstr = $name;
                   15969:                 }
                   15970:                 if ($allitems->{$item} eq '') {
                   15971:                     push(@{$trails},$trailstr);
                   15972:                     $allitems->{$item} = scalar(@{$trails})-1;
                   15973:                 }
                   15974:                 my @parents = ($name);
                   15975:                 if (ref($cats->[1]{$name}) eq 'ARRAY') {
                   15976:                     for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
                   15977:                         my $category = $cats->[1]{$name}[$j];
1.665     raeburn  15978:                         if (ref($subcats) eq 'HASH') {
                   15979:                             push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
                   15980:                         }
1.1321    raeburn  15981:                         &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats,$maxd);
1.665     raeburn  15982:                     }
                   15983:                 } else {
                   15984:                     if (ref($subcats) eq 'HASH') {
                   15985:                         $subcats->{$item} = [];
1.655     raeburn  15986:                     }
1.1321    raeburn  15987:                     if (ref($maxd) eq 'HASH') {
                   15988:                         $maxd->{$name} = 1;
                   15989:                     }
1.655     raeburn  15990:                 }
                   15991:             }
                   15992:         }
                   15993:     }
                   15994:     return;
                   15995: }
                   15996: 
                   15997: =pod
                   15998: 
1.1162    raeburn  15999: =item * &recurse_categories()
1.655     raeburn  16000: 
                   16001: Recursively used to generate breadcrumb trails for course categories.
                   16002: 
                   16003: Inputs:
1.663     raeburn  16004: 
1.655     raeburn  16005: cats (reference to array of arrays/hashes which encapsulates hierarchy of
                   16006:       categories and subcategories).
1.663     raeburn  16007: 
1.655     raeburn  16008: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663     raeburn  16009: 
                   16010: category (current course category, for which breadcrumb trail is being generated).
                   16011: 
                   16012: trails (reference to array of breadcrumb trails for each category).
                   16013: 
1.655     raeburn  16014: allitems (reference to hash - key is category key
                   16015:          (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663     raeburn  16016: 
1.655     raeburn  16017: parents (array containing containers directories for current category, 
                   16018:          back to top level). 
                   16019: 
                   16020: Returns: nothing
                   16021: 
                   16022: Side effects: populates trails and allitems hash references
                   16023: 
                   16024: =cut
                   16025: 
                   16026: sub recurse_categories {
1.1321    raeburn  16027:     my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats,$maxd) = @_;
1.655     raeburn  16028:     my $shallower = $depth - 1;
                   16029:     if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
                   16030:         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
                   16031:             my $name = $cats->[$depth]{$category}[$k];
                   16032:             my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
1.1321    raeburn  16033:             my $trailstr = join(' &raquo; ',(@{$parents},$category));
1.655     raeburn  16034:             if ($allitems->{$item} eq '') {
                   16035:                 push(@{$trails},$trailstr);
                   16036:                 $allitems->{$item} = scalar(@{$trails})-1;
                   16037:             }
                   16038:             my $deeper = $depth+1;
                   16039:             push(@{$parents},$category);
1.665     raeburn  16040:             if (ref($subcats) eq 'HASH') {
                   16041:                 my $subcat = &escape($name).':'.$category.':'.$depth;
                   16042:                 for (my $j=@{$parents}; $j>=0; $j--) {
                   16043:                     my $higher;
                   16044:                     if ($j > 0) {
                   16045:                         $higher = &escape($parents->[$j]).':'.
                   16046:                                   &escape($parents->[$j-1]).':'.$j;
                   16047:                     } else {
                   16048:                         $higher = &escape($parents->[$j]).'::'.$j;
                   16049:                     }
                   16050:                     push(@{$subcats->{$higher}},$subcat);
                   16051:                 }
                   16052:             }
                   16053:             &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
1.1321    raeburn  16054:                                 $subcats,$maxd);
1.655     raeburn  16055:             pop(@{$parents});
                   16056:         }
                   16057:     } else {
                   16058:         my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
1.1321    raeburn  16059:         my $trailstr = join(' &raquo; ',(@{$parents},$category));
1.655     raeburn  16060:         if ($allitems->{$item} eq '') {
                   16061:             push(@{$trails},$trailstr);
                   16062:             $allitems->{$item} = scalar(@{$trails})-1;
                   16063:         }
1.1321    raeburn  16064:         if (ref($maxd) eq 'HASH') {
                   16065:             if ($depth > $maxd->{$parents->[0]}) {
                   16066:                 $maxd->{$parents->[0]} = $depth;
                   16067:             }
                   16068:         }
1.655     raeburn  16069:     }
                   16070:     return;
                   16071: }
                   16072: 
1.663     raeburn  16073: =pod
                   16074: 
1.1162    raeburn  16075: =item * &assign_categories_table()
1.663     raeburn  16076: 
                   16077: Create a datatable for display of hierarchical categories in a domain,
                   16078: with checkboxes to allow a course to be categorized. 
                   16079: 
                   16080: Inputs:
                   16081: 
                   16082: cathash - reference to hash of categories defined for the domain (from
                   16083:           configuration.db)
                   16084: 
                   16085: currcat - scalar with an & separated list of categories assigned to a course. 
                   16086: 
1.919     raeburn  16087: type    - scalar contains course type (Course or Community).
                   16088: 
1.1260    raeburn  16089: disabled - scalar (optional) contains disabled="disabled" if input elements are
                   16090:            to be readonly (e.g., Domain Helpdesk role viewing course settings).
                   16091: 
1.663     raeburn  16092: Returns: $output (markup to be displayed) 
                   16093: 
                   16094: =cut
                   16095: 
                   16096: sub assign_categories_table {
1.1259    raeburn  16097:     my ($cathash,$currcat,$type,$disabled) = @_;
1.663     raeburn  16098:     my $output;
                   16099:     if (ref($cathash) eq 'HASH') {
1.1321    raeburn  16100:         my (@cats,@trails,%allitems,%idx,@jsarray,%maxd,@path,$maxdepth);
                   16101:         &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray,\%maxd);
1.663     raeburn  16102:         $maxdepth = scalar(@cats);
                   16103:         if (@cats > 0) {
                   16104:             my $itemcount = 0;
                   16105:             if (ref($cats[0]) eq 'ARRAY') {
                   16106:                 my @currcategories;
                   16107:                 if ($currcat ne '') {
                   16108:                     @currcategories = split('&',$currcat);
                   16109:                 }
1.919     raeburn  16110:                 my $table;
1.663     raeburn  16111:                 for (my $i=0; $i<@{$cats[0]}; $i++) {
                   16112:                     my $parent = $cats[0][$i];
1.919     raeburn  16113:                     next if ($parent eq 'instcode');
                   16114:                     if ($type eq 'Community') {
                   16115:                         next unless ($parent eq 'communities');
1.1239    raeburn  16116:                     } elsif ($type eq 'Placement') {
                   16117:                         next unless ($parent eq 'placement');
1.919     raeburn  16118:                     } else {
1.1239    raeburn  16119:                         next if (($parent eq 'communities') || ($parent eq 'placement'));
1.919     raeburn  16120:                     }
1.663     raeburn  16121:                     my $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   16122:                     my $item = &escape($parent).'::0';
                   16123:                     my $checked = '';
                   16124:                     if (@currcategories > 0) {
                   16125:                         if (grep(/^\Q$item\E$/,@currcategories)) {
1.772     bisitz   16126:                             $checked = ' checked="checked"';
1.663     raeburn  16127:                         }
                   16128:                     }
1.919     raeburn  16129:                     my $parent_title = $parent;
                   16130:                     if ($parent eq 'communities') {
                   16131:                         $parent_title = &mt('Communities');
1.1239    raeburn  16132:                     } elsif ($parent eq 'placement') {
                   16133:                         $parent_title = &mt('Placement Tests');
1.919     raeburn  16134:                     }
                   16135:                     $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
                   16136:                               '<input type="checkbox" name="usecategory" value="'.
1.1259    raeburn  16137:                               $item.'"'.$checked.$disabled.' />'.$parent_title.'</span>'.
1.919     raeburn  16138:                               '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663     raeburn  16139:                     my $depth = 1;
                   16140:                     push(@path,$parent);
1.1259    raeburn  16141:                     $table .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories,$disabled);
1.663     raeburn  16142:                     pop(@path);
1.919     raeburn  16143:                     $table .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
1.663     raeburn  16144:                     $itemcount ++;
                   16145:                 }
1.919     raeburn  16146:                 if ($itemcount) {
                   16147:                     $output = &Apache::loncommon::start_data_table().
                   16148:                               $table.
                   16149:                               &Apache::loncommon::end_data_table();
                   16150:                 }
1.663     raeburn  16151:             }
                   16152:         }
                   16153:     }
                   16154:     return $output;
                   16155: }
                   16156: 
                   16157: =pod
                   16158: 
1.1162    raeburn  16159: =item * &assign_category_rows()
1.663     raeburn  16160: 
                   16161: Create a datatable row for display of nested categories in a domain,
                   16162: with checkboxes to allow a course to be categorized,called recursively.
                   16163: 
                   16164: Inputs:
                   16165: 
                   16166: itemcount - track row number for alternating colors
                   16167: 
                   16168: cats - reference to array of arrays/hashes which encapsulates hierarchy of
                   16169:       categories and subcategories.
                   16170: 
                   16171: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
                   16172: 
                   16173: parent - parent of current category item
                   16174: 
                   16175: path - Array containing all categories back up through the hierarchy from the
                   16176:        current category to the top level.
                   16177: 
                   16178: currcategories - reference to array of current categories assigned to the course
                   16179: 
1.1260    raeburn  16180: disabled - scalar (optional) contains disabled="disabled" if input elements are
                   16181:            to be readonly (e.g., Domain Helpdesk role viewing course settings).
                   16182: 
1.663     raeburn  16183: Returns: $output (markup to be displayed).
                   16184: 
                   16185: =cut
                   16186: 
                   16187: sub assign_category_rows {
1.1259    raeburn  16188:     my ($itemcount,$cats,$depth,$parent,$path,$currcategories,$disabled) = @_;
1.663     raeburn  16189:     my ($text,$name,$item,$chgstr);
                   16190:     if (ref($cats) eq 'ARRAY') {
                   16191:         my $maxdepth = scalar(@{$cats});
                   16192:         if (ref($cats->[$depth]) eq 'HASH') {
                   16193:             if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
                   16194:                 my $numchildren = @{$cats->[$depth]{$parent}};
                   16195:                 my $css_class = $itemcount%2?' class="LC_odd_row"':'';
1.1145    raeburn  16196:                 $text .= '<td><table class="LC_data_table">';
1.663     raeburn  16197:                 for (my $j=0; $j<$numchildren; $j++) {
                   16198:                     $name = $cats->[$depth]{$parent}[$j];
                   16199:                     $item = &escape($name).':'.&escape($parent).':'.$depth;
                   16200:                     my $deeper = $depth+1;
                   16201:                     my $checked = '';
                   16202:                     if (ref($currcategories) eq 'ARRAY') {
                   16203:                         if (@{$currcategories} > 0) {
                   16204:                             if (grep(/^\Q$item\E$/,@{$currcategories})) {
1.772     bisitz   16205:                                 $checked = ' checked="checked"';
1.663     raeburn  16206:                             }
                   16207:                         }
                   16208:                     }
1.664     raeburn  16209:                     $text .= '<tr><td><span class="LC_nobreak"><label>'.
                   16210:                              '<input type="checkbox" name="usecategory" value="'.
1.1259    raeburn  16211:                              $item.'"'.$checked.$disabled.' />'.$name.'</label></span>'.
1.675     raeburn  16212:                              '<input type="hidden" name="catname" value="'.$name.'" />'.
                   16213:                              '</td><td>';
1.663     raeburn  16214:                     if (ref($path) eq 'ARRAY') {
                   16215:                         push(@{$path},$name);
1.1259    raeburn  16216:                         $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories,$disabled);
1.663     raeburn  16217:                         pop(@{$path});
                   16218:                     }
                   16219:                     $text .= '</td></tr>';
                   16220:                 }
                   16221:                 $text .= '</table></td>';
                   16222:             }
                   16223:         }
                   16224:     }
                   16225:     return $text;
                   16226: }
                   16227: 
1.1181    raeburn  16228: =pod
                   16229: 
                   16230: =back
                   16231: 
                   16232: =cut
                   16233: 
1.655     raeburn  16234: ############################################################
                   16235: ############################################################
                   16236: 
                   16237: 
1.443     albertel 16238: sub commit_customrole {
1.664     raeburn  16239:     my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
1.630     raeburn  16240:     my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443     albertel 16241:                          ($start?', '.&mt('starting').' '.localtime($start):'').
                   16242:                          ($end?', ending '.localtime($end):'').': <b>'.
                   16243:               &Apache::lonnet::assigncustomrole(
1.664     raeburn  16244:                  $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
1.443     albertel 16245:                  '</b><br />';
                   16246:     return $output;
                   16247: }
                   16248: 
                   16249: sub commit_standardrole {
1.1116    raeburn  16250:     my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;
1.541     raeburn  16251:     my ($output,$logmsg,$linefeed);
                   16252:     if ($context eq 'auto') {
                   16253:         $linefeed = "\n";
                   16254:     } else {
                   16255:         $linefeed = "<br />\n";
                   16256:     }  
1.443     albertel 16257:     if ($three eq 'st') {
1.541     raeburn  16258:         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
1.1116    raeburn  16259:                                          $one,$two,$sec,$context,$credits);
1.541     raeburn  16260:         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
1.626     raeburn  16261:             ($result eq 'unknown_course') || ($result eq 'refused')) {
                   16262:             $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
1.443     albertel 16263:         } else {
1.541     raeburn  16264:             $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443     albertel 16265:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  16266:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
                   16267:             if ($context eq 'auto') {
                   16268:                 $output .= $result.$linefeed.&mt('Add to classlist').': ok';
                   16269:             } else {
                   16270:                $output .= '<b>'.$result.'</b>'.$linefeed.
                   16271:                &mt('Add to classlist').': <b>ok</b>';
                   16272:             }
                   16273:             $output .= $linefeed;
1.443     albertel 16274:         }
                   16275:     } else {
                   16276:         $output = &mt('Assigning').' '.$three.' in '.$url.
                   16277:                ($start?', '.&mt('starting').' '.localtime($start):'').
1.541     raeburn  16278:                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652     raeburn  16279:         my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541     raeburn  16280:         if ($context eq 'auto') {
                   16281:             $output .= $result.$linefeed;
                   16282:         } else {
                   16283:             $output .= '<b>'.$result.'</b>'.$linefeed;
                   16284:         }
1.443     albertel 16285:     }
                   16286:     return $output;
                   16287: }
                   16288: 
                   16289: sub commit_studentrole {
1.1116    raeburn  16290:     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
                   16291:         $credits) = @_;
1.626     raeburn  16292:     my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541     raeburn  16293:     if ($context eq 'auto') {
                   16294:         $linefeed = "\n";
                   16295:     } else {
                   16296:         $linefeed = '<br />'."\n";
                   16297:     }
1.443     albertel 16298:     if (defined($one) && defined($two)) {
                   16299:         my $cid=$one.'_'.$two;
                   16300:         my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
                   16301:         my $secchange = 0;
                   16302:         my $expire_role_result;
                   16303:         my $modify_section_result;
1.628     raeburn  16304:         if ($oldsec ne '-1') { 
                   16305:             if ($oldsec ne $sec) {
1.443     albertel 16306:                 $secchange = 1;
1.628     raeburn  16307:                 my $now = time;
1.443     albertel 16308:                 my $uurl='/'.$cid;
                   16309:                 $uurl=~s/\_/\//g;
                   16310:                 if ($oldsec) {
                   16311:                     $uurl.='/'.$oldsec;
                   16312:                 }
1.626     raeburn  16313:                 $oldsecurl = $uurl;
1.628     raeburn  16314:                 $expire_role_result = 
1.652     raeburn  16315:                     &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628     raeburn  16316:                 if ($env{'request.course.sec'} ne '') { 
                   16317:                     if ($expire_role_result eq 'refused') {
                   16318:                         my @roles = ('st');
                   16319:                         my @statuses = ('previous');
                   16320:                         my @roledoms = ($one);
                   16321:                         my $withsec = 1;
                   16322:                         my %roleshash = 
                   16323:                             &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
                   16324:                                               \@statuses,\@roles,\@roledoms,$withsec);
                   16325:                         if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
                   16326:                             my ($oldstart,$oldend) = 
                   16327:                                 split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
                   16328:                             if ($oldend > 0 && $oldend <= $now) {
                   16329:                                 $expire_role_result = 'ok';
                   16330:                             }
                   16331:                         }
                   16332:                     }
                   16333:                 }
1.443     albertel 16334:                 $result = $expire_role_result;
                   16335:             }
                   16336:         }
                   16337:         if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.1116    raeburn  16338:             $modify_section_result = 
                   16339:                 &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
                   16340:                                                            undef,undef,undef,$sec,
                   16341:                                                            $end,$start,'','',$cid,
                   16342:                                                            '',$context,$credits);
1.443     albertel 16343:             if ($modify_section_result =~ /^ok/) {
                   16344:                 if ($secchange == 1) {
1.628     raeburn  16345:                     if ($sec eq '') {
                   16346:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
                   16347:                     } else {
                   16348:                         $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
                   16349:                     }
1.443     albertel 16350:                 } elsif ($oldsec eq '-1') {
1.628     raeburn  16351:                     if ($sec eq '') {
                   16352:                         $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
                   16353:                     } else {
                   16354:                         $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   16355:                     }
1.443     albertel 16356:                 } else {
1.628     raeburn  16357:                     if ($sec eq '') {
                   16358:                         $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
                   16359:                     } else {
                   16360:                         $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
                   16361:                     }
1.443     albertel 16362:                 }
                   16363:             } else {
1.1115    raeburn  16364:                 if ($secchange) { 
1.628     raeburn  16365:                     $$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;
                   16366:                 } else {
                   16367:                     $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
                   16368:                 }
1.443     albertel 16369:             }
                   16370:             $result = $modify_section_result;
                   16371:         } elsif ($secchange == 1) {
1.628     raeburn  16372:             if ($oldsec eq '') {
1.1103    raeburn  16373:                 $$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  16374:             } else {
                   16375:                 $$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;
                   16376:             }
1.626     raeburn  16377:             if ($expire_role_result eq 'refused') {
                   16378:                 my $newsecurl = '/'.$cid;
                   16379:                 $newsecurl =~ s/\_/\//g;
                   16380:                 if ($sec ne '') {
                   16381:                     $newsecurl.='/'.$sec;
                   16382:                 }
                   16383:                 if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
                   16384:                     if ($sec eq '') {
                   16385:                         $$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;
                   16386:                     } else {
                   16387:                         $$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;
                   16388:                     }
                   16389:                 }
                   16390:             }
1.443     albertel 16391:         }
                   16392:     } else {
1.626     raeburn  16393:         $$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 16394:         $result = "error: incomplete course id\n";
                   16395:     }
                   16396:     return $result;
                   16397: }
                   16398: 
1.1108    raeburn  16399: sub show_role_extent {
                   16400:     my ($scope,$context,$role) = @_;
                   16401:     $scope =~ s{^/}{};
                   16402:     my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
                   16403:     push(@courseroles,'co');
                   16404:     my @authorroles = &Apache::lonuserutils::roles_by_context('author');
                   16405:     if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
                   16406:         $scope =~ s{/}{_};
                   16407:         return '<span class="LC_cusr_emph">'.$env{'course.'.$scope.'.description'}.'</span>';
                   16408:     } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
                   16409:         my ($audom,$auname) = split(/\//,$scope);
                   16410:         return &mt('[_1] Author Space','<span class="LC_cusr_emph">'.
                   16411:                    &Apache::loncommon::plainname($auname,$audom).'</span>');
                   16412:     } else {
                   16413:         $scope =~ s{/$}{};
                   16414:         return &mt('Domain: [_1]','<span class="LC_cusr_emph">'.
                   16415:                    &Apache::lonnet::domain($scope,'description').'</span>');
                   16416:     }
                   16417: }
                   16418: 
1.443     albertel 16419: ############################################################
                   16420: ############################################################
                   16421: 
1.566     albertel 16422: sub check_clone {
1.578     raeburn  16423:     my ($args,$linefeed) = @_;
1.566     albertel 16424:     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
                   16425:     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
                   16426:     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
1.1344    raeburn  16427:     my $clonetitle;
                   16428:     my @clonemsg;
1.566     albertel 16429:     my $can_clone = 0;
1.944     raeburn  16430:     my $lctype = lc($args->{'crstype'});
1.908     raeburn  16431:     if ($lctype ne 'community') {
                   16432:         $lctype = 'course';
                   16433:     }
1.566     albertel 16434:     if ($clonehome eq 'no_host') {
1.944     raeburn  16435:         if ($args->{'crstype'} eq 'Community') {
1.1344    raeburn  16436:             push(@clonemsg,({
                   16437:                               mt => 'No new community created.',
                   16438:                               args => [],
                   16439:                             },
                   16440:                             {
                   16441:                               mt => 'A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',
                   16442:                               args => [$args->{'clonedomain'}.':'.$args->{'clonedomain'}],
                   16443:                             }));
1.908     raeburn  16444:         } else {
1.1344    raeburn  16445:             push(@clonemsg,({
                   16446:                               mt => 'No new course created.',
                   16447:                               args => [],
                   16448:                             },
                   16449:                             {
                   16450:                               mt => 'A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',
                   16451:                               args => [$args->{'clonecourse'}.':'.$args->{'clonedomain'}],
                   16452:                             }));
                   16453:         }
1.566     albertel 16454:     } else {
                   16455: 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.1344    raeburn  16456:         $clonetitle = $clonedesc{'description'};
1.944     raeburn  16457:         if ($args->{'crstype'} eq 'Community') {
1.908     raeburn  16458:             if ($clonedesc{'type'} ne 'Community') {
1.1344    raeburn  16459:                 push(@clonemsg,({
                   16460:                                   mt => 'No new community created.',
                   16461:                                   args => [],
                   16462:                                 },
                   16463:                                 {
                   16464:                                   mt => 'A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',
                   16465:                                   args => [$args->{'clonecourse'}.':'.$args->{'clonedomain'}],
                   16466:                                 }));
                   16467:                 return ($can_clone,\@clonemsg,$cloneid,$clonehome);
1.908     raeburn  16468:             }
                   16469:         }
1.1262    raeburn  16470: 	if (($env{'request.role.domain'} eq $args->{'clonedomain'}) &&
1.882     raeburn  16471:             (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.566     albertel 16472: 	    $can_clone = 1;
                   16473: 	} else {
1.1221    raeburn  16474: 	    my %clonehash = &Apache::lonnet::get('environment',['cloners','internal.coursecode'],
1.566     albertel 16475: 						 $args->{'clonedomain'},$args->{'clonecourse'});
1.1221    raeburn  16476:             if ($clonehash{'cloners'} eq '') {
                   16477:                 my %domdefs = &Apache::lonnet::get_domain_defaults($args->{'course_domain'});
                   16478:                 if ($domdefs{'canclone'}) {
                   16479:                     unless ($domdefs{'canclone'} eq 'none') {
                   16480:                         if ($domdefs{'canclone'} eq 'domain') {
                   16481:                             if ($args->{'ccdomain'} eq $args->{'clonedomain'}) {
                   16482:                                 $can_clone = 1;
                   16483:                             }
                   16484:                         } elsif (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && 
                   16485:                                  ($args->{'clonedomain'} eq  $args->{'course_domain'})) {
                   16486:                             if (&Apache::lonnet::default_instcode_cloning($args->{'clonedomain'},$domdefs{'canclone'},
                   16487:                                                                           $clonehash{'internal.coursecode'},$args->{'crscode'})) {
                   16488:                                 $can_clone = 1;
                   16489:                             }
                   16490:                         }
                   16491:                     }
                   16492:                 }
1.578     raeburn  16493:             } else {
1.1221    raeburn  16494: 	        my @cloners = split(/,/,$clonehash{'cloners'});
                   16495:                 if (grep(/^\*$/,@cloners)) {
1.942     raeburn  16496:                     $can_clone = 1;
1.1221    raeburn  16497:                 } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
1.942     raeburn  16498:                     $can_clone = 1;
1.1225    raeburn  16499:                 } elsif (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners)) {
                   16500:                     $can_clone = 1;
1.1221    raeburn  16501:                 }
                   16502:                 unless ($can_clone) {
1.1225    raeburn  16503:                     if (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && 
                   16504:                         ($args->{'clonedomain'} eq  $args->{'course_domain'})) {
1.1221    raeburn  16505:                         my (%gotdomdefaults,%gotcodedefaults);
                   16506:                         foreach my $cloner (@cloners) {
                   16507:                             if (($cloner ne '*') && ($cloner !~ /^\*\:$match_domain$/) &&
                   16508:                                 ($cloner !~ /^$match_username\:$match_domain$/) && ($cloner ne '')) {
                   16509:                                 my (%codedefaults,@code_order);
                   16510:                                 if (ref($gotcodedefaults{$args->{'clonedomain'}}) eq 'HASH') {
                   16511:                                     if (ref($gotcodedefaults{$args->{'clonedomain'}}{'defaults'}) eq 'HASH') {
                   16512:                                         %codedefaults = %{$gotcodedefaults{$args->{'clonedomain'}}{'defaults'}};
                   16513:                                     }
                   16514:                                     if (ref($gotcodedefaults{$args->{'clonedomain'}}{'order'}) eq 'ARRAY') {
                   16515:                                         @code_order = @{$gotcodedefaults{$args->{'clonedomain'}}{'order'}};
                   16516:                                     }
                   16517:                                 } else {
                   16518:                                     &Apache::lonnet::auto_instcode_defaults($args->{'clonedomain'},
                   16519:                                                                             \%codedefaults,
                   16520:                                                                             \@code_order);
                   16521:                                     $gotcodedefaults{$args->{'clonedomain'}}{'defaults'} = \%codedefaults;
                   16522:                                     $gotcodedefaults{$args->{'clonedomain'}}{'order'} = \@code_order;
                   16523:                                 }
                   16524:                                 if (@code_order > 0) {
                   16525:                                     if (&Apache::lonnet::check_instcode_cloning(\%codedefaults,\@code_order,
                   16526:                                                                                 $cloner,$clonehash{'internal.coursecode'},
                   16527:                                                                                 $args->{'crscode'})) {
                   16528:                                         $can_clone = 1;
                   16529:                                         last;
                   16530:                                     }
                   16531:                                 }
                   16532:                             }
                   16533:                         }
                   16534:                     }
1.1225    raeburn  16535:                 }
                   16536:             }
                   16537:             unless ($can_clone) {
                   16538:                 my $ccrole = 'cc';
                   16539:                 if ($args->{'crstype'} eq 'Community') {
                   16540:                     $ccrole = 'co';
                   16541:                 }
                   16542: 	        my %roleshash =
                   16543: 		    &Apache::lonnet::get_my_roles($args->{'ccuname'},
                   16544: 					          $args->{'ccdomain'},
                   16545:                                                   'userroles',['active'],[$ccrole],
                   16546: 					          [$args->{'clonedomain'}]);
                   16547: 	        if ($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) {
                   16548:                     $can_clone = 1;
                   16549:                 } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},
                   16550:                                                           $args->{'ccuname'},$args->{'ccdomain'})) {
                   16551:                     $can_clone = 1;
1.1221    raeburn  16552:                 }
                   16553:             }
                   16554:             unless ($can_clone) {
                   16555:                 if ($args->{'crstype'} eq 'Community') {
1.1344    raeburn  16556:                     push(@clonemsg,({
                   16557:                                       mt => 'No new community created.',
                   16558:                                       args => [],
                   16559:                                     },
                   16560:                                     {
                   16561:                                       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]).',
                   16562:                                       args => [$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}],
                   16563:                                     }));
1.942     raeburn  16564:                 } else {
1.1344    raeburn  16565:                     push(@clonemsg,({
                   16566:                                       mt => 'No new course created.',
                   16567:                                       args => [],
                   16568:                                     },
                   16569:                                     {
                   16570:                                       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]).',
                   16571:                                       args => [$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}],
                   16572:                                     }));
1.1221    raeburn  16573:                 }
1.566     albertel 16574: 	    }
1.578     raeburn  16575:         }
1.566     albertel 16576:     }
1.1344    raeburn  16577:     return ($can_clone,\@clonemsg,$cloneid,$clonehome,$clonetitle);
1.566     albertel 16578: }
                   16579: 
1.444     albertel 16580: sub construct_course {
1.1262    raeburn  16581:     my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,
1.1344    raeburn  16582:         $cnum,$category,$coderef,$callercontext,$user_lh) = @_;
                   16583:     my ($outcome,$msgref,$clonemsgref);
1.541     raeburn  16584:     my $linefeed =  '<br />'."\n";
                   16585:     if ($context eq 'auto') {
                   16586:         $linefeed = "\n";
                   16587:     }
1.566     albertel 16588: 
                   16589: #
                   16590: # Are we cloning?
                   16591: #
1.1344    raeburn  16592:     my ($can_clone,$cloneid,$clonehome,$clonetitle);
1.566     albertel 16593:     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.1344    raeburn  16594: 	($can_clone,$clonemsgref,$cloneid,$clonehome,$clonetitle) = &check_clone($args,$linefeed);
1.566     albertel 16595:         if (!$can_clone) {
1.1344    raeburn  16596: 	    return (0,$outcome,$clonemsgref);
1.566     albertel 16597: 	}
                   16598:     }
                   16599: 
1.444     albertel 16600: #
                   16601: # Open course
                   16602: #
1.1239    raeburn  16603:     my $showncrstype;
                   16604:     if ($args->{'crstype'} eq 'Placement') {
                   16605:         $showncrstype = 'placement test'; 
                   16606:     } else {  
                   16607:         $showncrstype = lc($args->{'crstype'});
                   16608:     }
1.444     albertel 16609:     my %cenv=();
                   16610:     $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
                   16611:                                              $args->{'cdescr'},
                   16612:                                              $args->{'curl'},
                   16613:                                              $args->{'course_home'},
                   16614:                                              $args->{'nonstandard'},
                   16615:                                              $args->{'crscode'},
                   16616:                                              $args->{'ccuname'}.':'.
                   16617:                                              $args->{'ccdomain'},
1.882     raeburn  16618:                                              $args->{'crstype'},
1.1344    raeburn  16619:                                              $cnum,$context,$category,
                   16620:                                              $callercontext);
1.444     albertel 16621: 
                   16622:     # Note: The testing routines depend on this being output; see 
                   16623:     # Utils::Course. This needs to at least be output as a comment
                   16624:     # if anyone ever decides to not show this, and Utils::Course::new
                   16625:     # will need to be suitably modified.
1.1344    raeburn  16626:     if (($callercontext eq 'auto') && ($user_lh ne '')) {
                   16627:         $outcome .= &mt_user($user_lh,'New LON-CAPA [_1] ID: [_2]',$showncrstype,$$courseid).$linefeed;
                   16628:     } else {
                   16629:         $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$showncrstype,$$courseid).$linefeed;
                   16630:     }
1.943     raeburn  16631:     if ($$courseid =~ /^error:/) {
1.1344    raeburn  16632:         return (0,$outcome,$clonemsgref);
1.943     raeburn  16633:     }
                   16634: 
1.444     albertel 16635: #
                   16636: # Check if created correctly
                   16637: #
1.479     albertel 16638:     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444     albertel 16639:     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.943     raeburn  16640:     if ($crsuhome eq 'no_host') {
1.1344    raeburn  16641:         if (($callercontext eq 'auto') && ($user_lh ne '')) {
                   16642:             $outcome .= &mt_user($user_lh,
                   16643:                             'Course creation failed, unrecognized course home server.');
                   16644:         } else {
                   16645:             $outcome .= &mt('Course creation failed, unrecognized course home server.');
                   16646:         }
                   16647:         $outcome .= $linefeed;
                   16648:         return (0,$outcome,$clonemsgref);
1.943     raeburn  16649:     }
1.541     raeburn  16650:     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566     albertel 16651: 
1.444     albertel 16652: #
1.566     albertel 16653: # Do the cloning
                   16654: #   
1.1344    raeburn  16655:     my @clonemsg;
1.566     albertel 16656:     if ($can_clone && $cloneid) {
1.1344    raeburn  16657:         push(@clonemsg,
                   16658:                       {
                   16659:                           mt => 'Created [_1] by cloning from [_2]',
                   16660:                           args => [$showncrstype,$clonetitle],
                   16661:                       });
1.566     albertel 16662: 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444     albertel 16663: # Copy all files
1.1344    raeburn  16664:         my @info =
                   16665: 	    &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},
                   16666: 	                                             $args->{'dateshift'},$args->{'crscode'},
                   16667:                                                      $args->{'ccuname'}.':'.$args->{'ccdomain'},
                   16668:                                                      $args->{'tinyurls'});
                   16669:         if (@info) {
                   16670:             push(@clonemsg,@info);
                   16671:         }
1.444     albertel 16672: # Restore URL
1.566     albertel 16673: 	$cenv{'url'}=$oldcenv{'url'};
1.444     albertel 16674: # Restore title
1.566     albertel 16675: 	$cenv{'description'}=$oldcenv{'description'};
1.955     raeburn  16676: # Restore creation date, creator and creation context.
                   16677:         $cenv{'internal.created'}=$oldcenv{'internal.created'};
                   16678:         $cenv{'internal.creator'}=$oldcenv{'internal.creator'};
                   16679:         $cenv{'internal.creationcontext'}=$oldcenv{'internal.creationcontext'};
1.444     albertel 16680: # Mark as cloned
1.566     albertel 16681: 	$cenv{'clonedfrom'}=$cloneid;
1.638     www      16682: # Need to clone grading mode
                   16683:         my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
                   16684:         $cenv{'grading'}=$newenv{'grading'};
                   16685: # Do not clone these environment entries
                   16686:         &Apache::lonnet::del('environment',
                   16687:                   ['default_enrollment_start_date',
                   16688:                    'default_enrollment_end_date',
                   16689:                    'question.email',
                   16690:                    'policy.email',
                   16691:                    'comment.email',
                   16692:                    'pch.users.denied',
1.725     raeburn  16693:                    'plc.users.denied',
                   16694:                    'hidefromcat',
1.1121    raeburn  16695:                    'checkforpriv',
1.1355    raeburn  16696:                    'categories'],
1.638     www      16697:                    $$crsudom,$$crsunum);
1.1170    raeburn  16698:         if ($args->{'textbook'}) {
                   16699:             $cenv{'internal.textbook'} = $args->{'textbook'};
                   16700:         }
1.444     albertel 16701:     }
1.566     albertel 16702: 
1.444     albertel 16703: #
                   16704: # Set environment (will override cloned, if existing)
                   16705: #
                   16706:     my @sections = ();
                   16707:     my @xlists = ();
                   16708:     if ($args->{'crstype'}) {
                   16709:         $cenv{'type'}=$args->{'crstype'};
                   16710:     }
1.1371    raeburn  16711:     if ($args->{'lti'}) {
                   16712:         $cenv{'internal.lti'}=$args->{'lti'};
                   16713:     }
1.444     albertel 16714:     if ($args->{'crsid'}) {
                   16715:         $cenv{'courseid'}=$args->{'crsid'};
                   16716:     }
                   16717:     if ($args->{'crscode'}) {
                   16718:         $cenv{'internal.coursecode'}=$args->{'crscode'};
                   16719:     }
                   16720:     if ($args->{'crsquota'} ne '') {
                   16721:         $cenv{'internal.coursequota'}=$args->{'crsquota'};
                   16722:     } else {
                   16723:         $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
                   16724:     }
                   16725:     if ($args->{'ccuname'}) {
                   16726:         $cenv{'internal.courseowner'} = $args->{'ccuname'}.
                   16727:                                         ':'.$args->{'ccdomain'};
                   16728:     } else {
                   16729:         $cenv{'internal.courseowner'} = $args->{'curruser'};
                   16730:     }
1.1116    raeburn  16731:     if ($args->{'defaultcredits'}) {
                   16732:         $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
                   16733:     }
1.444     albertel 16734:     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
                   16735:     if ($args->{'crssections'}) {
                   16736:         $cenv{'internal.sectionnums'} = '';
                   16737:         if ($args->{'crssections'} =~ m/,/) {
                   16738:             @sections = split/,/,$args->{'crssections'};
                   16739:         } else {
                   16740:             $sections[0] = $args->{'crssections'};
                   16741:         }
                   16742:         if (@sections > 0) {
                   16743:             foreach my $item (@sections) {
                   16744:                 my ($sec,$gp) = split/:/,$item;
                   16745:                 my $class = $args->{'crscode'}.$sec;
                   16746:                 my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
                   16747:                 $cenv{'internal.sectionnums'} .= $item.',';
                   16748:                 unless ($addcheck eq 'ok') {
1.1263    raeburn  16749:                     push(@badclasses,$class);
1.444     albertel 16750:                 }
                   16751:             }
                   16752:             $cenv{'internal.sectionnums'} =~ s/,$//;
                   16753:         }
                   16754:     }
                   16755: # do not hide course coordinator from staff listing, 
                   16756: # even if privileged
                   16757:     $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
1.1121    raeburn  16758: # add course coordinator's domain to domains to check for privileged users
                   16759: # if different to course domain
                   16760:     if ($$crsudom ne $args->{'ccdomain'}) {
                   16761:         $cenv{'checkforpriv'} = $args->{'ccdomain'};
                   16762:     }
1.444     albertel 16763: # add crosslistings
                   16764:     if ($args->{'crsxlist'}) {
                   16765:         $cenv{'internal.crosslistings'}='';
                   16766:         if ($args->{'crsxlist'} =~ m/,/) {
                   16767:             @xlists = split/,/,$args->{'crsxlist'};
                   16768:         } else {
                   16769:             $xlists[0] = $args->{'crsxlist'};
                   16770:         }
                   16771:         if (@xlists > 0) {
                   16772:             foreach my $item (@xlists) {
                   16773:                 my ($xl,$gp) = split/:/,$item;
                   16774:                 my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
                   16775:                 $cenv{'internal.crosslistings'} .= $item.',';
                   16776:                 unless ($addcheck eq 'ok') {
1.1263    raeburn  16777:                     push(@badclasses,$xl);
1.444     albertel 16778:                 }
                   16779:             }
                   16780:             $cenv{'internal.crosslistings'} =~ s/,$//;
                   16781:         }
                   16782:     }
                   16783:     if ($args->{'autoadds'}) {
                   16784:         $cenv{'internal.autoadds'}=$args->{'autoadds'};
                   16785:     }
                   16786:     if ($args->{'autodrops'}) {
                   16787:         $cenv{'internal.autodrops'}=$args->{'autodrops'};
                   16788:     }
                   16789: # check for notification of enrollment changes
                   16790:     my @notified = ();
                   16791:     if ($args->{'notify_owner'}) {
                   16792:         if ($args->{'ccuname'} ne '') {
                   16793:             push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
                   16794:         }
                   16795:     }
                   16796:     if ($args->{'notify_dc'}) {
                   16797:         if ($uname ne '') { 
1.630     raeburn  16798:             push(@notified,$uname.':'.$udom);
1.444     albertel 16799:         }
                   16800:     }
                   16801:     if (@notified > 0) {
                   16802:         my $notifylist;
                   16803:         if (@notified > 1) {
                   16804:             $notifylist = join(',',@notified);
                   16805:         } else {
                   16806:             $notifylist = $notified[0];
                   16807:         }
                   16808:         $cenv{'internal.notifylist'} = $notifylist;
                   16809:     }
                   16810:     if (@badclasses > 0) {
                   16811:         my %lt=&Apache::lonlocal::texthash(
1.1264    raeburn  16812:                 'tclb' => 'The courses listed below were included as sections or crosslistings affiliated with your new LON-CAPA course.',
                   16813:                 'howi' => 'However, if automated course roster updates are enabled for this class, these particular sections/crosslistings are not guaranteed to contribute towards enrollment.',
                   16814:                 'itis' => 'It is possible that rights to access enrollment for these classes will be available through assignment of co-owners.',
1.444     albertel 16815:         );
1.1264    raeburn  16816:         my $badclass_msg = $lt{'tclb'}.$linefeed.$lt{'howi'}.$linefeed.
                   16817:                            &mt('That is because the user identified as the course owner ([_1]) does not have rights to access enrollment in these classes, as determined by the policies of your institution on access to official classlists',$cenv{'internal.courseowner'}).$linefeed.$lt{'itis'};
1.541     raeburn  16818:         if ($context eq 'auto') {
                   16819:             $outcome .= $badclass_msg.$linefeed;
1.1261    raeburn  16820:         } else {
1.566     albertel 16821:             $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.1261    raeburn  16822:         }
                   16823:         foreach my $item (@badclasses) {
1.541     raeburn  16824:             if ($context eq 'auto') {
1.1261    raeburn  16825:                 $outcome .= " - $item\n";
1.541     raeburn  16826:             } else {
1.1261    raeburn  16827:                 $outcome .= "<li>$item</li>\n";
1.541     raeburn  16828:             }
1.1261    raeburn  16829:         }
                   16830:         if ($context eq 'auto') {
                   16831:             $outcome .= $linefeed;
                   16832:         } else {
                   16833:             $outcome .= "</ul><br /><br /></div>\n";
1.541     raeburn  16834:         } 
1.444     albertel 16835:     }
                   16836:     if ($args->{'no_end_date'}) {
                   16837:         $args->{'endaccess'} = 0;
                   16838:     }
                   16839:     $cenv{'internal.autostart'}=$args->{'enrollstart'};
                   16840:     $cenv{'internal.autoend'}=$args->{'enrollend'};
                   16841:     $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
                   16842:     $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
                   16843:     if ($args->{'showphotos'}) {
                   16844:       $cenv{'internal.showphotos'}=$args->{'showphotos'};
                   16845:     }
                   16846:     $cenv{'internal.authtype'} = $args->{'authtype'};
                   16847:     $cenv{'internal.autharg'} = $args->{'autharg'}; 
                   16848:     if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
                   16849:         if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'}  eq '') {
1.541     raeburn  16850:             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'); 
                   16851:             if ($context eq 'auto') {
                   16852:                 $outcome .= $krb_msg;
                   16853:             } else {
1.566     albertel 16854:                 $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541     raeburn  16855:             }
                   16856:             $outcome .= $linefeed;
1.444     albertel 16857:         }
                   16858:     }
                   16859:     if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
                   16860:        if ($args->{'setpolicy'}) {
                   16861:            $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   16862:        }
                   16863:        if ($args->{'setcontent'}) {
                   16864:            $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   16865:        }
1.1251    raeburn  16866:        if ($args->{'setcomment'}) {
                   16867:            $cenv{'comment.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
                   16868:        }
1.444     albertel 16869:     }
                   16870:     if ($args->{'reshome'}) {
                   16871: 	$cenv{'reshome'}=$args->{'reshome'}.'/';
                   16872: 	$cenv{'reshome'}=~s/\/+$/\//;
                   16873:     }
                   16874: #
                   16875: # course has keyed access
                   16876: #
                   16877:     if ($args->{'setkeys'}) {
                   16878:        $cenv{'keyaccess'}='yes';
                   16879:     }
                   16880: # if specified, key authority is not course, but user
                   16881: # only active if keyaccess is yes
                   16882:     if ($args->{'keyauth'}) {
1.487     albertel 16883: 	my ($user,$domain) = split(':',$args->{'keyauth'});
                   16884: 	$user = &LONCAPA::clean_username($user);
                   16885: 	$domain = &LONCAPA::clean_username($domain);
1.488     foxr     16886: 	if ($user ne '' && $domain ne '') {
1.487     albertel 16887: 	    $cenv{'keyauth'}=$user.':'.$domain;
1.444     albertel 16888: 	}
                   16889:     }
                   16890: 
1.1166    raeburn  16891: #
1.1167    raeburn  16892: #  generate and store uniquecode (available to course requester), if course should have one.
1.1166    raeburn  16893: #
                   16894:     if ($args->{'uniquecode'}) {
                   16895:         my ($code,$error) = &make_unique_code($$crsudom,$$crsunum);
                   16896:         if ($code) {
                   16897:             $cenv{'internal.uniquecode'} = $code;
1.1167    raeburn  16898:             my %crsinfo =
                   16899:                 &Apache::lonnet::courseiddump($$crsudom,'.',1,'.','.',$$crsunum,undef,undef,'.');
                   16900:             if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') {
                   16901:                 $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code;
                   16902:                 my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime');
                   16903:             } 
1.1166    raeburn  16904:             if (ref($coderef)) {
                   16905:                 $$coderef = $code;
                   16906:             }
                   16907:         }
                   16908:     }
                   16909: 
1.444     albertel 16910:     if ($args->{'disresdis'}) {
                   16911:         $cenv{'pch.roles.denied'}='st';
                   16912:     }
                   16913:     if ($args->{'disablechat'}) {
                   16914:         $cenv{'plc.roles.denied'}='st';
                   16915:     }
                   16916: 
                   16917:     # Record we've not yet viewed the Course Initialization Helper for this 
                   16918:     # course
                   16919:     $cenv{'course.helper.not.run'} = 1;
                   16920:     #
                   16921:     # Use new Randomseed
                   16922:     #
                   16923:     $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
                   16924:     $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
                   16925:     #
                   16926:     # The encryption code and receipt prefix for this course
                   16927:     #
                   16928:     $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
                   16929:     $cenv{'internal.encpref'}=100+int(9*rand(99));
                   16930:     #
                   16931:     # By default, use standard grading
                   16932:     if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
                   16933: 
1.541     raeburn  16934:     $outcome .= $linefeed.&mt('Setting environment').': '.                 
                   16935:           &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 16936: #
                   16937: # Open all assignments
                   16938: #
                   16939:     if ($args->{'openall'}) {
1.1341    raeburn  16940:        my $opendate = time;
                   16941:        if ($args->{'openallfrom'} =~ /^\d+$/) {
                   16942:            $opendate = $args->{'openallfrom'};
                   16943:        }
1.444     albertel 16944:        my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
1.1341    raeburn  16945:        my %storecontent = ($storeunder         => $opendate,
1.444     albertel 16946:                            $storeunder.'.type' => 'date_start');
1.1341    raeburn  16947:        $outcome .= &mt('All assignments open starting [_1]',
                   16948:                        &Apache::lonlocal::locallocaltime($opendate)).': '.
                   16949:                    &Apache::lonnet::cput
                   16950:                        ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444     albertel 16951:    }
                   16952: #
                   16953: # Set first page
                   16954: #
                   16955:     unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
                   16956: 	    || ($cloneid)) {
1.445     albertel 16957: 	use LONCAPA::map;
1.444     albertel 16958: 	$outcome .= &mt('Setting first resource').': ';
1.445     albertel 16959: 
                   16960: 	my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
                   16961:         my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
                   16962: 
1.444     albertel 16963:         $outcome .= ($fatal?$errtext:'read ok').' - ';
                   16964:         my $title; my $url;
                   16965:         if ($args->{'firstres'} eq 'syl') {
1.690     bisitz   16966: 	    $title=&mt('Syllabus');
1.444     albertel 16967:             $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
                   16968:         } else {
1.963     raeburn  16969:             $title=&mt('Table of Contents');
1.444     albertel 16970:             $url='/adm/navmaps';
                   16971:         }
1.445     albertel 16972: 
                   16973:         $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
                   16974: 	(my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
                   16975: 
                   16976: 	if ($errtext) { $fatal=2; }
1.541     raeburn  16977:         $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444     albertel 16978:     }
1.566     albertel 16979: 
1.1237    raeburn  16980: # 
                   16981: # Set params for Placement Tests
                   16982: #
1.1239    raeburn  16983:     if ($args->{'crstype'} eq 'Placement') {
                   16984:        my %storecontent; 
                   16985:        my $prefix=$$crsudom.'_'.$$crsunum.'.0.';
                   16986:        my %defaults = (
                   16987:                         buttonshide   => { value => 'yes',
                   16988:                                            type => 'string_yesno',},
                   16989:                         type          => { value => 'randomizetry',
                   16990:                                            type  => 'string_questiontype',},
                   16991:                         maxtries      => { value => 1,
                   16992:                                            type => 'int_pos',},
                   16993:                         problemstatus => { value => 'no',
                   16994:                                            type  => 'string_problemstatus',},
                   16995:                       );
                   16996:        foreach my $key (keys(%defaults)) {
                   16997:            $storecontent{$prefix.$key} = $defaults{$key}{'value'};
                   16998:            $storecontent{$prefix.$key.'.type'} = $defaults{$key}{'type'};
                   16999:        }
1.1237    raeburn  17000:        &Apache::lonnet::cput
                   17001:                  ('resourcedata',\%storecontent,$$crsudom,$$crsunum); 
                   17002:     }
                   17003: 
1.1344    raeburn  17004:     return (1,$outcome,\@clonemsg);
1.444     albertel 17005: }
                   17006: 
1.1166    raeburn  17007: sub make_unique_code {
                   17008:     my ($cdom,$cnum) = @_;
                   17009:     # get lock on uniquecodes db
                   17010:     my $lockhash = {
                   17011:                       $cnum."\0".'uniquecodes' => $env{'user.name'}.
                   17012:                                                   ':'.$env{'user.domain'},
                   17013:                    };
                   17014:     my $tries = 0;
                   17015:     my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
                   17016:     my ($code,$error);
                   17017:   
                   17018:     while (($gotlock ne 'ok') && ($tries<3)) {
                   17019:         $tries ++;
                   17020:         sleep 1;
                   17021:         $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
                   17022:     }
                   17023:     if ($gotlock eq 'ok') {
                   17024:         my %currcodes = &Apache::lonnet::dump_dom('uniquecodes',$cdom);
                   17025:         my $gotcode;
                   17026:         my $attempts = 0;
                   17027:         while ((!$gotcode) && ($attempts < 100)) {
                   17028:             $code = &generate_code();
                   17029:             if (!exists($currcodes{$code})) {
                   17030:                 $gotcode = 1;
                   17031:                 unless (&Apache::lonnet::newput_dom('uniquecodes',{ $code => $cnum },$cdom) eq 'ok') {
                   17032:                     $error = 'nostore';
                   17033:                 }
                   17034:             }
                   17035:             $attempts ++;
                   17036:         }
                   17037:         my @del_lock = ($cnum."\0".'uniquecodes');
                   17038:         my $dellockoutcome = &Apache::lonnet::del_dom('uniquecodes',\@del_lock,$cdom);
                   17039:     } else {
                   17040:         $error = 'nolock';
                   17041:     }
                   17042:     return ($code,$error);
                   17043: }
                   17044: 
                   17045: sub generate_code {
                   17046:     my $code;
                   17047:     my @letts = qw(B C D G H J K M N P Q R S T V W X Z);
                   17048:     for (my $i=0; $i<6; $i++) {
                   17049:         my $lettnum = int (rand 2);
                   17050:         my $item = '';
                   17051:         if ($lettnum) {
                   17052:             $item = $letts[int( rand(18) )];
                   17053:         } else {
                   17054:             $item = 1+int( rand(8) );
                   17055:         }
                   17056:         $code .= $item;
                   17057:     }
                   17058:     return $code;
                   17059: }
                   17060: 
1.444     albertel 17061: ############################################################
                   17062: ############################################################
                   17063: 
1.1237    raeburn  17064: # Community, Course and Placement Test
1.378     raeburn  17065: sub course_type {
                   17066:     my ($cid) = @_;
                   17067:     if (!defined($cid)) {
                   17068:         $cid = $env{'request.course.id'};
                   17069:     }
1.404     albertel 17070:     if (defined($env{'course.'.$cid.'.type'})) {
                   17071:         return $env{'course.'.$cid.'.type'};
1.378     raeburn  17072:     } else {
                   17073:         return 'Course';
1.377     raeburn  17074:     }
                   17075: }
1.156     albertel 17076: 
1.406     raeburn  17077: sub group_term {
                   17078:     my $crstype = &course_type();
                   17079:     my %names = (
                   17080:                   'Course' => 'group',
1.865     raeburn  17081:                   'Community' => 'group',
1.1237    raeburn  17082:                   'Placement' => 'group',
1.406     raeburn  17083:                 );
                   17084:     return $names{$crstype};
                   17085: }
                   17086: 
1.902     raeburn  17087: sub course_types {
1.1310    raeburn  17088:     my @types = ('official','unofficial','community','textbook','placement','lti');
1.902     raeburn  17089:     my %typename = (
                   17090:                          official   => 'Official course',
                   17091:                          unofficial => 'Unofficial course',
                   17092:                          community  => 'Community',
1.1165    raeburn  17093:                          textbook   => 'Textbook course',
1.1237    raeburn  17094:                          placement  => 'Placement test',
1.1310    raeburn  17095:                          lti        => 'LTI provider',
1.902     raeburn  17096:                    );
                   17097:     return (\@types,\%typename);
                   17098: }
                   17099: 
1.156     albertel 17100: sub icon {
                   17101:     my ($file)=@_;
1.505     albertel 17102:     my $curfext = lc((split(/\./,$file))[-1]);
1.168     albertel 17103:     my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156     albertel 17104:     my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168     albertel 17105:     if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
                   17106: 	if (-e  $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                   17107: 	          $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   17108: 	            $curfext.".gif") {
                   17109: 	    $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
                   17110: 		$curfext.".gif";
                   17111: 	}
                   17112:     }
1.249     albertel 17113:     return &lonhttpdurl($iconname);
1.154     albertel 17114: } 
1.84      albertel 17115: 
1.575     albertel 17116: sub lonhttpdurl {
1.692     www      17117: #
                   17118: # Had been used for "small fry" static images on separate port 8080.
                   17119: # Modify here if lightweight http functionality desired again.
                   17120: # Currently eliminated due to increasing firewall issues.
                   17121: #
1.575     albertel 17122:     my ($url)=@_;
1.692     www      17123:     return $url;
1.215     albertel 17124: }
                   17125: 
1.213     albertel 17126: sub connection_aborted {
                   17127:     my ($r)=@_;
                   17128:     $r->print(" ");$r->rflush();
                   17129:     my $c = $r->connection;
                   17130:     return $c->aborted();
                   17131: }
                   17132: 
1.221     foxr     17133: #    Escapes strings that may have embedded 's that will be put into
1.222     foxr     17134: #    strings as 'strings'.
                   17135: sub escape_single {
1.221     foxr     17136:     my ($input) = @_;
1.223     albertel 17137:     $input =~ s/\\/\\\\/g;	# Escape the \'s..(must be first)>
1.221     foxr     17138:     $input =~ s/\'/\\\'/g;	# Esacpe the 's....
                   17139:     return $input;
                   17140: }
1.223     albertel 17141: 
1.222     foxr     17142: #  Same as escape_single, but escape's "'s  This 
                   17143: #  can be used for  "strings"
                   17144: sub escape_double {
                   17145:     my ($input) = @_;
                   17146:     $input =~ s/\\/\\\\/g;	# Escape the /'s..(must be first)>
                   17147:     $input =~ s/\"/\\\"/g;	# Esacpe the "s....
                   17148:     return $input;
                   17149: }
1.223     albertel 17150:  
1.222     foxr     17151: #   Escapes the last element of a full URL.
                   17152: sub escape_url {
                   17153:     my ($url)   = @_;
1.238     raeburn  17154:     my @urlslices = split(/\//, $url,-1);
1.369     www      17155:     my $lastitem = &escape(pop(@urlslices));
1.1203    raeburn  17156:     return &HTML::Entities::encode(join('/',@urlslices),"'").'/'.$lastitem;
1.222     foxr     17157: }
1.462     albertel 17158: 
1.820     raeburn  17159: sub compare_arrays {
                   17160:     my ($arrayref1,$arrayref2) = @_;
                   17161:     my (@difference,%count);
                   17162:     @difference = ();
                   17163:     %count = ();
                   17164:     if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
                   17165:         foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
                   17166:         foreach my $element (keys(%count)) {
                   17167:             if ($count{$element} == 1) {
                   17168:                 push(@difference,$element);
                   17169:             }
                   17170:         }
                   17171:     }
                   17172:     return @difference;
                   17173: }
                   17174: 
1.1322    raeburn  17175: sub lon_status_items {
                   17176:     my %defaults = (
                   17177:                      E         => 100,
                   17178:                      W         => 4,
                   17179:                      N         => 1,
1.1324    raeburn  17180:                      U         => 5,
1.1322    raeburn  17181:                      threshold => 200,
                   17182:                      sysmail   => 2500,
                   17183:                    );
                   17184:     my %names = (
                   17185:                    E => 'Errors',
                   17186:                    W => 'Warnings',
                   17187:                    N => 'Notices',
1.1324    raeburn  17188:                    U => 'Unsent',
1.1322    raeburn  17189:                 );
                   17190:     return (\%defaults,\%names);
                   17191: }
                   17192: 
1.817     bisitz   17193: # -------------------------------------------------------- Initialize user login
1.462     albertel 17194: sub init_user_environment {
1.463     albertel 17195:     my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462     albertel 17196:     my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
                   17197: 
                   17198:     my $public=($username eq 'public' && $domain eq 'public');
                   17199: 
1.1062    raeburn  17200:     my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
1.462     albertel 17201:     my $now=time;
                   17202: 
                   17203:     if ($public) {
                   17204: 	my $max_public=100;
                   17205: 	my $oldest;
                   17206: 	my $oldest_time=0;
                   17207: 	for(my $next=1;$next<=$max_public;$next++) {
                   17208: 	    if (-e $lonids."/publicuser_$next.id") {
                   17209: 		my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
                   17210: 		if ($mtime<$oldest_time || !$oldest_time) {
                   17211: 		    $oldest_time=$mtime;
                   17212: 		    $oldest=$next;
                   17213: 		}
                   17214: 	    } else {
                   17215: 		$cookie="publicuser_$next";
                   17216: 		last;
                   17217: 	    }
                   17218: 	}
                   17219: 	if (!$cookie) { $cookie="publicuser_$oldest"; }
                   17220:     } else {
1.1275    raeburn  17221: 	# See if old ID present, if so, remove if this isn't a robot,
                   17222: 	# killing any existing non-robot sessions
1.463     albertel 17223: 	if (!$args->{'robot'}) {
                   17224: 	    opendir(DIR,$lonids);
                   17225: 	    while ($filename=readdir(DIR)) {
                   17226: 		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
1.1320    raeburn  17227:                     if (tie(my %oldenv,'GDBM_File',"$lonids/$filename",
                   17228:                             &GDBM_READER(),0640)) {
1.1295    raeburn  17229:                         my $linkedfile;
1.1320    raeburn  17230:                         if (exists($oldenv{'user.linkedenv'})) {
                   17231:                             $linkedfile = $oldenv{'user.linkedenv'};
1.1295    raeburn  17232:                         }
1.1320    raeburn  17233:                         untie(%oldenv);
                   17234:                         if (unlink("$lonids/$filename")) {
                   17235:                             if ($linkedfile =~ /^[a-f0-9]+_linked$/) {
                   17236:                                 if (-l "$lonids/$linkedfile.id") {
                   17237:                                     unlink("$lonids/$linkedfile.id");
                   17238:                                 }
1.1295    raeburn  17239:                             }
                   17240:                         }
                   17241:                     } else {
                   17242:                         unlink($lonids.'/'.$filename);
                   17243:                     }
1.463     albertel 17244: 		}
1.462     albertel 17245: 	    }
1.463     albertel 17246: 	    closedir(DIR);
1.1204    raeburn  17247: # If there is a undeleted lockfile for the user's paste buffer remove it.
                   17248:             my $namespace = 'nohist_courseeditor';
                   17249:             my $lockingkey = 'paste'."\0".'locked_num';
                   17250:             my %lockhash = &Apache::lonnet::get($namespace,[$lockingkey],
                   17251:                                                 $domain,$username);
                   17252:             if (exists($lockhash{$lockingkey})) {
                   17253:                 my $delresult = &Apache::lonnet::del($namespace,[$lockingkey],$domain,$username);
                   17254:                 unless ($delresult eq 'ok') {
                   17255:                     &Apache::lonnet::logthis("Failed to delete paste buffer locking key in $namespace for ".$username.":".$domain." Result was: $delresult");
                   17256:                 }
                   17257:             }
1.462     albertel 17258: 	}
                   17259: # Give them a new cookie
1.463     albertel 17260: 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684     www      17261: 		                   : $now.$$.int(rand(10000)));
1.463     albertel 17262: 	$cookie="$username\_$id\_$domain\_$authhost";
1.462     albertel 17263:     
                   17264: # Initialize roles
                   17265: 
1.1062    raeburn  17266: 	($userroles,$firstaccenv,$timerintenv) = 
                   17267:             &Apache::lonnet::rolesinit($domain,$username,$authhost);
1.462     albertel 17268:     }
                   17269: # ------------------------------------ Check browser type and MathML capability
                   17270: 
1.1194    raeburn  17271:     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,$clientunicode,
                   17272:         $clientos,$clientmobile,$clientinfo,$clientosversion) = &decode_user_agent($r);
1.462     albertel 17273: 
                   17274: # ------------------------------------------------------------- Get environment
                   17275: 
                   17276:     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
                   17277:     my ($tmp) = keys(%userenv);
1.1275    raeburn  17278:     if ($tmp =~ /^(con_lost|error|no_such_host)/i) {
1.462     albertel 17279: 	undef(%userenv);
                   17280:     }
                   17281:     if (($userenv{'interface'}) && (!$form->{'interface'})) {
                   17282: 	$form->{'interface'}=$userenv{'interface'};
                   17283:     }
                   17284:     if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
                   17285: 
                   17286: # --------------- Do not trust query string to be put directly into environment
1.817     bisitz   17287:     foreach my $option ('interface','localpath','localres') {
                   17288:         $form->{$option}=~s/[\n\r\=]//gs;
1.462     albertel 17289:     }
                   17290: # --------------------------------------------------------- Write first profile
                   17291: 
                   17292:     {
1.1350    raeburn  17293:         my $ip = &Apache::lonnet::get_requestor_ip($r);
1.462     albertel 17294: 	my %initial_env = 
                   17295: 	    ("user.name"          => $username,
                   17296: 	     "user.domain"        => $domain,
                   17297: 	     "user.home"          => $authhost,
                   17298: 	     "browser.type"       => $clientbrowser,
                   17299: 	     "browser.version"    => $clientversion,
                   17300: 	     "browser.mathml"     => $clientmathml,
                   17301: 	     "browser.unicode"    => $clientunicode,
                   17302: 	     "browser.os"         => $clientos,
1.1137    raeburn  17303:              "browser.mobile"     => $clientmobile,
1.1141    raeburn  17304:              "browser.info"       => $clientinfo,
1.1194    raeburn  17305:              "browser.osversion"  => $clientosversion,
1.462     albertel 17306: 	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
                   17307: 	     "request.course.fn"  => '',
                   17308: 	     "request.course.uri" => '',
                   17309: 	     "request.course.sec" => '',
                   17310: 	     "request.role"       => 'cm',
                   17311: 	     "request.role.adv"   => $env{'user.adv'},
1.1350    raeburn  17312: 	     "request.host"       => $ip,);
1.462     albertel 17313: 
                   17314:         if ($form->{'localpath'}) {
                   17315: 	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
                   17316: 	    $initial_env{"browser.localres"}   = $form->{'localres'};
                   17317:         }
                   17318: 	
                   17319: 	if ($form->{'interface'}) {
                   17320: 	    $form->{'interface'}=~s/\W//gs;
                   17321: 	    $initial_env{"browser.interface"} = $form->{'interface'};
                   17322: 	    $env{'browser.interface'}=$form->{'interface'};
                   17323: 	}
                   17324: 
1.1157    raeburn  17325:         if ($form->{'iptoken'}) {
                   17326:             my $lonhost = $r->dir_config('lonHostID');
                   17327:             $initial_env{"user.noloadbalance"} = $lonhost;
                   17328:             $env{'user.noloadbalance'} = $lonhost;
                   17329:         }
                   17330: 
1.1268    raeburn  17331:         if ($form->{'noloadbalance'}) {
                   17332:             my @hosts = &Apache::lonnet::current_machine_ids();
                   17333:             my $hosthere = $form->{'noloadbalance'};
                   17334:             if (grep(/^\Q$hosthere\E$/,@hosts)) {
                   17335:                 $initial_env{"user.noloadbalance"} = $hosthere;
                   17336:                 $env{'user.noloadbalance'} = $hosthere;
                   17337:             }
                   17338:         }
                   17339: 
1.1016    raeburn  17340:         unless ($domain eq 'public') {
1.1273    raeburn  17341:             my %is_adv = ( is_adv => $env{'user.adv'} );
                   17342:             my %domdef = &Apache::lonnet::get_domain_defaults($domain);
                   17343: 
                   17344:             foreach my $tool ('aboutme','blog','webdav','portfolio') {
                   17345:                 $userenv{'availabletools.'.$tool} = 
                   17346:                     &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
                   17347:                                                       undef,\%userenv,\%domdef,\%is_adv);
                   17348:             }
1.980     raeburn  17349: 
1.1311    raeburn  17350:             foreach my $crstype ('official','unofficial','community','textbook','placement','lti') {
1.1273    raeburn  17351:                 $userenv{'canrequest.'.$crstype} =
                   17352:                     &Apache::lonnet::usertools_access($username,$domain,$crstype,
                   17353:                                                       'reload','requestcourses',
                   17354:                                                       \%userenv,\%domdef,\%is_adv);
                   17355:             }
1.724     raeburn  17356: 
1.1273    raeburn  17357:             $userenv{'canrequest.author'} =
                   17358:                 &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
                   17359:                                                   'reload','requestauthor',
1.980     raeburn  17360:                                                   \%userenv,\%domdef,\%is_adv);
1.1273    raeburn  17361:             my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
                   17362:                                                  $domain,$username);
                   17363:             my $reqstatus = $reqauthor{'author_status'};
                   17364:             if ($reqstatus eq 'approval' || $reqstatus eq 'approved') { 
                   17365:                 if (ref($reqauthor{'author'}) eq 'HASH') {
                   17366:                     $userenv{'requestauthorqueued'} = $reqstatus.':'.
                   17367:                                                       $reqauthor{'author'}{'timestamp'};
                   17368:                 }
1.1092    raeburn  17369:             }
1.1287    raeburn  17370:             my ($types,$typename) = &course_types();
                   17371:             if (ref($types) eq 'ARRAY') {
                   17372:                 my @options = ('approval','validate','autolimit');
                   17373:                 my $optregex = join('|',@options);
                   17374:                 my (%willtrust,%trustchecked);
                   17375:                 foreach my $type (@{$types}) {
                   17376:                     my $dom_str = $env{'environment.reqcrsotherdom.'.$type};
                   17377:                     if ($dom_str ne '') {
                   17378:                         my $updatedstr = '';
                   17379:                         my @possdomains = split(',',$dom_str);
                   17380:                         foreach my $entry (@possdomains) {
                   17381:                             my ($extdom,$extopt) = split(':',$entry);
                   17382:                             unless ($trustchecked{$extdom}) {
                   17383:                                 $willtrust{$extdom} = &Apache::lonnet::will_trust('reqcrs',$domain,$extdom);
                   17384:                                 $trustchecked{$extdom} = 1;
                   17385:                             }
                   17386:                             if ($willtrust{$extdom}) {
                   17387:                                 $updatedstr .= $entry.',';
                   17388:                             }
                   17389:                         }
                   17390:                         $updatedstr =~ s/,$//;
                   17391:                         if ($updatedstr) {
                   17392:                             $userenv{'reqcrsotherdom.'.$type} = $updatedstr;
                   17393:                         } else {
                   17394:                             delete($userenv{'reqcrsotherdom.'.$type});
                   17395:                         }
                   17396:                     }
                   17397:                 }
                   17398:             }
1.1092    raeburn  17399:         }
1.462     albertel 17400: 	$env{'user.environment'} = "$lonids/$cookie.id";
1.1062    raeburn  17401: 
1.462     albertel 17402: 	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
                   17403: 		 &GDBM_WRCREAT(),0640)) {
                   17404: 	    &_add_to_env(\%disk_env,\%initial_env);
                   17405: 	    &_add_to_env(\%disk_env,\%userenv,'environment.');
                   17406: 	    &_add_to_env(\%disk_env,$userroles);
1.1062    raeburn  17407:             if (ref($firstaccenv) eq 'HASH') {
                   17408:                 &_add_to_env(\%disk_env,$firstaccenv);
                   17409:             }
                   17410:             if (ref($timerintenv) eq 'HASH') {
                   17411:                 &_add_to_env(\%disk_env,$timerintenv);
                   17412:             }
1.463     albertel 17413: 	    if (ref($args->{'extra_env'})) {
                   17414: 		&_add_to_env(\%disk_env,$args->{'extra_env'});
                   17415: 	    }
1.462     albertel 17416: 	    untie(%disk_env);
                   17417: 	} else {
1.705     tempelho 17418: 	    &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
                   17419: 			   'Could not create environment storage in lonauth: '.$!.'</span>');
1.462     albertel 17420: 	    return 'error: '.$!;
                   17421: 	}
                   17422:     }
                   17423:     $env{'request.role'}='cm';
                   17424:     $env{'request.role.adv'}=$env{'user.adv'};
                   17425:     $env{'browser.type'}=$clientbrowser;
                   17426: 
                   17427:     return $cookie;
                   17428: 
                   17429: }
                   17430: 
                   17431: sub _add_to_env {
                   17432:     my ($idf,$env_data,$prefix) = @_;
1.676     raeburn  17433:     if (ref($env_data) eq 'HASH') {
                   17434:         while (my ($key,$value) = each(%$env_data)) {
                   17435: 	    $idf->{$prefix.$key} = $value;
                   17436: 	    $env{$prefix.$key}   = $value;
                   17437:         }
1.462     albertel 17438:     }
                   17439: }
                   17440: 
1.685     tempelho 17441: # --- Get the symbolic name of a problem and the url
                   17442: sub get_symb {
                   17443:     my ($request,$silent) = @_;
1.726     raeburn  17444:     (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685     tempelho 17445:     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
                   17446:     if ($symb eq '') {
                   17447:         if (!$silent) {
1.1071    raeburn  17448:             if (ref($request)) { 
                   17449:                 $request->print("Unable to handle ambiguous references:$url:.");
                   17450:             }
1.685     tempelho 17451:             return ();
                   17452:         }
                   17453:     }
                   17454:     &Apache::lonenc::check_decrypt(\$symb);
                   17455:     return ($symb);
                   17456: }
                   17457: 
                   17458: # --------------------------------------------------------------Get annotation
                   17459: 
                   17460: sub get_annotation {
                   17461:     my ($symb,$enc) = @_;
                   17462: 
                   17463:     my $key = $symb;
                   17464:     if (!$enc) {
                   17465:         $key =
                   17466:             &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
                   17467:     }
                   17468:     my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
                   17469:     return $annotation{$key};
                   17470: }
                   17471: 
                   17472: sub clean_symb {
1.731     raeburn  17473:     my ($symb,$delete_enc) = @_;
1.685     tempelho 17474: 
                   17475:     &Apache::lonenc::check_decrypt(\$symb);
                   17476:     my $enc = $env{'request.enc'};
1.731     raeburn  17477:     if ($delete_enc) {
1.730     raeburn  17478:         delete($env{'request.enc'});
                   17479:     }
1.685     tempelho 17480: 
                   17481:     return ($symb,$enc);
                   17482: }
1.462     albertel 17483: 
1.1181    raeburn  17484: ############################################################
                   17485: ############################################################
                   17486: 
                   17487: =pod
                   17488: 
                   17489: =head1 Routines for building display used to search for courses
                   17490: 
                   17491: 
                   17492: =over 4
                   17493: 
                   17494: =item * &build_filters()
                   17495: 
                   17496: Create markup for a table used to set filters to use when selecting
1.1182    raeburn  17497: courses in a domain.  Used by lonpickcourse.pm, lonmodifycourse.pm
                   17498: and quotacheck.pl
                   17499: 
1.1181    raeburn  17500: 
                   17501: Inputs:
                   17502: 
                   17503: filterlist - anonymous array of fields to include as potential filters 
                   17504: 
                   17505: crstype - course type
                   17506: 
                   17507: roleelement - fifth arg in selectcourse_link() populates fifth arg in javascript: opencrsbrowser() function, used
                   17508:               to pop-open a course selector (will contain "extra element"). 
                   17509: 
                   17510: multelement - if multiple course selections will be allowed, this will be a hidden form element: name: multiple; value: 1
                   17511: 
                   17512: filter - anonymous hash of criteria and their values
                   17513: 
                   17514: action - form action
                   17515: 
                   17516: numfiltersref - ref to scalar (count of number of elements in institutional codes -- e.g., 4 for year, semester, department, and number)
                   17517: 
1.1182    raeburn  17518: caller - caller context (e.g., set to 'modifycourse' when routine is called from lonmodifycourse.pm)
1.1181    raeburn  17519: 
                   17520: cloneruname - username of owner of new course who wants to clone
                   17521: 
                   17522: clonerudom - domain of owner of new course who wants to clone
                   17523: 
                   17524: typeelem - text to use for left column in row containing course type (i.e., Course, Community or Course/Community) 
                   17525: 
                   17526: codetitlesref - reference to array of titles of components in institutional codes (official courses)
                   17527: 
                   17528: codedom - domain
                   17529: 
                   17530: formname - value of form element named "form". 
                   17531: 
                   17532: fixeddom - domain, if fixed.
                   17533: 
                   17534: prevphase - value to assign to form element named "phase" when going back to the previous screen  
                   17535: 
                   17536: cnameelement - name of form element in form on opener page which will receive title of selected course 
                   17537: 
                   17538: cnumelement - name of form element in form on opener page which will receive courseID  of selected course
                   17539: 
                   17540: cdomelement - name of form element in form on opener page which will receive domain of selected course
                   17541: 
                   17542: setroles - includes access constraint identifier when setting a roles-based condition for acces to a portfolio file
                   17543: 
                   17544: clonetext - hidden form elements containing list of courses cloneable by intended course owner when DC creates a course
                   17545: 
                   17546: clonewarning - warning message about missing information for intended course owner when DC creates a course
                   17547: 
1.1182    raeburn  17548: 
1.1181    raeburn  17549: Returns: $output - HTML for display of search criteria, and hidden form elements.
                   17550: 
1.1182    raeburn  17551: 
1.1181    raeburn  17552: Side Effects: None
                   17553: 
                   17554: =cut
                   17555: 
                   17556: # ---------------------------------------------- search for courses based on last activity etc.
                   17557: 
                   17558: sub build_filters {
                   17559:     my ($filterlist,$crstype,$roleelement,$multelement,$filter,$action,
                   17560:         $numtitlesref,$caller,$cloneruname,$clonerudom,$typeelement,
                   17561:         $codetitlesref,$codedom,$formname,$fixeddom,$prevphase,
                   17562:         $cnameelement,$cnumelement,$cdomelement,$setroles,
                   17563:         $clonetext,$clonewarning) = @_;
1.1182    raeburn  17564:     my ($list,$jscript);
1.1181    raeburn  17565:     my $onchange = 'javascript:updateFilters(this)';
                   17566:     my ($domainselectform,$sincefilterform,$createdfilterform,
                   17567:         $ownerdomselectform,$persondomselectform,$instcodeform,
                   17568:         $typeselectform,$instcodetitle);
                   17569:     if ($formname eq '') {
                   17570:         $formname = $caller;
                   17571:     }
                   17572:     foreach my $item (@{$filterlist}) {
                   17573:         unless (($item eq 'descriptfilter') || ($item eq 'instcodefilter') ||
                   17574:                 ($item eq 'sincefilter') || ($item eq 'createdfilter')) {
                   17575:             if ($item eq 'domainfilter') {
                   17576:                 $filter->{$item} = &LONCAPA::clean_domain($filter->{$item});
                   17577:             } elsif ($item eq 'coursefilter') {
                   17578:                 $filter->{$item} = &LONCAPA::clean_courseid($filter->{$item});
                   17579:             } elsif ($item eq 'ownerfilter') {
                   17580:                 $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
                   17581:             } elsif ($item eq 'ownerdomfilter') {
                   17582:                 $filter->{'ownerdomfilter'} =
                   17583:                     &LONCAPA::clean_domain($filter->{$item});
                   17584:                 $ownerdomselectform = &select_dom_form($filter->{'ownerdomfilter'},
                   17585:                                                        'ownerdomfilter',1);
                   17586:             } elsif ($item eq 'personfilter') {
                   17587:                 $filter->{$item} = &LONCAPA::clean_username($filter->{$item});
                   17588:             } elsif ($item eq 'persondomfilter') {
                   17589:                 $persondomselectform = &select_dom_form($filter->{'persondomfilter'},
                   17590:                                                         'persondomfilter',1);
                   17591:             } else {
                   17592:                 $filter->{$item} =~ s/\W//g;
                   17593:             }
                   17594:             if (!$filter->{$item}) {
                   17595:                 $filter->{$item} = '';
                   17596:             }
                   17597:         }
                   17598:         if ($item eq 'domainfilter') {
                   17599:             my $allow_blank = 1;
                   17600:             if ($formname eq 'portform') {
                   17601:                 $allow_blank=0;
                   17602:             } elsif ($formname eq 'studentform') {
                   17603:                 $allow_blank=0;
                   17604:             }
                   17605:             if ($fixeddom) {
                   17606:                 $domainselectform = '<input type="hidden" name="domainfilter"'.
                   17607:                                     ' value="'.$codedom.'" />'.
                   17608:                                     &Apache::lonnet::domain($codedom,'description');
                   17609:             } else {
                   17610:                 $domainselectform = &select_dom_form($filter->{$item},
                   17611:                                                      'domainfilter',
                   17612:                                                       $allow_blank,'',$onchange);
                   17613:             }
                   17614:         } else {
                   17615:             $list->{$item} = &HTML::Entities::encode($filter->{$item},'<>&"');
                   17616:         }
                   17617:     }
                   17618: 
                   17619:     # last course activity filter and selection
                   17620:     $sincefilterform = &timebased_select_form('sincefilter',$filter);
                   17621: 
                   17622:     # course created filter and selection
                   17623:     if (exists($filter->{'createdfilter'})) {
                   17624:         $createdfilterform = &timebased_select_form('createdfilter',$filter);
                   17625:     }
                   17626: 
1.1239    raeburn  17627:     my $prefix = $crstype;
                   17628:     if ($crstype eq 'Placement') {
                   17629:         $prefix = 'Placement Test'
                   17630:     }
1.1181    raeburn  17631:     my %lt = &Apache::lonlocal::texthash(
1.1239    raeburn  17632:                 'cac' => "$prefix Activity",
                   17633:                 'ccr' => "$prefix Created",
                   17634:                 'cde' => "$prefix Title",
                   17635:                 'cdo' => "$prefix Domain",
1.1181    raeburn  17636:                 'ins' => 'Institutional Code',
                   17637:                 'inc' => 'Institutional Categorization',
1.1239    raeburn  17638:                 'cow' => "$prefix Owner/Co-owner",
                   17639:                 'cop' => "$prefix Personnel Includes",
1.1181    raeburn  17640:                 'cog' => 'Type',
                   17641:              );
                   17642: 
                   17643:     if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
                   17644:         my $typeval = 'Course';
                   17645:         if ($crstype eq 'Community') {
                   17646:             $typeval = 'Community';
1.1239    raeburn  17647:         } elsif ($crstype eq 'Placement') {
                   17648:             $typeval = 'Placement';
1.1181    raeburn  17649:         }
                   17650:         $typeselectform = '<input type="hidden" name="type" value="'.$typeval.'" />';
                   17651:     } else {
                   17652:         $typeselectform =  '<select name="type" size="1"';
                   17653:         if ($onchange) {
                   17654:             $typeselectform .= ' onchange="'.$onchange.'"';
                   17655:         }
                   17656:         $typeselectform .= '>'."\n";
1.1237    raeburn  17657:         foreach my $posstype ('Course','Community','Placement') {
1.1239    raeburn  17658:             my $shown;
                   17659:             if ($posstype eq 'Placement') {
                   17660:                 $shown = &mt('Placement Test');
                   17661:             } else {
                   17662:                 $shown = &mt($posstype);
                   17663:             }
1.1181    raeburn  17664:             $typeselectform.='<option value="'.$posstype.'"'.
1.1239    raeburn  17665:                 ($posstype eq $crstype ? ' selected="selected" ' : ''). ">".$shown."</option>\n";
1.1181    raeburn  17666:         }
                   17667:         $typeselectform.="</select>";
                   17668:     }
                   17669: 
                   17670:     my ($cloneableonlyform,$cloneabletitle);
                   17671:     if (exists($filter->{'cloneableonly'})) {
                   17672:         my $cloneableon = '';
                   17673:         my $cloneableoff = ' checked="checked"';
                   17674:         if ($filter->{'cloneableonly'}) {
                   17675:             $cloneableon = $cloneableoff;
                   17676:             $cloneableoff = '';
                   17677:         }
                   17678:         $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>';
                   17679:         if ($formname eq 'ccrs') {
1.1187    bisitz   17680:             $cloneabletitle = &mt('Cloneable for [_1]',$cloneruname.':'.$clonerudom);
1.1181    raeburn  17681:         } else {
                   17682:             $cloneabletitle = &mt('Cloneable by you');
                   17683:         }
                   17684:     }
                   17685:     my $officialjs;
                   17686:     if ($crstype eq 'Course') {
                   17687:         if (exists($filter->{'instcodefilter'})) {
1.1182    raeburn  17688: #            if (($fixeddom) || ($formname eq 'requestcrs') ||
                   17689: #                ($formname eq 'modifycourse') || ($formname eq 'filterpicker')) {
                   17690:             if ($codedom) { 
1.1181    raeburn  17691:                 $officialjs = 1;
                   17692:                 ($instcodeform,$jscript,$$numtitlesref) =
                   17693:                     &Apache::courseclassifier::instcode_selectors($codedom,'filterpicker',
                   17694:                                                                   $officialjs,$codetitlesref);
                   17695:                 if ($jscript) {
1.1182    raeburn  17696:                     $jscript = '<script type="text/javascript">'."\n".
                   17697:                                '// <![CDATA['."\n".
                   17698:                                $jscript."\n".
                   17699:                                '// ]]>'."\n".
                   17700:                                '</script>'."\n";
1.1181    raeburn  17701:                 }
                   17702:             }
                   17703:             if ($instcodeform eq '') {
                   17704:                 $instcodeform =
                   17705:                     '<input type="text" name="instcodefilter" size="10" value="'.
                   17706:                     $list->{'instcodefilter'}.'" />';
                   17707:                 $instcodetitle = $lt{'ins'};
                   17708:             } else {
                   17709:                 $instcodetitle = $lt{'inc'};
                   17710:             }
                   17711:             if ($fixeddom) {
                   17712:                 $instcodetitle .= '<br />('.$codedom.')';
                   17713:             }
                   17714:         }
                   17715:     }
                   17716:     my $output = qq|
                   17717: <form method="post" name="filterpicker" action="$action">
                   17718: <input type="hidden" name="form" value="$formname" />
                   17719: |;
                   17720:     if ($formname eq 'modifycourse') {
                   17721:         $output .= '<input type="hidden" name="phase" value="courselist" />'."\n".
                   17722:                    '<input type="hidden" name="prevphase" value="'.
                   17723:                    $prevphase.'" />'."\n";
1.1198    musolffc 17724:     } elsif ($formname eq 'quotacheck') {
                   17725:         $output .= qq|
                   17726: <input type="hidden" name="sortby" value="" />
                   17727: <input type="hidden" name="sortorder" value="" />
                   17728: |;
                   17729:     } else {
1.1181    raeburn  17730:         my $name_input;
                   17731:         if ($cnameelement ne '') {
                   17732:             $name_input = '<input type="hidden" name="cnameelement" value="'.
                   17733:                           $cnameelement.'" />';
                   17734:         }
                   17735:         $output .= qq|
1.1182    raeburn  17736: <input type="hidden" name="cnumelement" value="$cnumelement" />
                   17737: <input type="hidden" name="cdomelement" value="$cdomelement" />
1.1181    raeburn  17738: $name_input
                   17739: $roleelement
                   17740: $multelement
                   17741: $typeelement
                   17742: |;
                   17743:         if ($formname eq 'portform') {
                   17744:             $output .= '<input type="hidden" name="setroles" value="'.$setroles.'" />'."\n";
                   17745:         }
                   17746:     }
                   17747:     if ($fixeddom) {
                   17748:         $output .= '<input type="hidden" name="fixeddom" value="'.$fixeddom.'" />'."\n";
                   17749:     }
                   17750:     $output .= "<br />\n".&Apache::lonhtmlcommon::start_pick_box();
                   17751:     if ($sincefilterform) {
                   17752:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cac'})
                   17753:                   .$sincefilterform
                   17754:                   .&Apache::lonhtmlcommon::row_closure();
                   17755:     }
                   17756:     if ($createdfilterform) {
                   17757:         $output .= &Apache::lonhtmlcommon::row_title($lt{'ccr'})
                   17758:                   .$createdfilterform
                   17759:                   .&Apache::lonhtmlcommon::row_closure();
                   17760:     }
                   17761:     if ($domainselectform) {
                   17762:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cdo'})
                   17763:                   .$domainselectform
                   17764:                   .&Apache::lonhtmlcommon::row_closure();
                   17765:     }
                   17766:     if ($typeselectform) {
                   17767:         if (($formname eq 'ccrs') || ($formname eq 'requestcrs')) {
                   17768:             $output .= $typeselectform;
                   17769:         } else {
                   17770:             $output .= &Apache::lonhtmlcommon::row_title($lt{'cog'})
                   17771:                       .$typeselectform
                   17772:                       .&Apache::lonhtmlcommon::row_closure();
                   17773:         }
                   17774:     }
                   17775:     if ($instcodeform) {
                   17776:         $output .= &Apache::lonhtmlcommon::row_title($instcodetitle)
                   17777:                   .$instcodeform
                   17778:                   .&Apache::lonhtmlcommon::row_closure();
                   17779:     }
                   17780:     if (exists($filter->{'ownerfilter'})) {
                   17781:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cow'}).
                   17782:                    '<table><tr><td>'.&mt('Username').'<br />'.
                   17783:                    '<input type="text" name="ownerfilter" size="20" value="'.
                   17784:                    $list->{'ownerfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
                   17785:                    $ownerdomselectform.'</td></tr></table>'.
                   17786:                    &Apache::lonhtmlcommon::row_closure();
                   17787:     }
                   17788:     if (exists($filter->{'personfilter'})) {
                   17789:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cop'}).
                   17790:                    '<table><tr><td>'.&mt('Username').'<br />'.
                   17791:                    '<input type="text" name="personfilter" size="20" value="'.
                   17792:                    $list->{'personfilter'}.'" /></td><td>'.&mt('Domain').'<br />'.
                   17793:                    $persondomselectform.'</td></tr></table>'.
                   17794:                    &Apache::lonhtmlcommon::row_closure();
                   17795:     }
                   17796:     if (exists($filter->{'coursefilter'})) {
                   17797:         $output .= &Apache::lonhtmlcommon::row_title(&mt('LON-CAPA course ID'))
                   17798:                   .'<input type="text" name="coursefilter" size="25" value="'
                   17799:                   .$list->{'coursefilter'}.'" />'
                   17800:                   .&Apache::lonhtmlcommon::row_closure();
                   17801:     }
                   17802:     if ($cloneableonlyform) {
                   17803:         $output .= &Apache::lonhtmlcommon::row_title($cloneabletitle).
                   17804:                    $cloneableonlyform.&Apache::lonhtmlcommon::row_closure();
                   17805:     }
                   17806:     if (exists($filter->{'descriptfilter'})) {
                   17807:         $output .= &Apache::lonhtmlcommon::row_title($lt{'cde'})
                   17808:                   .'<input type="text" name="descriptfilter" size="40" value="'
                   17809:                   .$list->{'descriptfilter'}.'" />'
                   17810:                   .&Apache::lonhtmlcommon::row_closure(1);
                   17811:     }
                   17812:     $output .= &Apache::lonhtmlcommon::end_pick_box().'<p>'.$clonetext."\n".
                   17813:                '<input type="hidden" name="updater" value="" />'."\n".
                   17814:                '<input type="submit" name="gosearch" value="'.
                   17815:                &mt('Search').'" /></p>'."\n".'</form>'."\n".'<hr />'."\n";
                   17816:     return $jscript.$clonewarning.$output;
                   17817: }
                   17818: 
                   17819: =pod 
                   17820: 
                   17821: =item * &timebased_select_form()
                   17822: 
1.1182    raeburn  17823: Create markup for a dropdown list used to select a time-based
1.1181    raeburn  17824: filter e.g., Course Activity, Course Created, when searching for courses
                   17825: or communities
                   17826: 
                   17827: Inputs:
                   17828: 
                   17829: item - name of form element (sincefilter or createdfilter)
                   17830: 
                   17831: filter - anonymous hash of criteria and their values
                   17832: 
                   17833: Returns: HTML for a select box contained a blank, then six time selections,
                   17834:          with value set in incoming form variables currently selected. 
                   17835: 
                   17836: Side Effects: None
                   17837: 
                   17838: =cut
                   17839: 
                   17840: sub timebased_select_form {
                   17841:     my ($item,$filter) = @_;
                   17842:     if (ref($filter) eq 'HASH') {
                   17843:         $filter->{$item} =~ s/[^\d-]//g;
                   17844:         if (!$filter->{$item}) { $filter->{$item}=-1; }
                   17845:         return &select_form(
                   17846:                             $filter->{$item},
                   17847:                             $item,
                   17848:                             {      '-1' => '',
                   17849:                                 '86400' => &mt('today'),
                   17850:                                '604800' => &mt('last week'),
                   17851:                               '2592000' => &mt('last month'),
                   17852:                               '7776000' => &mt('last three months'),
                   17853:                              '15552000' => &mt('last six months'),
                   17854:                              '31104000' => &mt('last year'),
                   17855:                     'select_form_order' =>
                   17856:                            ['-1','86400','604800','2592000','7776000',
                   17857:                             '15552000','31104000']});
                   17858:     }
                   17859: }
                   17860: 
                   17861: =pod
                   17862: 
                   17863: =item * &js_changer()
                   17864: 
                   17865: Create script tag containing Javascript used to submit course search form
1.1183    raeburn  17866: when course type or domain is changed, and also to hide 'Searching ...' on
                   17867: page load completion for page showing search result.
1.1181    raeburn  17868: 
                   17869: Inputs: None
                   17870: 
1.1183    raeburn  17871: Returns: markup containing updateFilters() and hideSearching() javascript functions. 
1.1181    raeburn  17872: 
                   17873: Side Effects: None
                   17874: 
                   17875: =cut
                   17876: 
                   17877: sub js_changer {
                   17878:     return <<ENDJS;
                   17879: <script type="text/javascript">
                   17880: // <![CDATA[
                   17881: function updateFilters(caller) {
                   17882:     if (typeof(caller) != "undefined") {
                   17883:         document.filterpicker.updater.value = caller.name;
                   17884:     }
                   17885:     document.filterpicker.submit();
                   17886: }
1.1183    raeburn  17887: 
                   17888: function hideSearching() {
                   17889:     if (document.getElementById('searching')) {
                   17890:         document.getElementById('searching').style.display = 'none';
                   17891:     }
                   17892:     return;
                   17893: }
                   17894: 
1.1181    raeburn  17895: // ]]>
                   17896: </script>
                   17897: 
                   17898: ENDJS
                   17899: }
                   17900: 
                   17901: =pod
                   17902: 
1.1182    raeburn  17903: =item * &search_courses()
                   17904: 
                   17905: Process selected filters form course search form and pass to lonnet::courseiddump
                   17906: to retrieve a hash for which keys are courseIDs which match the selected filters.
                   17907: 
                   17908: Inputs:
                   17909: 
                   17910: dom - domain being searched 
                   17911: 
                   17912: type - course type ('Course' or 'Community' or '.' if any).
                   17913: 
                   17914: filter - anonymous hash of criteria and their values
                   17915: 
                   17916: numtitles - for institutional codes - number of categories
                   17917: 
                   17918: cloneruname - optional username of new course owner
                   17919: 
                   17920: clonerudom - optional domain of new course owner
                   17921: 
1.1221    raeburn  17922: domcloner - optional "domcloner" flag; has value=1 if user has ccc priv in domain being filtered by, 
1.1182    raeburn  17923:             (used when DC is using course creation form)
                   17924: 
                   17925: codetitles - reference to array of titles of components in institutional codes (official courses).
                   17926: 
1.1221    raeburn  17927: cc_clone - escaped comma separated list of courses for which course cloner has active CC role
                   17928:            (and so can clone automatically)
                   17929: 
                   17930: reqcrsdom - domain of new course, where search_courses is used to identify potential courses to clone
                   17931: 
                   17932: reqinstcode - institutional code of new course, where search_courses is used to identify potential 
                   17933:               courses to clone 
1.1182    raeburn  17934: 
                   17935: Returns: %courses - hash of courses satisfying search criteria, keys = course IDs, values are corresponding colon-separated escaped description, institutional code, owner and type.
                   17936: 
                   17937: 
                   17938: Side Effects: None
                   17939: 
                   17940: =cut
                   17941: 
                   17942: 
                   17943: sub search_courses {
1.1221    raeburn  17944:     my ($dom,$type,$filter,$numtitles,$cloneruname,$clonerudom,$domcloner,$codetitles,
                   17945:         $cc_clone,$reqcrsdom,$reqinstcode) = @_;
1.1182    raeburn  17946:     my (%courses,%showcourses,$cloner);
                   17947:     if (($filter->{'ownerfilter'} ne '') ||
                   17948:         ($filter->{'ownerdomfilter'} ne '')) {
                   17949:         $filter->{'combownerfilter'} = $filter->{'ownerfilter'}.':'.
                   17950:                                        $filter->{'ownerdomfilter'};
                   17951:     }
                   17952:     foreach my $item ('descriptfilter','coursefilter','combownerfilter') {
                   17953:         if (!$filter->{$item}) {
                   17954:             $filter->{$item}='.';
                   17955:         }
                   17956:     }
                   17957:     my $now = time;
                   17958:     my $timefilter =
                   17959:        ($filter->{'sincefilter'}==-1?1:$now-$filter->{'sincefilter'});
                   17960:     my ($createdbefore,$createdafter);
                   17961:     if (($filter->{'createdfilter'} ne '') && ($filter->{'createdfilter'} !=-1)) {
                   17962:         $createdbefore = $now;
                   17963:         $createdafter = $now-$filter->{'createdfilter'};
                   17964:     }
                   17965:     my ($instcodefilter,$regexpok);
                   17966:     if ($numtitles) {
                   17967:         if ($env{'form.official'} eq 'on') {
                   17968:             $instcodefilter =
                   17969:                 &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
                   17970:             $regexpok = 1;
                   17971:         } elsif ($env{'form.official'} eq 'off') {
                   17972:             $instcodefilter = &Apache::courseclassifier::instcode_search_str($dom,$numtitles,$codetitles);
                   17973:             unless ($instcodefilter eq '') {
                   17974:                 $regexpok = -1;
                   17975:             }
                   17976:         }
                   17977:     } else {
                   17978:         $instcodefilter = $filter->{'instcodefilter'};
                   17979:     }
                   17980:     if ($instcodefilter eq '') { $instcodefilter = '.'; }
                   17981:     if ($type eq '') { $type = '.'; }
                   17982: 
                   17983:     if (($clonerudom ne '') && ($cloneruname ne '')) {
                   17984:         $cloner = $cloneruname.':'.$clonerudom;
                   17985:     }
                   17986:     %courses = &Apache::lonnet::courseiddump($dom,
                   17987:                                              $filter->{'descriptfilter'},
                   17988:                                              $timefilter,
                   17989:                                              $instcodefilter,
                   17990:                                              $filter->{'combownerfilter'},
                   17991:                                              $filter->{'coursefilter'},
                   17992:                                              undef,undef,$type,$regexpok,undef,undef,
1.1221    raeburn  17993:                                              undef,undef,$cloner,$cc_clone,
1.1182    raeburn  17994:                                              $filter->{'cloneableonly'},
                   17995:                                              $createdbefore,$createdafter,undef,
1.1221    raeburn  17996:                                              $domcloner,undef,$reqcrsdom,$reqinstcode);
1.1182    raeburn  17997:     if (($filter->{'personfilter'} ne '') && ($filter->{'persondomfilter'} ne '')) {
                   17998:         my $ccrole;
                   17999:         if ($type eq 'Community') {
                   18000:             $ccrole = 'co';
                   18001:         } else {
                   18002:             $ccrole = 'cc';
                   18003:         }
                   18004:         my %rolehash = &Apache::lonnet::get_my_roles($filter->{'personfilter'},
                   18005:                                                      $filter->{'persondomfilter'},
                   18006:                                                      'userroles',undef,
                   18007:                                                      [$ccrole,'in','ad','ep','ta','cr'],
                   18008:                                                      $dom);
                   18009:         foreach my $role (keys(%rolehash)) {
                   18010:             my ($cnum,$cdom,$courserole) = split(':',$role);
                   18011:             my $cid = $cdom.'_'.$cnum;
                   18012:             if (exists($courses{$cid})) {
                   18013:                 if (ref($courses{$cid}) eq 'HASH') {
                   18014:                     if (ref($courses{$cid}{roles}) eq 'ARRAY') {
                   18015:                         if (!grep(/^\Q$courserole\E$/,@{$courses{$cid}{roles}})) {
1.1263    raeburn  18016:                             push(@{$courses{$cid}{roles}},$courserole);
1.1182    raeburn  18017:                         }
                   18018:                     } else {
                   18019:                         $courses{$cid}{roles} = [$courserole];
                   18020:                     }
                   18021:                     $showcourses{$cid} = $courses{$cid};
                   18022:                 }
                   18023:             }
                   18024:         }
                   18025:         %courses = %showcourses;
                   18026:     }
                   18027:     return %courses;
                   18028: }
                   18029: 
                   18030: =pod
                   18031: 
1.1181    raeburn  18032: =back
                   18033: 
1.1207    raeburn  18034: =head1 Routines for version requirements for current course.
                   18035: 
                   18036: =over 4
                   18037: 
                   18038: =item * &check_release_required()
                   18039: 
                   18040: Compares required LON-CAPA version with version on server, and
                   18041: if required version is newer looks for a server with the required version.
                   18042: 
                   18043: Looks first at servers in user's owen domain; if none suitable, looks at
                   18044: servers in course's domain are permitted to host sessions for user's domain.
                   18045: 
                   18046: Inputs:
                   18047: 
                   18048: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
                   18049: 
                   18050: $courseid - Course ID of current course
                   18051: 
                   18052: $rolecode - User's current role in course (for switchserver query string).
                   18053: 
                   18054: $required - LON-CAPA version needed by course (format: Major.Minor).
                   18055: 
                   18056: 
                   18057: Returns:
                   18058: 
                   18059: $switchserver - query string tp append to /adm/switchserver call (if 
                   18060:                 current server's LON-CAPA version is too old. 
                   18061: 
                   18062: $warning - Message is displayed if no suitable server could be found.
                   18063: 
                   18064: =cut
                   18065: 
                   18066: sub check_release_required {
                   18067:     my ($loncaparev,$courseid,$rolecode,$required) = @_;
                   18068:     my ($switchserver,$warning);
                   18069:     if ($required ne '') {
                   18070:         my ($reqdmajor,$reqdminor) = ($required =~ /^(\d+)\.(\d+)$/);
                   18071:         my ($major,$minor) = ($loncaparev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                   18072:         if ($reqdmajor ne '' && $reqdminor ne '') {
                   18073:             my $otherserver;
                   18074:             if (($major eq '' && $minor eq '') ||
                   18075:                 (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)))) {
                   18076:                 my ($userdomserver) = &Apache::lonnet::choose_server($env{'user.domain'},undef,$required,1);
                   18077:                 my $switchlcrev =
                   18078:                     &Apache::lonnet::get_server_loncaparev($env{'user.domain'},
                   18079:                                                            $userdomserver);
                   18080:                 my ($swmajor,$swminor) = ($switchlcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
                   18081:                 if (($swmajor eq '' && $swminor eq '') || ($reqdmajor > $swmajor) ||
                   18082:                     (($reqdmajor == $swmajor) && ($reqdminor > $swminor))) {
                   18083:                     my $cdom = $env{'course.'.$courseid.'.domain'};
                   18084:                     if ($cdom ne $env{'user.domain'}) {
                   18085:                         my ($coursedomserver,$coursehostname) = &Apache::lonnet::choose_server($cdom,undef,$required,1);
                   18086:                         my $serverhomeID = &Apache::lonnet::get_server_homeID($coursehostname);
                   18087:                         my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
                   18088:                         my %defdomdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
                   18089:                         my %udomdefaults = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
                   18090:                         my $remoterev = &Apache::lonnet::get_server_loncaparev($serverhomedom,$coursedomserver);
                   18091:                         my $canhost =
                   18092:                             &Apache::lonnet::can_host_session($env{'user.domain'},
                   18093:                                                               $coursedomserver,
                   18094:                                                               $remoterev,
                   18095:                                                               $udomdefaults{'remotesessions'},
                   18096:                                                               $defdomdefaults{'hostedsessions'});
                   18097: 
                   18098:                         if ($canhost) {
                   18099:                             $otherserver = $coursedomserver;
                   18100:                         } else {
                   18101:                             $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.");
                   18102:                         }
                   18103:                     } else {
                   18104:                         $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).");
                   18105:                     }
                   18106:                 } else {
                   18107:                     $otherserver = $userdomserver;
                   18108:                 }
                   18109:             }
                   18110:             if ($otherserver ne '') {
                   18111:                 $switchserver = 'otherserver='.$otherserver.'&amp;role='.$rolecode;
                   18112:             }
                   18113:         }
                   18114:     }
                   18115:     return ($switchserver,$warning);
                   18116: }
                   18117: 
                   18118: =pod
                   18119: 
                   18120: =item * &check_release_result()
                   18121: 
                   18122: Inputs:
                   18123: 
                   18124: $switchwarning - Warning message if no suitable server found to host session.
                   18125: 
                   18126: $switchserver - query string to append to /adm/switchserver containing lonHostID
                   18127:                 and current role.
                   18128: 
                   18129: Returns: HTML to display with information about requirement to switch server.
                   18130:          Either displaying warning with link to Roles/Courses screen or
                   18131:          display link to switchserver.
                   18132: 
1.1181    raeburn  18133: =cut
                   18134: 
1.1207    raeburn  18135: sub check_release_result {
                   18136:     my ($switchwarning,$switchserver) = @_;
                   18137:     my $output = &start_page('Selected course unavailable on this server').
                   18138:                  '<p class="LC_warning">';
                   18139:     if ($switchwarning) {
                   18140:         $output .= $switchwarning.'<br /><a href="/adm/roles">';
                   18141:         if (&show_course()) {
                   18142:             $output .= &mt('Display courses');
                   18143:         } else {
                   18144:             $output .= &mt('Display roles');
                   18145:         }
                   18146:         $output .= '</a>';
                   18147:     } elsif ($switchserver) {
                   18148:         $output .= &mt('This course requires a newer version of LON-CAPA than is installed on this server.').
                   18149:                    '<br />'.
                   18150:                    '<a href="/adm/switchserver?'.$switchserver.'">'.
                   18151:                    &mt('Switch Server').
                   18152:                    '</a>';
                   18153:     }
                   18154:     $output .= '</p>'.&end_page();
                   18155:     return $output;
                   18156: }
                   18157: 
                   18158: =pod
                   18159: 
                   18160: =item * &needs_coursereinit()
                   18161: 
                   18162: Determine if course contents stored for user's session needs to be
                   18163: refreshed, because content has changed since "Big Hash" last tied.
                   18164: 
                   18165: Check for change is made if time last checked is more than 10 minutes ago
                   18166: (by default).
                   18167: 
                   18168: Inputs:
                   18169: 
                   18170: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
                   18171: 
                   18172: $interval (optional) - Time which may elapse (in s) between last check for content
                   18173:                        change in current course. (default: 600 s).  
                   18174: 
                   18175: Returns: an array; first element is:
                   18176: 
                   18177: =over 4
                   18178: 
                   18179: 'switch' - if content updates mean user's session
                   18180:            needs to be switched to a server running a newer LON-CAPA version
                   18181:  
                   18182: 'update' - if course session needs to be refreshed (i.e., Big Hash needs to be reloaded)
                   18183:            on current server hosting user's session                
                   18184: 
                   18185: ''       - if no action required.
                   18186: 
                   18187: =back
                   18188: 
                   18189: If first item element is 'switch':
                   18190: 
                   18191: second item is $switchwarning - Warning message if no suitable server found to host session. 
                   18192: 
                   18193: third item is $switchserver - query string to append to /adm/switchserver containing lonHostID
                   18194:                               and current role. 
                   18195: 
                   18196: otherwise: no other elements returned.
                   18197: 
                   18198: =back
                   18199: 
                   18200: =cut
                   18201: 
                   18202: sub needs_coursereinit {
                   18203:     my ($loncaparev,$interval) = @_;
                   18204:     return() unless ($env{'request.course.id'} && $env{'request.course.tied'});
                   18205:     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   18206:     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                   18207:     my $now = time;
                   18208:     if ($interval eq '') {
                   18209:         $interval = 600;
                   18210:     }
                   18211:     if (($now-$env{'request.course.timechecked'})>$interval) {
1.1282    raeburn  18212:         &Apache::lonnet::appenv({'request.course.timechecked'=>$now});
1.1372    raeburn  18213:         my $blocked = &blocking_status('reinit',undef,$cnum,$cdom,undef,1);
1.1282    raeburn  18214:         if ($blocked) {
                   18215:             return ();
                   18216:         }
1.1207    raeburn  18217:         my $lastchange = &Apache::lonnet::get_coursechange($cdom,$cnum);
                   18218:         if ($lastchange > $env{'request.course.tied'}) {
                   18219:             my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
                   18220:             if ($curr_reqd_hash{'internal.releaserequired'} ne '') {
                   18221:                 my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'};
                   18222:                 if ($curr_reqd_hash{'internal.releaserequired'} ne $required) {
                   18223:                     &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' =>
                   18224:                                              $curr_reqd_hash{'internal.releaserequired'}});
                   18225:                     my ($switchserver,$switchwarning) =
                   18226:                         &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'},
                   18227:                                                 $curr_reqd_hash{'internal.releaserequired'});
                   18228:                     if ($switchwarning ne '' || $switchserver ne '') {
                   18229:                         return ('switch',$switchwarning,$switchserver);
                   18230:                     }
                   18231:                 }
                   18232:             }
                   18233:             return ('update');
                   18234:         }
                   18235:     }
                   18236:     return ();
                   18237: }
1.1181    raeburn  18238: 
1.1083    raeburn  18239: sub update_content_constraints {
1.1326    raeburn  18240:     my ($cdom,$cnum,$chome,$cid,$keeporder) = @_;
1.1083    raeburn  18241:     my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
                   18242:     my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
1.1307    raeburn  18243:     my (%checkresponsetypes,%checkcrsrestypes);
1.1083    raeburn  18244:     foreach my $key (keys(%Apache::lonnet::needsrelease)) {
1.1236    raeburn  18245:         my ($item,$name,$value) = split(/:/,$key);
1.1083    raeburn  18246:         if ($item eq 'resourcetag') {
                   18247:             if ($name eq 'responsetype') {
                   18248:                 $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
                   18249:             }
1.1307    raeburn  18250:         } elsif ($item eq 'course') {
                   18251:             if ($name eq 'courserestype') {
                   18252:                 $checkcrsrestypes{$value} = $Apache::lonnet::needsrelease{$key};
                   18253:             }
1.1083    raeburn  18254:         }
                   18255:     }
                   18256:     my $navmap = Apache::lonnavmaps::navmap->new();
                   18257:     if (defined($navmap)) {
1.1307    raeburn  18258:         my (%allresponses,%allcrsrestypes);
                   18259:         foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() || $_[0]->is_tool() },1,0)) {
                   18260:             if ($res->is_tool()) {
                   18261:                 if ($allcrsrestypes{'exttool'}) {
                   18262:                     $allcrsrestypes{'exttool'} ++;
                   18263:                 } else {
                   18264:                     $allcrsrestypes{'exttool'} = 1;
                   18265:                 }
                   18266:                 next;
                   18267:             }
1.1083    raeburn  18268:             my %responses = $res->responseTypes();
                   18269:             foreach my $key (keys(%responses)) {
                   18270:                 next unless(exists($checkresponsetypes{$key}));
                   18271:                 $allresponses{$key} += $responses{$key};
                   18272:             }
                   18273:         }
                   18274:         foreach my $key (keys(%allresponses)) {
                   18275:             my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
                   18276:             if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
                   18277:                 ($reqdmajor,$reqdminor) = ($major,$minor);
                   18278:             }
                   18279:         }
1.1307    raeburn  18280:         foreach my $key (keys(%allcrsrestypes)) {
1.1308    raeburn  18281:             my ($major,$minor) = split(/\./,$checkcrsrestypes{$key});
1.1307    raeburn  18282:             if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
                   18283:                 ($reqdmajor,$reqdminor) = ($major,$minor);
                   18284:             }
                   18285:         }
1.1083    raeburn  18286:         undef($navmap);
                   18287:     }
1.1326    raeburn  18288:     my (@resources,@order,@resparms,@zombies);
                   18289:     if ($keeporder) {
                   18290:         use LONCAPA::map;
                   18291:         @resources = @LONCAPA::map::resources;
                   18292:         @order = @LONCAPA::map::order;
                   18293:         @resparms = @LONCAPA::map::resparms;
                   18294:         @zombies = @LONCAPA::map::zombies;
                   18295:     }
1.1308    raeburn  18296:     my $suppmap = 'supplemental.sequence';
                   18297:     my ($suppcount,$supptools,$errors) = (0,0,0);
                   18298:     ($suppcount,$supptools,$errors) = &recurse_supplemental($cnum,$cdom,$suppmap,
                   18299:                                                             $suppcount,$supptools,$errors);
1.1326    raeburn  18300:     if ($keeporder) {
                   18301:         @LONCAPA::map::resources = @resources;
                   18302:         @LONCAPA::map::order = @order;
                   18303:         @LONCAPA::map::resparms = @resparms;
                   18304:         @LONCAPA::map::zombies = @zombies;
                   18305:     }
1.1308    raeburn  18306:     if ($supptools) {
                   18307:         my ($major,$minor) = split(/\./,$checkcrsrestypes{'exttool'});
                   18308:         if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
                   18309:             ($reqdmajor,$reqdminor) = ($major,$minor);
                   18310:         }
                   18311:     }
1.1083    raeburn  18312:     unless (($reqdmajor eq '') && ($reqdminor eq '')) {
                   18313:         &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
                   18314:     }
                   18315:     return;
                   18316: }
                   18317: 
1.1110    raeburn  18318: sub allmaps_incourse {
                   18319:     my ($cdom,$cnum,$chome,$cid) = @_;
                   18320:     if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
                   18321:         $cid = $env{'request.course.id'};
                   18322:         $cdom = $env{'course.'.$cid.'.domain'};
                   18323:         $cnum = $env{'course.'.$cid.'.num'};
                   18324:         $chome = $env{'course.'.$cid.'.home'};
                   18325:     }
                   18326:     my %allmaps = ();
                   18327:     my $lastchange =
                   18328:         &Apache::lonnet::get_coursechange($cdom,$cnum);
                   18329:     if ($lastchange > $env{'request.course.tied'}) {
                   18330:         my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
                   18331:         unless ($ferr) {
1.1326    raeburn  18332:             &update_content_constraints($cdom,$cnum,$chome,$cid,1);
1.1110    raeburn  18333:         }
                   18334:     }
                   18335:     my $navmap = Apache::lonnavmaps::navmap->new();
                   18336:     if (defined($navmap)) {
                   18337:         foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) {
                   18338:             $allmaps{$res->src()} = 1;
                   18339:         }
                   18340:     }
                   18341:     return \%allmaps;
                   18342: }
                   18343: 
1.1083    raeburn  18344: sub parse_supplemental_title {
                   18345:     my ($title) = @_;
                   18346: 
                   18347:     my ($foldertitle,$renametitle);
                   18348:     if ($title =~ /&amp;&amp;&amp;/) {
                   18349:         $title = &HTML::Entites::decode($title);
                   18350:     }
                   18351:     if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
                   18352:         $renametitle=$4;
                   18353:         my ($time,$uname,$udom) = ($1,$2,$3);
                   18354:         $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
                   18355:         my $name =  &plainname($uname,$udom);
                   18356:         $name = &HTML::Entities::encode($name,'"<>&\'');
                   18357:         $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
                   18358:         $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.
                   18359:             $name.': <br />'.$foldertitle;
                   18360:     }
                   18361:     if (wantarray) {
                   18362:         return ($title,$foldertitle,$renametitle);
                   18363:     }
                   18364:     return $title;
                   18365: }
                   18366: 
1.1143    raeburn  18367: sub recurse_supplemental {
1.1308    raeburn  18368:     my ($cnum,$cdom,$suppmap,$numfiles,$numexttools,$errors) = @_;
1.1143    raeburn  18369:     if ($suppmap) {
                   18370:         my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
                   18371:         if ($fatal) {
                   18372:             $errors ++;
                   18373:         } else {
                   18374:             if ($#LONCAPA::map::resources > 0) {
                   18375:                 foreach my $res (@LONCAPA::map::resources) {
                   18376:                     my ($title,$src,$ext,$type,$status)=split(/\:/,$res);
                   18377:                     if (($src ne '') && ($status eq 'res')) {
1.1146    raeburn  18378:                         if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
1.1308    raeburn  18379:                             ($numfiles,$numexttools,$errors) = &recurse_supplemental($cnum,$cdom,$1,
                   18380:                                                                    $numfiles,$numexttools,$errors);
1.1143    raeburn  18381:                         } else {
1.1308    raeburn  18382:                             if ($src =~ m{^/adm/$cdom/$cnum/\d+/ext\.tool$}) {
                   18383:                                 $numexttools ++;
                   18384:                             }
1.1143    raeburn  18385:                             $numfiles ++;
                   18386:                         }
                   18387:                     }
                   18388:                 }
                   18389:             }
                   18390:         }
                   18391:     }
1.1308    raeburn  18392:     return ($numfiles,$numexttools,$errors);
1.1143    raeburn  18393: }
                   18394: 
1.1101    raeburn  18395: sub symb_to_docspath {
1.1267    raeburn  18396:     my ($symb,$navmapref) = @_;
                   18397:     return unless ($symb && ref($navmapref));
1.1101    raeburn  18398:     my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
                   18399:     if ($resurl=~/\.(sequence|page)$/) {
                   18400:         $mapurl=$resurl;
                   18401:     } elsif ($resurl eq 'adm/navmaps') {
                   18402:         $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
                   18403:     }
                   18404:     my $mapresobj;
1.1267    raeburn  18405:     unless (ref($$navmapref)) {
                   18406:         $$navmapref = Apache::lonnavmaps::navmap->new();
                   18407:     }
                   18408:     if (ref($$navmapref)) {
                   18409:         $mapresobj = $$navmapref->getResourceByUrl($mapurl);
1.1101    raeburn  18410:     }
                   18411:     $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
                   18412:     my $type=$2;
                   18413:     my $path;
                   18414:     if (ref($mapresobj)) {
                   18415:         my $pcslist = $mapresobj->map_hierarchy();
                   18416:         if ($pcslist ne '') {
                   18417:             foreach my $pc (split(/,/,$pcslist)) {
                   18418:                 next if ($pc <= 1);
1.1267    raeburn  18419:                 my $res = $$navmapref->getByMapPc($pc);
1.1101    raeburn  18420:                 if (ref($res)) {
                   18421:                     my $thisurl = $res->src();
                   18422:                     $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
                   18423:                     my $thistitle = $res->title();
                   18424:                     $path .= '&'.
                   18425:                              &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
1.1146    raeburn  18426:                              &escape($thistitle).
1.1101    raeburn  18427:                              ':'.$res->randompick().
                   18428:                              ':'.$res->randomout().
                   18429:                              ':'.$res->encrypted().
                   18430:                              ':'.$res->randomorder().
                   18431:                              ':'.$res->is_page();
                   18432:                 }
                   18433:             }
                   18434:         }
                   18435:         $path =~ s/^\&//;
                   18436:         my $maptitle = $mapresobj->title();
                   18437:         if ($mapurl eq 'default') {
1.1129    raeburn  18438:             $maptitle = 'Main Content';
1.1101    raeburn  18439:         }
                   18440:         $path .= (($path ne '')? '&' : '').
                   18441:                  &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146    raeburn  18442:                  &escape($maptitle).
1.1101    raeburn  18443:                  ':'.$mapresobj->randompick().
                   18444:                  ':'.$mapresobj->randomout().
                   18445:                  ':'.$mapresobj->encrypted().
                   18446:                  ':'.$mapresobj->randomorder().
                   18447:                  ':'.$mapresobj->is_page();
                   18448:     } else {
                   18449:         my $maptitle = &Apache::lonnet::gettitle($mapurl);
                   18450:         my $ispage = (($type eq 'page')? 1 : '');
                   18451:         if ($mapurl eq 'default') {
1.1129    raeburn  18452:             $maptitle = 'Main Content';
1.1101    raeburn  18453:         }
                   18454:         $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146    raeburn  18455:                 &escape($maptitle).':::::'.$ispage;
1.1101    raeburn  18456:     }
                   18457:     unless ($mapurl eq 'default') {
                   18458:         $path = 'default&'.
1.1146    raeburn  18459:                 &escape('Main Content').
1.1101    raeburn  18460:                 ':::::&'.$path;
                   18461:     }
                   18462:     return $path;
                   18463: }
                   18464: 
1.1094    raeburn  18465: sub captcha_display {
1.1327    raeburn  18466:     my ($context,$lonhost,$defdom) = @_;
1.1094    raeburn  18467:     my ($output,$error);
1.1234    raeburn  18468:     my ($captcha,$pubkey,$privkey,$version) = 
1.1327    raeburn  18469:         &get_captcha_config($context,$lonhost,$defdom);
1.1095    raeburn  18470:     if ($captcha eq 'original') {
1.1094    raeburn  18471:         $output = &create_captcha();
                   18472:         unless ($output) {
1.1172    raeburn  18473:             $error = 'captcha';
1.1094    raeburn  18474:         }
                   18475:     } elsif ($captcha eq 'recaptcha') {
1.1234    raeburn  18476:         $output = &create_recaptcha($pubkey,$version);
1.1094    raeburn  18477:         unless ($output) {
1.1172    raeburn  18478:             $error = 'recaptcha';
1.1094    raeburn  18479:         }
                   18480:     }
1.1234    raeburn  18481:     return ($output,$error,$captcha,$version);
1.1094    raeburn  18482: }
                   18483: 
                   18484: sub captcha_response {
1.1327    raeburn  18485:     my ($context,$lonhost,$defdom) = @_;
1.1094    raeburn  18486:     my ($captcha_chk,$captcha_error);
1.1327    raeburn  18487:     my ($captcha,$pubkey,$privkey,$version) = &get_captcha_config($context,$lonhost,$defdom);
1.1095    raeburn  18488:     if ($captcha eq 'original') {
1.1094    raeburn  18489:         ($captcha_chk,$captcha_error) = &check_captcha();
                   18490:     } elsif ($captcha eq 'recaptcha') {
1.1234    raeburn  18491:         $captcha_chk = &check_recaptcha($privkey,$version);
1.1094    raeburn  18492:     } else {
                   18493:         $captcha_chk = 1;
                   18494:     }
                   18495:     return ($captcha_chk,$captcha_error);
                   18496: }
                   18497: 
                   18498: sub get_captcha_config {
1.1327    raeburn  18499:     my ($context,$lonhost,$dom_in_effect) = @_;
1.1234    raeburn  18500:     my ($captcha,$pubkey,$privkey,$version,$hashtocheck);
1.1094    raeburn  18501:     my $hostname = &Apache::lonnet::hostname($lonhost);
                   18502:     my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
                   18503:     my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
1.1095    raeburn  18504:     if ($context eq 'usercreation') {
                   18505:         my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
                   18506:         if (ref($domconfig{$context}) eq 'HASH') {
                   18507:             $hashtocheck = $domconfig{$context}{'cancreate'};
                   18508:             if (ref($hashtocheck) eq 'HASH') {
                   18509:                 if ($hashtocheck->{'captcha'} eq 'recaptcha') {
                   18510:                     if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
                   18511:                         $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
                   18512:                         $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
                   18513:                     }
                   18514:                     if ($privkey && $pubkey) {
                   18515:                         $captcha = 'recaptcha';
1.1234    raeburn  18516:                         $version = $hashtocheck->{'recaptchaversion'};
                   18517:                         if ($version ne '2') {
                   18518:                             $version = 1;
                   18519:                         }
1.1095    raeburn  18520:                     } else {
                   18521:                         $captcha = 'original';
                   18522:                     }
                   18523:                 } elsif ($hashtocheck->{'captcha'} ne 'notused') {
                   18524:                     $captcha = 'original';
                   18525:                 }
1.1094    raeburn  18526:             }
1.1095    raeburn  18527:         } else {
                   18528:             $captcha = 'captcha';
                   18529:         }
                   18530:     } elsif ($context eq 'login') {
                   18531:         my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
                   18532:         if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
                   18533:             $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
                   18534:             $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
1.1094    raeburn  18535:             if ($privkey && $pubkey) {
                   18536:                 $captcha = 'recaptcha';
1.1234    raeburn  18537:                 $version = $domconfhash{$serverhomedom.'.login.recaptchaversion'};
                   18538:                 if ($version ne '2') {
                   18539:                     $version = 1; 
                   18540:                 }
1.1095    raeburn  18541:             } else {
                   18542:                 $captcha = 'original';
1.1094    raeburn  18543:             }
1.1095    raeburn  18544:         } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
                   18545:             $captcha = 'original';
1.1094    raeburn  18546:         }
1.1327    raeburn  18547:     } elsif ($context eq 'passwords') {
                   18548:         if ($dom_in_effect) {
                   18549:             my %passwdconf = &Apache::lonnet::get_passwdconf($dom_in_effect);
                   18550:             if ($passwdconf{'captcha'} eq 'recaptcha') {
                   18551:                 if (ref($passwdconf{'recaptchakeys'}) eq 'HASH') {
                   18552:                     $pubkey = $passwdconf{'recaptchakeys'}{'public'};
                   18553:                     $privkey = $passwdconf{'recaptchakeys'}{'private'};
                   18554:                 }
                   18555:                 if ($privkey && $pubkey) {
                   18556:                     $captcha = 'recaptcha';
                   18557:                     $version = $passwdconf{'recaptchaversion'};
                   18558:                     if ($version ne '2') {
                   18559:                         $version = 1;
                   18560:                     }
                   18561:                 } else {
                   18562:                     $captcha = 'original';
                   18563:                 }
                   18564:             } elsif ($passwdconf{'captcha'} ne 'notused') {
                   18565:                 $captcha = 'original';
                   18566:             }
                   18567:         }
                   18568:     } 
1.1234    raeburn  18569:     return ($captcha,$pubkey,$privkey,$version);
1.1094    raeburn  18570: }
                   18571: 
                   18572: sub create_captcha {
                   18573:     my %captcha_params = &captcha_settings();
                   18574:     my ($output,$maxtries,$tries) = ('',10,0);
                   18575:     while ($tries < $maxtries) {
                   18576:         $tries ++;
                   18577:         my $captcha = Authen::Captcha->new (
                   18578:                                            output_folder => $captcha_params{'output_dir'},
                   18579:                                            data_folder   => $captcha_params{'db_dir'},
                   18580:                                           );
                   18581:         my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
                   18582: 
                   18583:         if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
                   18584:             $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
1.1367    raeburn  18585:                       '<span class="LC_nobreak">'.
1.1094    raeburn  18586:                       &mt('Type in the letters/numbers shown below').'&nbsp;'.
1.1176    raeburn  18587:                       '<input type="text" size="5" name="code" value="" autocomplete="off" />'.
1.1367    raeburn  18588:                       '</span><br />'.
1.1176    raeburn  18589:                       '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
1.1094    raeburn  18590:             last;
                   18591:         }
                   18592:     }
1.1323    raeburn  18593:     if ($output eq '') {
                   18594:         &Apache::lonnet::logthis("Failed to create Captcha code after $tries attempts.");
                   18595:     }
1.1094    raeburn  18596:     return $output;
                   18597: }
                   18598: 
                   18599: sub captcha_settings {
                   18600:     my %captcha_params = (
                   18601:                            output_dir     => $Apache::lonnet::perlvar{'lonCaptchaDir'},
                   18602:                            www_output_dir => "/captchaspool",
                   18603:                            db_dir         => $Apache::lonnet::perlvar{'lonCaptchaDb'},
                   18604:                            numchars       => '5',
                   18605:                          );
                   18606:     return %captcha_params;
                   18607: }
                   18608: 
                   18609: sub check_captcha {
                   18610:     my ($captcha_chk,$captcha_error);
                   18611:     my $code = $env{'form.code'};
                   18612:     my $md5sum = $env{'form.crypt'};
                   18613:     my %captcha_params = &captcha_settings();
                   18614:     my $captcha = Authen::Captcha->new(
                   18615:                       output_folder => $captcha_params{'output_dir'},
                   18616:                       data_folder   => $captcha_params{'db_dir'},
                   18617:                   );
1.1109    raeburn  18618:     $captcha_chk = $captcha->check_code($code,$md5sum);
1.1094    raeburn  18619:     my %captcha_hash = (
                   18620:                         0       => 'Code not checked (file error)',
                   18621:                        -1      => 'Failed: code expired',
                   18622:                        -2      => 'Failed: invalid code (not in database)',
                   18623:                        -3      => 'Failed: invalid code (code does not match crypt)',
                   18624:     );
                   18625:     if ($captcha_chk != 1) {
                   18626:         $captcha_error = $captcha_hash{$captcha_chk}
                   18627:     }
                   18628:     return ($captcha_chk,$captcha_error);
                   18629: }
                   18630: 
                   18631: sub create_recaptcha {
1.1234    raeburn  18632:     my ($pubkey,$version) = @_;
                   18633:     if ($version >= 2) {
1.1367    raeburn  18634:         return '<div class="g-recaptcha" data-sitekey="'.$pubkey.'"></div>'.
                   18635:                '<div style="padding:0;clear:both;margin:0;border:0"></div>';
1.1234    raeburn  18636:     } else {
                   18637:         my $use_ssl;
                   18638:         if ($ENV{'SERVER_PORT'} == 443) {
                   18639:             $use_ssl = 1;
                   18640:         }
                   18641:         my $captcha = Captcha::reCAPTCHA->new;
                   18642:         return $captcha->get_options_setter({theme => 'white'})."\n".
                   18643:                $captcha->get_html($pubkey,undef,$use_ssl).
                   18644:                &mt('If the text is hard to read, [_1] will replace them.',
                   18645:                    '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
                   18646:                '<br /><br />';
                   18647:     }
1.1094    raeburn  18648: }
                   18649: 
                   18650: sub check_recaptcha {
1.1234    raeburn  18651:     my ($privkey,$version) = @_;
1.1094    raeburn  18652:     my $captcha_chk;
1.1350    raeburn  18653:     my $ip = &Apache::lonnet::get_requestor_ip();
1.1234    raeburn  18654:     if ($version >= 2) {
                   18655:         my %info = (
                   18656:                      secret   => $privkey, 
                   18657:                      response => $env{'form.g-recaptcha-response'},
1.1350    raeburn  18658:                      remoteip => $ip,
1.1234    raeburn  18659:                    );
1.1280    raeburn  18660:         my $request=new HTTP::Request('POST','https://www.google.com/recaptcha/api/siteverify');
                   18661:         $request->content(join('&',map {
                   18662:                          my $name = escape($_);
                   18663:                          "$name=" . ( ref($info{$_}) eq 'ARRAY'
                   18664:                          ? join("&$name=", map {escape($_) } @{$info{$_}})
                   18665:                          : &escape($info{$_}) );
                   18666:         } keys(%info)));
                   18667:         my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',10,1);
1.1234    raeburn  18668:         if ($response->is_success)  {
                   18669:             my $data = JSON::DWIW->from_json($response->decoded_content);
                   18670:             if (ref($data) eq 'HASH') {
                   18671:                 if ($data->{'success'}) {
                   18672:                     $captcha_chk = 1;
                   18673:                 }
                   18674:             }
                   18675:         }
                   18676:     } else {
                   18677:         my $captcha = Captcha::reCAPTCHA->new;
                   18678:         my $captcha_result =
                   18679:             $captcha->check_answer(
                   18680:                                     $privkey,
1.1350    raeburn  18681:                                     $ip,
1.1234    raeburn  18682:                                     $env{'form.recaptcha_challenge_field'},
                   18683:                                     $env{'form.recaptcha_response_field'},
                   18684:                                   );
                   18685:         if ($captcha_result->{is_valid}) {
                   18686:             $captcha_chk = 1;
                   18687:         }
1.1094    raeburn  18688:     }
                   18689:     return $captcha_chk;
                   18690: }
                   18691: 
1.1174    raeburn  18692: sub emailusername_info {
1.1244    raeburn  18693:     my @fields = ('firstname','lastname','institution','web','location','officialemail','id');
1.1174    raeburn  18694:     my %titles = &Apache::lonlocal::texthash (
                   18695:                      lastname      => 'Last Name',
                   18696:                      firstname     => 'First Name',
                   18697:                      institution   => 'School/college/university',
                   18698:                      location      => "School's city, state/province, country",
                   18699:                      web           => "School's web address",
                   18700:                      officialemail => 'E-mail address at institution (if different)',
1.1244    raeburn  18701:                      id            => 'Student/Employee ID',
1.1174    raeburn  18702:                  );
                   18703:     return (\@fields,\%titles);
                   18704: }
                   18705: 
1.1161    raeburn  18706: sub cleanup_html {
                   18707:     my ($incoming) = @_;
                   18708:     my $outgoing;
                   18709:     if ($incoming ne '') {
                   18710:         $outgoing = $incoming;
                   18711:         $outgoing =~ s/;/&#059;/g;
                   18712:         $outgoing =~ s/\#/&#035;/g;
                   18713:         $outgoing =~ s/\&/&#038;/g;
                   18714:         $outgoing =~ s/</&#060;/g;
                   18715:         $outgoing =~ s/>/&#062;/g;
                   18716:         $outgoing =~ s/\(/&#040/g;
                   18717:         $outgoing =~ s/\)/&#041;/g;
                   18718:         $outgoing =~ s/"/&#034;/g;
                   18719:         $outgoing =~ s/'/&#039;/g;
                   18720:         $outgoing =~ s/\$/&#036;/g;
                   18721:         $outgoing =~ s{/}{&#047;}g;
                   18722:         $outgoing =~ s/=/&#061;/g;
                   18723:         $outgoing =~ s/\\/&#092;/g
                   18724:     }
                   18725:     return $outgoing;
                   18726: }
                   18727: 
1.1190    musolffc 18728: # Checks for critical messages and returns a redirect url if one exists.
                   18729: # $interval indicates how often to check for messages.
1.1282    raeburn  18730: # $context is the calling context -- roles, grades, contents, menu or flip. 
1.1190    musolffc 18731: sub critical_redirect {
1.1282    raeburn  18732:     my ($interval,$context) = @_;
1.1356    raeburn  18733:     unless (($env{'user.domain'} ne '') && ($env{'user.name'} ne '')) {
                   18734:         return ();
                   18735:     }
1.1190    musolffc 18736:     if ((time-$env{'user.criticalcheck.time'})>$interval) {
1.1282    raeburn  18737:         if (($env{'request.course.id'}) && (($context eq 'flip') || ($context eq 'contents'))) {
                   18738:             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   18739:             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1372    raeburn  18740:             my $blocked = &blocking_status('alert',undef,$cnum,$cdom,undef,1);
1.1282    raeburn  18741:             if ($blocked) {
                   18742:                 my $checkrole = "cm./$cdom/$cnum";
                   18743:                 if ($env{'request.course.sec'} ne '') {
                   18744:                     $checkrole .= "/$env{'request.course.sec'}";
                   18745:                 }
                   18746:                 unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
                   18747:                         ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
                   18748:                     return;
                   18749:                 }
                   18750:             }
                   18751:         }
1.1190    musolffc 18752:         my @what=&Apache::lonnet::dump('critical', $env{'user.domain'}, 
                   18753:                                         $env{'user.name'});
                   18754:         &Apache::lonnet::appenv({'user.criticalcheck.time'=>time});
1.1191    raeburn  18755:         my $redirecturl;
1.1190    musolffc 18756:         if ($what[0]) {
1.1356    raeburn  18757: 	    if (($what[0] ne 'con_lost') && ($what[0] ne 'no_such_host') && ($what[0]!~/^error\:/)) {
1.1190    musolffc 18758: 	        $redirecturl='/adm/email?critical=display';
1.1191    raeburn  18759: 	        my $url=&Apache::lonnet::absolute_url().$redirecturl;
                   18760:                 return (1, $url);
1.1190    musolffc 18761:             }
1.1191    raeburn  18762:         }
                   18763:     } 
                   18764:     return ();
1.1190    musolffc 18765: }
                   18766: 
1.1174    raeburn  18767: # Use:
                   18768: #   my $answer=reply("encrypt:passwd:$udom:$uname:$upass",$tryserver);
                   18769: #
                   18770: ##################################################
                   18771: #          password associated functions         #
                   18772: ##################################################
                   18773: sub des_keys {
                   18774:     # Make a new key for DES encryption.
                   18775:     # Each key has two parts which are returned separately.
                   18776:     # Please note:  Each key must be passed through the &hex function
                   18777:     # before it is output to the web browser.  The hex versions cannot
                   18778:     # be used to decrypt.
                   18779:     my @hexstr=('0','1','2','3','4','5','6','7',
                   18780:                 '8','9','a','b','c','d','e','f');
                   18781:     my $lkey='';
                   18782:     for (0..7) {
                   18783:         $lkey.=$hexstr[rand(15)];
                   18784:     }
                   18785:     my $ukey='';
                   18786:     for (0..7) {
                   18787:         $ukey.=$hexstr[rand(15)];
                   18788:     }
                   18789:     return ($lkey,$ukey);
                   18790: }
                   18791: 
                   18792: sub des_decrypt {
                   18793:     my ($key,$cyphertext) = @_;
                   18794:     my $keybin=pack("H16",$key);
                   18795:     my $cypher;
                   18796:     if ($Crypt::DES::VERSION>=2.03) {
                   18797:         $cypher=new Crypt::DES $keybin;
                   18798:     } else {
                   18799:         $cypher=new DES $keybin;
                   18800:     }
1.1233    raeburn  18801:     my $plaintext='';
                   18802:     my $cypherlength = length($cyphertext);
                   18803:     my $numchunks = int($cypherlength/32);
                   18804:     for (my $j=0; $j<$numchunks; $j++) {
                   18805:         my $start = $j*32;
                   18806:         my $cypherblock = substr($cyphertext,$start,32);
                   18807:         my $chunk =
                   18808:             $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,0,16))));
                   18809:         $chunk .=
                   18810:             $cypher->decrypt(unpack("a8",pack("H16",substr($cypherblock,16,16))));
                   18811:         $chunk=substr($chunk,1,ord(substr($chunk,0,1)) );
                   18812:         $plaintext .= $chunk;
                   18813:     }
1.1174    raeburn  18814:     return $plaintext;
                   18815: }
                   18816: 
1.1344    raeburn  18817: sub get_requested_shorturls {
1.1309    raeburn  18818:     my ($cdom,$cnum,$navmap) = @_;
                   18819:     return unless (ref($navmap));
1.1344    raeburn  18820:     my ($numnew,$errors);
1.1309    raeburn  18821:     my @toshorten = &Apache::loncommon::get_env_multiple('form.addtiny');
                   18822:     if (@toshorten) {
                   18823:         my (%maps,%resources,%titles);
                   18824:         &Apache::loncourserespicker::enumerate_course_contents($navmap,\%maps,\%resources,\%titles,
                   18825:                                                                'shorturls',$cdom,$cnum);
                   18826:         if (keys(%resources)) {
1.1344    raeburn  18827:             my %tocreate;
1.1309    raeburn  18828:             foreach my $item (sort {$a <=> $b} (@toshorten)) {
                   18829:                 my $symb = $resources{$item};
                   18830:                 if ($symb) {
                   18831:                     $tocreate{$cnum.'&'.$symb} = 1;
                   18832:                 }
                   18833:             }
1.1344    raeburn  18834:             if (keys(%tocreate)) {
                   18835:                 ($numnew,$errors) = &make_short_symbs($cdom,$cnum,
                   18836:                                                       \%tocreate);
                   18837:             }
1.1309    raeburn  18838:         }
1.1344    raeburn  18839:     }
                   18840:     return ($numnew,$errors);
                   18841: }
                   18842: 
                   18843: sub make_short_symbs {
                   18844:     my ($cdom,$cnum,$tocreateref,$lockuser) = @_;
                   18845:     my ($numnew,@errors);
                   18846:     if (ref($tocreateref) eq 'HASH') {
                   18847:         my %tocreate = %{$tocreateref};
1.1309    raeburn  18848:         if (keys(%tocreate)) {
                   18849:             my %coursetiny = &Apache::lonnet::dump('tiny',$cdom,$cnum);
                   18850:             my $su = Short::URL->new(no_vowels => 1);
                   18851:             my $init = '';
                   18852:             my (%newunique,%addcourse,%courseonly,%failed);
                   18853:             # get lock on tiny db
                   18854:             my $now = time;
1.1344    raeburn  18855:             if ($lockuser eq '') {
                   18856:                 $lockuser = $env{'user.name'}.':'.$env{'user.domain'};
                   18857:             }
1.1309    raeburn  18858:             my $lockhash = {
1.1344    raeburn  18859:                                 "lock\0$now" => $lockuser,
1.1309    raeburn  18860:                             };
                   18861:             my $tries = 0;
                   18862:             my $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom);
                   18863:             my ($code,$error);
                   18864:             while (($gotlock ne 'ok') && ($tries<3)) {
                   18865:                 $tries ++;
                   18866:                 sleep 1;
1.1319    raeburn  18867:                 $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom);
1.1309    raeburn  18868:             }
                   18869:             if ($gotlock eq 'ok') {
                   18870:                 $init = &shorten_symbs($cdom,$init,$su,\%coursetiny,\%tocreate,\%newunique,
                   18871:                                        \%addcourse,\%courseonly,\%failed);
                   18872:                 if (keys(%failed)) {
                   18873:                     my $numfailed = scalar(keys(%failed));
                   18874:                     push(@errors,&mt('error: could not obtain unique six character URL for [quant,_1,resource]',$numfailed));
                   18875:                 }
                   18876:                 if (keys(%newunique)) {
                   18877:                     my $putres = &Apache::lonnet::newput_dom('tiny',\%newunique,$cdom);
                   18878:                     if ($putres eq 'ok') {
                   18879:                         $numnew = scalar(keys(%newunique));
                   18880:                         my $newputres = &Apache::lonnet::newput('tiny',\%addcourse,$cdom,$cnum);
                   18881:                         unless ($newputres eq 'ok') {
                   18882:                             push(@errors,&mt('error: could not store course look-up of short URLs'));
                   18883:                         }
                   18884:                     } else {
                   18885:                         push(@errors,&mt('error: could not store unique six character URLs'));
                   18886:                     }
                   18887:                 }
                   18888:                 my $dellockres = &Apache::lonnet::del_dom('tiny',["lock\0$now"],$cdom);
                   18889:                 unless ($dellockres eq 'ok') {
                   18890:                     push(@errors,&mt('error: could not release lockfile'));
                   18891:                 }
                   18892:             } else {
                   18893:                 push(@errors,&mt('error: could not obtain lockfile'));
                   18894:             }
                   18895:             if (keys(%courseonly)) {
                   18896:                 my $result = &Apache::lonnet::newput('tiny',\%courseonly,$cdom,$cnum);
                   18897:                 if ($result ne 'ok') {
                   18898:                     push(@errors,&mt('error: could not update course look-up of short URLs'));
                   18899:                 }
                   18900:             }
                   18901:         }
                   18902:     }
                   18903:     return ($numnew,\@errors);
                   18904: }
                   18905: 
                   18906: sub shorten_symbs {
                   18907:     my ($cdom,$init,$su,$coursetiny,$tocreate,$newunique,$addcourse,$courseonly,$failed) = @_;
                   18908:     return unless ((ref($su)) && (ref($coursetiny) eq 'HASH') && (ref($tocreate) eq 'HASH') &&
                   18909:                    (ref($newunique) eq 'HASH') && (ref($addcourse) eq 'HASH') &&
                   18910:                    (ref($courseonly) eq 'HASH') && (ref($failed) eq 'HASH'));
                   18911:     my (%possibles,%collisions);
                   18912:     foreach my $key (keys(%{$tocreate})) {
                   18913:         my $num = String::CRC32::crc32($key);
                   18914:         my $tiny = $su->encode($num,$init);
                   18915:         if ($tiny) {
                   18916:             $possibles{$tiny} = $key;
                   18917:         }
                   18918:     }
                   18919:     if (!$init) {
                   18920:         $init = 1;
                   18921:     } else {
                   18922:         $init ++;
                   18923:     }
                   18924:     if (keys(%possibles)) {
                   18925:         my @posstiny = keys(%possibles);
                   18926:         my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
                   18927:         my %currtiny = &Apache::lonnet::get('tiny',\@posstiny,$cdom,$configuname);
                   18928:         if (keys(%currtiny)) {
                   18929:             foreach my $key (keys(%currtiny)) {
                   18930:                 next if ($currtiny{$key} eq '');
                   18931:                 if ($currtiny{$key} eq $possibles{$key}) {
                   18932:                     my ($tcnum,$tsymb) = split(/\&/,$currtiny{$key});
                   18933:                     unless (($coursetiny->{$tsymb} eq $key) || ($addcourse->{$tsymb} eq $key) || ($courseonly->{$tsymb} eq $key)) {
                   18934:                         $courseonly->{$tsymb} = $key;
                   18935:                     }
                   18936:                 } else {
                   18937:                     $collisions{$possibles{$key}} = 1;
                   18938:                 }
                   18939:                 delete($possibles{$key});
                   18940:             }
                   18941:         }
                   18942:         foreach my $key (keys(%possibles)) {
                   18943:             $newunique->{$key} = $possibles{$key};
                   18944:             my ($tcnum,$tsymb) = split(/\&/,$possibles{$key});
                   18945:             unless (($coursetiny->{$tsymb} eq $key) || ($addcourse->{$tsymb} eq $key) || ($courseonly->{$tsymb} eq $key)) {
                   18946:                 $addcourse->{$tsymb} = $key;
                   18947:             }
                   18948:         }
                   18949:     }
                   18950:     if (keys(%collisions)) {
                   18951:         if ($init <5) {
                   18952:             if (!$init) {
                   18953:                 $init = 1;
                   18954:             } else {
                   18955:                 $init ++;
                   18956:             }
                   18957:             $init = &shorten_symbs($cdom,$init,$su,$coursetiny,\%collisions,
                   18958:                                    $newunique,$addcourse,$courseonly,$failed);
                   18959:         } else {
                   18960:             foreach my $key (keys(%collisions)) {
                   18961:                 $failed->{$key} = 1;
                   18962:             }
                   18963:         }
                   18964:     }
                   18965:     return $init;
                   18966: }
                   18967: 
1.1328    raeburn  18968: sub is_nonframeable {
1.1329    raeburn  18969:     my ($url,$absolute,$hostname,$ip,$nocache) = @_;
                   18970:     my ($remprotocol,$remhost) = ($url =~ m{^(https?)\://(([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,})}i);
1.1330    raeburn  18971:     return if (($remprotocol eq '') || ($remhost eq ''));
1.1329    raeburn  18972: 
                   18973:     $remprotocol = lc($remprotocol);
                   18974:     $remhost = lc($remhost);
                   18975:     my $remport = 80;
                   18976:     if ($remprotocol eq 'https') {
                   18977:         $remport = 443;
                   18978:     }
1.1330    raeburn  18979:     my ($result,$cached) = &Apache::lonnet::is_cached_new('noiframe',$remhost.':'.$remport);
1.1329    raeburn  18980:     if ($cached) {
                   18981:         unless ($nocache) {
                   18982:             if ($result) {
                   18983:                 return 1;
                   18984:             } else {
                   18985:                 return 0;
                   18986:             }
                   18987:         }
                   18988:     }
1.1328    raeburn  18989:     my $uselink;
                   18990:     my $request = new HTTP::Request('HEAD',$url);
                   18991:     my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',5);
                   18992:     if ($response->is_success()) {
                   18993:         my $secpolicy = lc($response->header('content-security-policy'));
                   18994:         my $xframeop = lc($response->header('x-frame-options'));
                   18995:         $secpolicy =~ s/^\s+|\s+$//g;
                   18996:         $xframeop =~ s/^\s+|\s+$//g;
                   18997:         if (($secpolicy ne '') || ($xframeop ne '')) {
1.1329    raeburn  18998:             my $remotehost = $remprotocol.'://'.$remhost;
1.1328    raeburn  18999:             my ($origin,$protocol,$port);
                   19000:             if ($ENV{'SERVER_PORT'} =~/^\d+$/) {
                   19001:                 $port = $ENV{'SERVER_PORT'};
                   19002:             } else {
                   19003:                 $port = 80;
                   19004:             }
                   19005:             if ($absolute eq '') {
                   19006:                 $protocol = 'http:';
                   19007:                 if ($port == 443) {
                   19008:                     $protocol = 'https:';
                   19009:                 }
                   19010:                 $origin = $protocol.'//'.lc($hostname);
                   19011:             } else {
                   19012:                 $origin = lc($absolute);
                   19013:                 ($protocol,$hostname) = ($absolute =~ m{^(https?:)//([^/]+)$});
                   19014:             }
                   19015:             if (($secpolicy) && ($secpolicy =~ /\Qframe-ancestors\E([^;]*)(;|$)/)) {
                   19016:                 my $framepolicy = $1;
                   19017:                 $framepolicy =~ s/^\s+|\s+$//g;
                   19018:                 my @policies = split(/\s+/,$framepolicy);
                   19019:                 if (@policies) {
                   19020:                     if (grep(/^\Q'none'\E$/,@policies)) {
                   19021:                         $uselink = 1;
                   19022:                     } else {
                   19023:                         $uselink = 1;
                   19024:                         if ((grep(/^\Q*\E$/,@policies)) || (grep(/^\Q$protocol\E$/,@policies)) ||
                   19025:                                 (($origin ne '') && (grep(/^\Q$origin\E$/,@policies))) ||
                   19026:                                 (($ip ne '') && (grep(/^\Q$ip\E$/,@policies)))) {
                   19027:                             undef($uselink);
                   19028:                         }
                   19029:                         if ($uselink) {
                   19030:                             if (grep(/^\Q'self'\E$/,@policies)) {
                   19031:                                 if (($origin ne '') && ($remotehost eq $origin)) {
                   19032:                                     undef($uselink);
                   19033:                                 }
                   19034:                             }
                   19035:                         }
                   19036:                         if ($uselink) {
                   19037:                             my @possok;
                   19038:                             if ($ip ne '') {
                   19039:                                 push(@possok,$ip);
                   19040:                             }
                   19041:                             my $hoststr = '';
                   19042:                             foreach my $part (reverse(split(/\./,$hostname))) {
                   19043:                                 if ($hoststr eq '') {
                   19044:                                     $hoststr = $part;
                   19045:                                 } else {
                   19046:                                     $hoststr = "$part.$hoststr";
                   19047:                                 }
                   19048:                                 if ($hoststr eq $hostname) {
                   19049:                                     push(@possok,$hostname);
                   19050:                                 } else {
                   19051:                                     push(@possok,"*.$hoststr");
                   19052:                                 }
                   19053:                             }
                   19054:                             if (@possok) {
                   19055:                                 foreach my $poss (@possok) {
                   19056:                                     last if (!$uselink);
                   19057:                                     foreach my $policy (@policies) {
                   19058:                                         if ($policy =~ m{^(\Q$protocol\E//|)\Q$poss\E(\Q:$port\E|)$}) {
                   19059:                                             undef($uselink);
                   19060:                                             last;
                   19061:                                         }
                   19062:                                     }
                   19063:                                 }
                   19064:                             }
                   19065:                         }
                   19066:                     }
                   19067:                 }
                   19068:             } elsif ($xframeop ne '') {
                   19069:                 $uselink = 1;
                   19070:                 my @policies = split(/\s*,\s*/,$xframeop);
                   19071:                 if (@policies) {
                   19072:                     unless (grep(/^deny$/,@policies)) {
                   19073:                         if ($origin ne '') {
                   19074:                             if (grep(/^sameorigin$/,@policies)) {
                   19075:                                 if ($remotehost eq $origin) {
                   19076:                                     undef($uselink);
                   19077:                                 }
                   19078:                             }
                   19079:                             if ($uselink) {
                   19080:                                 foreach my $policy (@policies) {
                   19081:                                     if ($policy =~ /^allow-from\s*(.+)$/) {
                   19082:                                         my $allowfrom = $1;
                   19083:                                         if (($allowfrom ne '') && ($allowfrom eq $origin)) {
                   19084:                                             undef($uselink);
                   19085:                                             last;
                   19086:                                         }
                   19087:                                     }
                   19088:                                 }
                   19089:                             }
                   19090:                         }
                   19091:                     }
                   19092:                 }
                   19093:             }
                   19094:         }
                   19095:     }
1.1329    raeburn  19096:     if ($nocache) {
                   19097:         if ($cached) {
                   19098:             my $devalidate;
                   19099:             if ($uselink && !$result) {
                   19100:                 $devalidate = 1;
                   19101:             } elsif (!$uselink && $result) {
                   19102:                 $devalidate = 1;
                   19103:             }
                   19104:             if ($devalidate) {
                   19105:                 &Apache::lonnet::devalidate_cache_new('noiframe',$remhost.':'.$remport);
                   19106:             }
                   19107:         }
                   19108:     } else {
                   19109:         if ($uselink) {
                   19110:             $result = 1;
                   19111:         } else {
                   19112:             $result = 0;
                   19113:         }
                   19114:         &Apache::lonnet::do_cache_new('noiframe',$remhost.':'.$remport,$result,3600);
                   19115:     }
1.1328    raeburn  19116:     return $uselink;
                   19117: }
                   19118: 
1.1359    raeburn  19119: sub page_menu {
                   19120:     my ($menucolls,$menunum) = @_;
                   19121:     my %menu;
                   19122:     foreach my $item (split(/;/,$menucolls)) {
                   19123:         my ($num,$value) = split(/\%/,$item);
                   19124:         if ($num eq $menunum) {
                   19125:             my @entries = split(/\&/,$value);
                   19126:             foreach my $entry (@entries) {
                   19127:                 my ($name,$fields) = split(/=/,$entry);
1.1368    raeburn  19128:                 if (($name eq 'top') || ($name eq 'inline') || ($name eq 'foot') || ($name eq 'main')) {
1.1359    raeburn  19129:                     $menu{$name} = $fields;
                   19130:                 } else {
                   19131:                     my @shown;
                   19132:                     if ($fields =~ /,/) {
                   19133:                         @shown = split(/,/,$fields);
                   19134:                     } else {
                   19135:                         @shown = ($fields);
                   19136:                     }
                   19137:                     if (@shown) {
                   19138:                         foreach my $field (@shown) {
                   19139:                             next if ($field eq '');
                   19140:                             $menu{$field} = 1;
                   19141:                         }
                   19142:                     }
                   19143:                 }
                   19144:             }
                   19145:         }
                   19146:     }
                   19147:     return %menu;
                   19148: }
                   19149: 
1.112     bowersj2 19150: 1;
                   19151: __END__;
1.41      ng       19152: 

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